Appel par nom :
L'appel par nom est une stratégie d'évaluation où les arguments d'une fonction ne sont pas évalués avant l'appel de la fonction ; plutôt, ils sont directement substitués dans le corps de la fonction (en utilisant une substitution évitant la capture) et sont ensuite laissés à évaluer chaque fois qu'ils apparaissent dans la fonction. Si un argument n'est pas utilisé dans le corps de la fonction, l'argument n'est jamais évalué ; s'il est utilisé plusieurs fois, il est réévalué chaque fois qu'il apparaît. (Voir l'Appareil de Jensen.)
L'évaluation par nom est occasionnellement préférable à l'évaluation par valeur. Si un argument d'une fonction n'est pas utilisé dans la fonction, l'appel par nom permettra d'économiser du temps en n'évaluant pas l'argument, tandis que l'appel par valeur l'évaluera quand même. Si l'argument est un calcul non-terminant, l'avantage est énorme. Cependant, lorsque l'argument de la fonction est utilisé, l'appel par nom est souvent plus lent, nécessitant un mécanisme tel qu'une suspension.
Une utilisation précoce était ALGOL 60. Les langages.NET d'aujourd'hui peuvent simuler l'appel par nom en utilisant des délégués ou des paramètres d'Expression. Ce dernier donne un arbre de syntaxe abstraite à la fonction. Eiffel fournit des agents, qui représentent une opération à évaluer au besoin. Seed7 fournit un appel par nom avec des paramètres de fonction.
Appel Par Macro :
L'appel par expansion de macro est similaire à l'appel par nom, mais utilise une substitution textuelle plutôt qu'une substitution évitant la capture. Avec une utilisation imprudente, la substitution de macro peut entraîner une capture de variables et conduire à un comportement indésirable. Les macros hygiéniques évitent ce problème en vérifiant et remplaçant les variables obscures qui ne sont pas des paramètres.
REMARQUE : Dans les langages d'évaluation non stricte
Exemple d'Appel par Macro :
Appel par Expansion de Macro : de nombreux langages de programmation, y compris C, Lisp et Scheme, fournissent aux développeurs un mécanisme pour ajouter une nouvelle syntaxe à la grammaire de base du langage appelée macros. Les macros sont développées en code par un préprocesseur de macro. Ces macros peuvent contenir des arguments, qui sont copiés dans le code final produit par le préprocesseur. Par exemple, le programme C ci-dessous implémente la fonction d'échange via une macro :
#define SWAP(X,Y) {int temp = X; X = Y; Y = temp;} int main() { int a = 2; int b = 3; printf("%d, %d\n", a, b); SWAP(a, b); printf("%d,
> %d\n", a, b); }
Cette macro implémente une routine d'échange valide. Le
programme prétraité ressemblera au code ci-dessous. Comme le corps de la macro est directement copié dans le texte du programme appelant, il fonctionne dans le contexte de ce programme. En d'autres termes, la macro fera référence directement aux noms de variables qu'elle reçoit, et non à leurs valeurs.
int main() { int a = 2; int b = 3; printf("%d, %d\n", a, b); {
> int tmp = (a); (a) = (b); (b) = tmp; }; printf("%d, %d\n", a, b); }
Les expressions transmises à la macro en tant que paramètres sont évaluées chaque fois qu'elles sont utilisées dans le corps de la macro. Si l'argument n'est jamais utilisé, alors il n'est simplement pas évalué. Par exemple, le programme ci-dessous incrémente la variable b deux fois :
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) int main() { int a = 2, b = 3; int c = MAX(a, b++); printf("a = %d, b = %d, c = %d\n", a, b, c); }
Les macros souffrent d'un problème, appelé capture de variable. Si une macro définit une variable v qui est déjà définie dans l'environnement de l'appelant, et que v est passé à la macro en tant que paramètre, le corps de la macro ne pourra pas distinguer une occurrence de v de l'autre. Par exemple, le programme ci-dessous a une macro qui définit une variable temp. L'appel à l'intérieur de main fait que la variable temp définie à l'intérieur de cette fonction soit capturée par la définition à l'intérieur du corps de la macro.
#define SWAP(X,Y) {int temp=X; X=Y;} int main() { int a = 2; int temp = 17; printf("%d, temp = %d\n", a, temp); SWAP(a, temp);
> printf("%d, temp = %d\n", a, temp); }
Une fois que ce programme est développé par
le préprocesseur C, nous obtenons le code ci-dessous. Ce programme ne parvient pas à échanger les valeurs des variables temp et a :
int main() { int a = 2; int temp = 17; printf("%d, temp = %d\n",
> a, temp); {int temp=a; a=temp; temp=temp;}; printf("%d, temp =
> %d\n", a, temp); }
Il existe plusieurs stratégies d'évaluation paresseuse
qui évitent le problème de capture de variable. Les deux techniques les plus connues sont l'appel par nom et l'appel par besoin.
Exemple d'Appel Par Nom :
Appel par Nom : dans cette stratégie d'évaluation, le paramètre réel n'est évalué que si utilisé à l'intérieur de la fonction ; cependant, cette évaluation utilise le contexte de la routine appelante. Par exemple, dans l'exemple ci-dessous, tiré du livre de Weber, nous avons une fonction g qui renvoie l'entier 6. À l'intérieur de la fonction f, la première assignation, par exemple b = 5, stocke 5 dans la variable i. La deuxième assignation, b = a, lit la valeur de i, actuellement 5, et ajoute 1 à celle-ci. Cette valeur est ensuite stockée dans i.
void f(par-nom int a, par-nom int b) {
b=5;
b=a;
}
int g() {
int i = 3;
f(i+1,i);
return i;
}
Très peu de langages implémentent la stratégie d'évaluation par nom. Le plus éminent de ces langages est Algol. Simula, un descendant direct d'Algol, implémente également l'appel par nom, comme nous pouvons le voir dans cet exemple. L'appel par nom entraîne toujours l'évaluation du paramètre, même si ce paramètre est utilisé plusieurs fois. Ce comportement peut être gaspilleur dans les langages transparents sur le plan référentiel, car, dans ces langages, les variables sont immuables.