Stratégie d’évaluation

0

Dans un langage de programmation , une stratégie d’évaluation est un ensemble de règles d’évaluation des expressions. [1] Le terme est souvent utilisé pour désigner la notion plus spécifique d’une stratégie de passage de paramètres [2] qui définit le type de valeur transmise à la fonction pour chaque paramètre (la stratégie de liaison ), [3] si évaluer les paramètres d’un appel de fonction, et si oui dans quel ordre (l’ ordre d’évaluation ). [4] La notion de stratégie de réduction est distincte, [5]bien que certains auteurs confondent les deux termes et que la définition de chaque terme ne soit pas largement acceptée. [6]

Pour illustrer, une application de fonction peut évaluer l’argument avant d’évaluer le corps de la fonction et transmettre l’adresse, donnant à la fonction la possibilité de rechercher la valeur actuelle de l’argument et de la modifier via assignation . [7]

La stratégie d’évaluation est spécifiée par la définition du langage de programmation et n’est pas fonction d’une implémentation spécifique. La convention d’appel définit les détails de transmission des paramètres spécifiques à l’implémentation.

Table

Il s’agit d’un tableau des stratégies d’évaluation et des langues représentatives par année d’introduction. Les langues représentatives sont répertoriées par ordre chronologique, en commençant par la ou les langues qui ont introduit la stratégie (le cas échéant) et suivies par les principales langues qui utilisent la stratégie. [8] : 434

Stratégie d’évaluation Langues représentatives Année d’introduction
Appel par référence FORTRAN II , PL/I 1958
Appel par valeur ALGOL , C , Schéma 1960
Appel par nom ALGOL 60 , Simule 1960
Appel par copie-restauration Fortran IV , Ada [9] 1962
Appel par besoin Haskell , R [10] 1971 [11]
Paramètres d’appel par référence C++ , PHP , [12] C# , [13] Visual Basic .NET [14] ?
Appel par référence à const C , C++ ?
Appelez en partageant Java, Python, Ruby ?

Ordonnances d’évaluation

Alors que l’ ordre des opérations définit l’ arbre syntaxique abstrait de l’expression, l’ordre d’évaluation définit l’ordre dans lequel les expressions sont évaluées. Par exemple, le programme Python

def f ( x ): print ( x ) return x f ( 1 ) + f ( 2 )

sorties 1 2en raison de l’ordre d’évaluation de gauche à droite de Python, mais un programme similaire en OCaml :

let f x = print_string ( string_of_int x ); x ;; f 1 + f 2

sorties 2 1en raison de l’ordre d’évaluation de droite à gauche d’OCaml.

L’ordre d’évaluation est principalement visible dans le code avec des effets secondaires , mais il affecte également les performances du code car un ordre rigide inhibe l’ordonnancement des instructions . Pour cette raison, les standards de langage tels que C++ laissaient traditionnellement l’ordre indéfini, bien que des langages tels que Java et C# définissent l’ordre d’évaluation de gauche à droite [8] : 240–241 et que le standard C++17 ait ajouté des contraintes sur la ordre d’évaluation. [15]

Évaluation stricte

L’ordre applicatif est une famille d’ordres d’évaluation dans laquelle les arguments d’une fonction sont évalués complètement avant que la fonction ne soit appliquée. [16] Cela a pour effet de rendre la fonction stricte , c’est-à-dire que le résultat de la fonction est indéfini si l’un des arguments est indéfini, donc l’évaluation d’ordre applicatif est plus communément appelée évaluation stricte . De plus, un appel de fonction est effectué dès qu’il est rencontré dans une procédure, il est donc aussi appelé évaluation avide ou évaluation gloutonne . [17] [18] Certains auteurs se réfèrent à l’évaluation stricte comme “appel par valeur” en raison de la stratégie de liaison appel par valeur nécessitant une évaluation stricte. [4]

Common Lisp, Eiffel et Java évaluent les arguments des fonctions de gauche à droite. C laisse l’ordre indéfini. [19] Scheme exige que l’ordre d’exécution soit l’exécution séquentielle d’une permutation non spécifiée des arguments. [20] OCaml laisse de même l’ordre non spécifié, mais en pratique évalue les arguments de droite à gauche en raison de la conception de sa machine abstraite . [21] Tous ces éléments sont une évaluation stricte.

Évaluation non stricte

Un ordre d’ évaluation non strict est un ordre d’évaluation qui n’est pas strict, c’est-à-dire qu’une fonction peut renvoyer un résultat avant que tous ses arguments ne soient entièrement évalués. [22] : 46–47 L’exemple prototypique est l’évaluation d’ordre normal , qui n’évalue aucun des arguments jusqu’à ce qu’ils soient nécessaires dans le corps de la fonction. [23] L’évaluation de l’ordre normal a la propriété de se terminer sans erreur chaque fois qu’un autre ordre d’évaluation se termine sans erreur. [24] Notez que l’ évaluation paresseuseest classée dans cet article comme une technique contraignante plutôt qu’un ordre d’évaluation. Mais cette distinction n’est pas toujours respectée et certains auteurs définissent l’évaluation paresseuse comme une évaluation d’ordre normal ou vice-versa, [25] [26] ou confondent non-strictité et évaluation paresseuse. [22] : 43–44

Les expressions booléennes dans de nombreuses langues utilisent une forme d’évaluation non stricte appelée évaluation en court-circuit , où l’évaluation revient dès qu’il peut être déterminé qu’un booléen non ambigu en résultera, par exemple, dans une expression disjonctive (OU) où trueest rencontré, ou dans une expression conjonctive (ET) où falseest rencontré, et ainsi de suite. [26] Les expressions conditionnelles utilisent de la même manière une évaluation non stricte – une seule des branches est évaluée. [22]

Comparaison de l’ordre applicatif et de l’évaluation de l’ordre normal

Avec une évaluation d’ordre normale, les expressions contenant un calcul coûteux, une erreur ou une boucle infinie seront ignorées si elles ne sont pas nécessaires, [4] permettant la spécification de constructions de flux de contrôle définies par l’utilisateur, une fonctionnalité non disponible avec l’évaluation d’ordre applicatif. L’évaluation de l’ordre normal utilise des structures complexes telles que les thunks pour les expressions non évaluées, par rapport à la pile d’appels utilisée dans l’évaluation de l’ordre applicatif. [27] L’évaluation normale des commandes a toujours eu un manque d’outils de débogage utilisables en raison de sa complexité. [28]

Stratégies contraignantes strictes

Appel par valeur

Dans l’appel par valeur, la valeur évaluée de l’expression d’argument est liée à la variable correspondante dans la fonction (souvent en copiant la valeur dans une nouvelle région de mémoire). Si la fonction ou la procédure est capable d’affecter des valeurs à ses paramètres, seule sa variable locale est affectée, c’est-à-dire que tout ce qui est passé dans un appel de fonction est inchangé dans la portée de l’appelant lorsque la fonction revient.

Limites implicites

Dans certains cas, le terme “appel par valeur” est problématique, car la valeur qui est transmise n’est pas la valeur de la variable au sens ordinaire de valeur, mais une référence spécifique à la mise en œuvre à la valeur. L’effet est que ce qui ressemble syntaxiquement à un appel par valeur peut finir par se comporter plutôt comme un appel par référence ou un appel par partage , souvent en fonction d’aspects très subtils de la sémantique du langage.

La raison du passage d’une référence est souvent que le langage ne fournit techniquement pas une représentation de valeur de données compliquées, mais les représente plutôt comme une structure de données tout en préservant un semblant d’apparence de valeur dans le code source. Il est souvent difficile de prévoir exactement où la frontière est tracée entre les valeurs appropriées et les structures de données se faisant passer pour telles. En C , un tableau (dont les chaînes sont des cas particuliers) est une structure de données mais le nom d’un tableau est traité comme (a comme valeur) la référence au premier élément du tableau, tandis que le nom d’une variable struct fait référence à une valeur même s’il a des champs qui sont des vecteurs. En érable, un vecteur est un cas particulier d’une table et donc d’une structure de données, mais une liste (qui est rendue et peut être indexée exactement de la même manière) est une valeur. En Tcl , les valeurs sont “à double accès” de sorte que la représentation de la valeur est utilisée au niveau du script, et le langage lui-même gère la structure de données correspondante, si nécessaire. Les modifications apportées via la structure de données sont répercutées sur la représentation de la valeur et vice versa.

La description « appel par valeur où la valeur est une référence » est courante (mais ne doit pas être comprise comme étant un appel par référence) ; un autre terme est appel par partage . Ainsi, le comportement de l’appel par valeur Java ou Visual Basic et de l’appel par valeur C ou Pascal est sensiblement différent : en C ou Pascal, l’appel d’une fonction avec une grande structure en argument entraînera la copie de toute la structure (sauf si elle est en réalité une référence à une structure), provoquant potentiellement une grave dégradation des performances, et les mutations de la structure sont invisibles pour l’appelant. Cependant, en Java ou Visual Basic, seule la référence à la structure est copiée, ce qui est rapide, et les mutations de la structure sont visibles pour l’appelant.

Appel par référence

L’appel par référence (ou le passage par référence) est une stratégie d’évaluation dans laquelle un paramètre est lié à une référence implicite à la variable utilisée comme argument, plutôt qu’à une copie de sa valeur.

Cela signifie généralement que la fonction peut modifier (c’est-à-dire affecter à ) la variable utilisée comme argument, ce qui sera vu par son appelant. L’appel par référence peut donc être utilisé pour fournir un canal de communication supplémentaire entre la fonction appelée et la fonction appelante. Un langage d’appel par référence rend plus difficile pour un programmeur de suivre les effets d’un appel de fonction et peut introduire des bogues subtils. Un simple test décisif pour savoir si un langage prend en charge la sémantique d’appel par référence est de savoir s’il est possible d’écrire une swap(a, b)fonction traditionnelle dans le langage. [29]

L’appel par référence peut être simulé dans les langages qui utilisent l’appel par valeur et ne prennent pas exactement en charge l’appel par référence, en utilisant des références (objets qui font référence à d’autres objets), tels que des pointeurs (objets représentant les adresses mémoire d’autres objets) . Des langages tels que C , ML et Rust utilisent cette technique. Il ne s’agit pas d’une stratégie d’évaluation distincte – le langage appelle par valeur – mais on l’appelle parfois “appel par adresse” ou “passage par adresse”. En ML, les références sont sécurisées pour le type et la mémoire , comme Rust.

Dans les langages purement fonctionnels, il n’y a généralement pas de différence sémantique entre les deux stratégies (puisque leurs structures de données sont immuables, il n’y a donc aucune possibilité pour une fonction de modifier l’un de ses arguments), elles sont donc généralement décrites comme des appels par valeur même si les implémentations utilisent fréquemment l’appel par référence en interne pour des gains d’efficacité.

Voici un exemple qui illustre l’appel par référence dans le langage de programmation E :

def modifier(var p, &q) { p := 27 # passé par valeur : seul le paramètre local est modifié q := 27 # passé par référence : la variable utilisée dans l’appel est modifiée } ? var a := 1 # valeur : 1 ? var b := 2 # valeur : 2 ? modifier(a, &b) ? un # valeur : 1 ? b # valeur : 27

Voici un exemple d’appel par adresse qui simule l’appel par référence en C :

void modifier ( int p , int * q , int * r ) { p = 27 ; // passé par valeur : seul le paramètre local est modifié * q = 27 ; // passé par valeur ou référence, vérifiez le site d’appel pour déterminer lequel * r = 27 ; // transmis par valeur ou référence, vérifiez le site d’appel pour déterminer lequel } int principal () { int a = 1 ; int b = 1 ; entier x = 1 ; int * c = & x ; modifier ( a , & b , c ); // a est passé par valeur, b est passé par référence en créant un pointeur (appel par valeur), // c est un pointeur passé par valeur // b et x sont modifiés return 0 ; }

Appelez en partageant

L’appel par partage (également appelé «appel par objet» ou «appel par partage d’objet») est une stratégie d’évaluation notée pour la première fois par Barbara Liskov en 1974 pour le langage CLU . [30] Il est utilisé par des langages tels que Python , [31] Java (pour les références d’objets), Ruby , JavaScript , Scheme, OCaml, AppleScript et bien d’autres. Cependant, le terme “appel par partage” n’est pas d’usage courant ; la terminologie est incohérente d’une source à l’autre. Par exemple, dans la communauté Java, on dit que Java est appelé par valeur. [32] L’appel par partage implique que les valeurs du langage sont basées sur des objets plutôt que surtypes primitifs , c’est-à-dire que toutes les valeurs sont ” boxées “. Parce qu’ils sont encadrés, on peut dire qu’ils passent par copie de référence (où les primitives sont encadrées avant de passer et déboîtées à la fonction appelée).

La sémantique de l’appel par partage diffère de l’appel par référence : “En particulier ce n’est pas un appel par valeur car les mutations d’arguments effectuées par la routine appelée seront visibles par l’appelant. Et ce n’est pas un appel par référence car l’accès n’est pas donné à les variables de l’appelant, mais seulement à certains objets”. [33] Ainsi, par exemple, si une variable a été transmise, il n’est pas possible de simuler une affectation sur cette variable dans la portée de l’appelé. [34] Cependant, puisque la fonction a accès au même objet que l’appelant (aucune copie n’est faite), les mutations de ces objets, si les objets sont mutables, au sein de la fonction sont visibles pour l’appelant, ce qui peut sembler différent de la sémantique d’appel par valeur. Les mutations d’un objet mutable dans la fonction sont visibles pour l’appelant car l’objet n’est ni copié ni cloné, il est partagé.

Par exemple, en Python, les listes sont modifiables, donc :

def f ( une_liste ): une_liste . ajouter ( 1 ) m = [] f ( m ) imprimer ( m )

sorties [1]parce que la appendméthode modifie l’objet sur lequel elle est appelée.

Les affectations au sein d’une fonction ne sont pas perceptibles par l’appelant, car, dans ces langages, transmettre la variable signifie uniquement transmettre (accéder à) l’objet réel auquel la variable fait référence, et non accéder à la variable d’origine (de l’appelant). Étant donné que la variable de rebond n’existe que dans la portée de la fonction, la contrepartie dans l’appelant conserve sa liaison d’origine.

Comparez la mutation Python ci-dessus avec le code ci-dessous, qui lie l’argument formel à un nouvel objet :

def f ( une_liste ): une_liste = une_liste + [ 1 ] m = [] f ( m ) imprimer ( m )

outputs [], car l’instruction a_list + [1]réaffecte une nouvelle liste à la variable plutôt qu’à l’emplacement auquel elle fait référence.

Pour les objets immuables , il n’y a pas de réelle différence entre appel par partage et appel par valeur, sauf si l’identité de l’objet est visible dans le langage. L’utilisation de l’appel par partage avec des objets mutables est une alternative aux paramètres d’ entrée/sortie : le paramètre n’est pas affecté à (l’argument n’est pas écrasé et l’identité de l’objet n’est pas modifiée), mais l’objet (argument) est muté. [35]

Appel par copie-restauration

L’appel par copie-restauration – également connu sous le nom de “copie-entrée copie-sortie”, “résultat d’appel par valeur”, “retour d’appel par valeur” (comme on l’appelle dans la communauté Fortran ) – est un cas particulier d’appel par référence où le la référence fournie est unique pour l’appelant. Cette variante a retenu l’attention dans les contextes de multitraitement et d’appel de procédure distante : [36] si un paramètre d’un appel de fonction est une référence qui pourrait être accessible par un autre thread d’exécution, son contenu peut être copié vers une nouvelle référence qui ne l’est pas ; lorsque l’appel de fonction revient, le contenu mis à jour de cette nouvelle référence est recopié dans la référence d’origine (“restauré”).

La sémantique de l’appel par copie-restauration diffère également de celle de l’appel par référence, où deux ou plusieurs arguments de fonction sont des alias (c’est-à-dire qu’ils pointent vers la même variable dans l’environnement de l’appelant). Sous appel par référence, écrire à l’un affectera l’autre ; l’appel par copie-restauration évite cela en donnant à la fonction des copies distinctes, mais laisse le résultat dans l’environnement de l’appelant indéfini selon lequel des arguments alias est copié en premier – les copies seront-elles faites dans l’ordre de gauche à droite à la fois à l’entrée et au retour ?

Lorsque la référence est passée à l’appelé non initialisée, cette stratégie d’évaluation peut être appelée “appel par résultat”.

Stratégies de liaison non strictes

Appel par nom

L’appel par nom est une stratégie d’évaluation dans laquelle les arguments d’une fonction ne sont pas évalués avant que la fonction ne soit appelée. Ils sont plutôt remplacés directement dans le corps de la fonction (à l’aide d’une Substitution évitant la capture ), puis laissés à évaluer chaque fois qu’ils apparaissent dans le une 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 apparition. (Voir L’appareil de Jensen .)

L’évaluation par nom est parfois préférable à l’évaluation par valeur. Si l’argument d’une fonction n’est pas utilisé dans la fonction, l’appel par nom fera gagner du temps en n’évaluant pas l’argument, tandis que l’appel par valeur l’évaluera malgré tout. Si l’argument est un calcul sans terminaison, l’avantage est énorme. Cependant, lorsque l’argument de fonction est utilisé, l’appel par nom est souvent plus lent, nécessitant un mécanisme tel qu’un thunk .

Les Langages .NET d’aujourd’hui peuvent simuler un appel par nom à l’aide de délégués ou de Expression<T>paramètres. Ce dernier se traduit par un arbre syntaxique abstrait donné à la fonction. Eiffel fournit des agents, qui représentent une opération à évaluer en cas de besoin. Seed7 fournit un appel par nom avec des paramètres de fonction. Les programmes Java peuvent effectuer une évaluation paresseuse similaire à l’aide d’ expressions lambda et de l’ java.util.function.Supplier<T>interface.

Appel par besoin

L’appel par besoin est une variante mémorisée de l’appel par le nom, où, si l’argument de la fonction est évalué, cette valeur est stockée pour une utilisation ultérieure. Si l’argument est pur (c’est-à-dire sans effets secondaires), cela produit les mêmes résultats que l’appel par nom, ce qui permet d’économiser le coût de recalcul de l’argument.

Haskell est un langage bien connu qui utilise l’évaluation des appels par besoin. Étant donné que l’évaluation des expressions peut se produire arbitrairement loin dans un calcul, Haskell ne prend en charge que les effets secondaires (tels que mutation ) via l’utilisation de monads . Cela élimine tout comportement inattendu des variables dont les valeurs changent avant leur évaluation différée.

Dans l’implémentation de R de l’appel par besoin, tous les arguments sont passés, ce qui signifie que R autorise des effets secondaires arbitraires.

L’évaluation paresseuse est l’implémentation la plus courante de la sémantique d’appel par besoin, mais des variations comme l’évaluation optimiste existent. Les Langages .NET implémentent l’appel par besoin en utilisant le type Lazy<T>.

La réduction de graphe est une implémentation efficace de l’évaluation paresseuse.

Appel par extension de macro

L’extension d’appel par macro est similaire à l’appel par nom, mais utilise la substitution textuelle plutôt que la capture, évitant ainsi la substitution. Mais la substitution de macro peut provoquer des erreurs, entraînant une capture de variable , conduisant à un comportement indésirable. Les Macros hygiéniques évitent ce problème en vérifiant et en remplaçant les variables masquées qui ne sont pas des paramètres.

Appel par futur

“Call by future”, également connu sous le nom de “parallel call by name” ou “leient evaluation”, [37] est une stratégie d’évaluation concurrente combinant une sémantique non stricte avec une évaluation impatiente. La méthode nécessite une planification et une synchronisation dynamiques fines, mais convient aux machines massivement parallèles.

La stratégie crée un avenir (promesse) pour le corps de la fonction et chacun de ses arguments. Ces contrats à terme sont calculés en même temps que le flux du reste du programme. Lorsqu’un futur A nécessite la valeur d’un autre futur B qui n’a pas encore été calculé, le futur A bloque jusqu’à ce que le futur B finisse de calculer et ait une valeur. Si le futur B a déjà fini de calculer la valeur est renvoyée immédiatement. Les conditionnels bloquent jusqu’à ce que leur condition soit évaluée, et les lambdas ne créent pas de contrats à terme tant qu’ils ne sont pas entièrement appliqués. [38]

S’il est implémenté avec des processus ou des threads, la création d’un futur générera un ou plusieurs nouveaux processus ou threads (pour les promesses), l’accès à la valeur les synchronisera avec le thread principal, et terminer le calcul du futur correspond à tuer les promesses en calculant son valeur. Si elle est implémentée avec une coroutine , comme dans .NET async/await , la création d’un futur appelle une coroutine (une fonction asynchrone), qui peut céder à l’appelant, et à son tour être renvoyée lorsque la valeur est utilisée, en mode multitâche coopératif.

La stratégie est non déterministe, car l’évaluation peut intervenir à tout moment entre la création du futur (c’est-à-dire lorsque l’expression est donnée) et l’utilisation de la valeur du futur. La stratégie est non stricte car le corps de la fonction peut renvoyer une valeur avant que les arguments ne soient évalués. Cependant, dans la plupart des implémentations, l’exécution peut toujours rester bloquée en évaluant un argument inutile. Par exemple, le programme

f x = 1 / x g y = 1 principal = imprimer ( g ( f 0 ))

peut soit gfinir avant fet produire 1, soit entraîner une erreur due à l’évaluation de 1/0. [22]

L’appel par futur est similaire à l’appel par besoin dans la mesure où les valeurs ne sont calculées qu’une seule fois. Avec une gestion soigneuse des erreurs et de la non-résiliation, en particulier la terminaison des contrats à terme en cours s’il est déterminé qu’ils ne seront pas nécessaires, l’appel par futur a également les mêmes propriétés de terminaison que l’évaluation des appels par besoin. [38] Cependant, l’appel par futur peut effectuer un travail spéculatif inutile par rapport à l’appel par besoin, comme l’évaluation approfondie d’une structure de données paresseuse. [22] Cela peut être évité en utilisant des contrats à terme paresseux qui ne démarrent pas le calcul tant qu’il n’est pas certain que la valeur est nécessaire.

Évaluation optimiste

L’évaluation optimiste est une variante d’appel par besoin où l’argument de la fonction est partiellement évalué dans un style d’appel par valeur pendant un certain temps (qui peut être ajusté au moment de l’ exécution ). Une fois ce temps écoulé, l’évaluation est abandonnée et la fonction est appliquée à l’aide d’un appel par besoin. [39] Cette approche évite certaines des dépenses d’exécution de l’appel en fonction des besoins tout en conservant les caractéristiques de terminaison souhaitées.

Voir également

  • Forme normale bêta
  • Comparaison des langages de programmation
  • eval
  • Calcul lambda
  • Appel par valeur push
  • Évaluation partielle

Références

  1. ^ Araki, Shota; Nishizaki, Shin-ya (novembre 2014). “Évaluation par appel du nom des calculs RPC et RMI” . Théorie et pratique du calcul : 1. doi : 10.1142/9789814612883_0001 . Récupéré le 21 août 2021 .
  2. ^ Turbak, Franklyn; Gifford, David (18 juillet 2008). Concepts de conception dans les langages de programmation . Presse du MIT. p. 309.ISBN _ 978-0-262-30315-6.
  3. ^ Manivelle, Erik; Felleisen, Matthias (1991). “Le passage de paramètres et le calcul lambda”. Actes du 18e symposium ACM SIGPLAN-SIGACT sur les principes des langages de programmation – POPL ’91 : 2. CiteSeerX 10.1.1.23.4385 . doi : 10.1145/99583.99616 .
  4. ^ un bc Wilhelm , Reinhard; Seidl, Helmut (10 novembre 2010). Conception du compilateur : machines virtuelles . Springer Science et médias d’affaires. p. 61. ISBN 978-3-642-14909-2.
  5. ^ Nita, Stefania Loredana; Mihailescu, Marius (2017). “Présentation” . Concurrent pratique Haskell : 3. doi : 10.1007/978-1-4842-2781-7_1 .
  6. ^ Pierce, Benjamin C. (2002). Types et langages de programmation . Presse du MIT . p. 56. ISBN 0-262-16209-1.
  7. ^ Daniel P. Friedman; Baguette Mitchell (2008). Essentials of Programming Languages ​​(troisième éd.). Cambridge, MA : La presse du MIT . ISBN 978-0262062794.
  8. ^ un b Scott, Michael Lee (2016). La pragmatique du langage de programmation (quatrième éd.). Waltham, MA : Elsevier. ISBN 9780124104778.
  9. ^ Hasti, Rebecca. « Passage de paramètres » . CS 536 : Introduction aux langages de programmation et aux compilateurs . Université du Wisconsin . Récupéré le 22 août 2021 .
  10. ^ Fay, Colin (30 juillet 2018). “À propos de l’évaluation paresseuse” . R-blogueurs . Récupéré le 21 août 2021 .
  11. ^ Wadsworth, Christopher P. (1971). Sémantique et Pragmatique du Lambda Calculus (PhD). L’université d’Oxford.
  12. ^ “PHP : Passer par référence – Manuel” . www.php.net . Récupéré le 04/07/2021 .
  13. ^ BillWagner. “Paramètres de passage – Guide de programmation C#” . docs.microsoft.com . Récupéré le 04/07/2021 .
  14. ^ KathleenDollard. “Passer des arguments par valeur et par référence – Visual Basic” . docs.microsoft.com . Récupéré le 04/07/2021 .
  15. ^ Filipek, Bartlomiej. “Ordre d’évaluation d’expression plus strict en C++17” . Histoires C++ . Récupéré le 24 août 2021 .
  16. ^ Abelson, Harold ; Sussman, Gerald Jay (1996). « Ordre normal et ordre applicatif » . Structure et interprétation des programmes informatiques (deuxième éd.). Cambridge, Mass. : MIT Press. ISBN 0-262-01153-0.
  17. ^ Reese, Richard M. (14 octobre 2015). Apprentissage de la programmation fonctionnelle Java . Packt Publishing Ltd. p. 106. ISBN 978-1-78528-935-4.
  18. ^ Antani, Véd; Timms, Simon; Mantyla, Dan (31 août 2016). JavaScript : programmation fonctionnelle pour les développeurs JavaScript . Packt Publishing Ltd. p. 614.ISBN _ 978-1-78712-557-5.
  19. ^ Seacord, Robert C. “EXP30-C. Ne dépendez pas de l’ordre d’évaluation des effets secondaires” . Norme de codage SEI CERT C . Université Carnegie-Mellon . Récupéré le 23 août 2021 .
  20. ^ Anglade, S.; Lacrampe, JJ; Queinnec, C. (octobre 1994). “Sémantique des combinaisons dans le schéma” (PDF) . Pointeurs Lisp ACM SIGPLAN . VII (4) : 15-20. doi : 10.1145/382109.382669 .
  21. ^ “Pourquoi les arguments de la fonction OCaml sont-ils évalués de droite à gauche ?” . OCaml . 30 novembre 2017.
  22. ^ un bcde Tremblay , G. (avril 2000) . “L’évaluation indulgente n’est ni stricte ni paresseuse”. Langages informatiques . 26 (1): 43–66. CiteSeerX 10.1.1.137.9885 . doi : 10.1016/S0096-0551(01)00006-6 .
  23. ^ George, Lai (mars 1987). Évaluation efficace de l’ordre normal grâce aux informations sur la rigueur (MSc). Université d’Utah. p. dix.
  24. ^ Borning, Alan (automne 1999). “Évaluation d’ordre applicatif vs normal dans les langages fonctionnels” (PDF) . CSE 505 : Concepts des langages de programmation . Université de Washington . Récupéré le 23 août 2021 .
  25. ^ Abelson, Harold ; Sussman, Gerald Jay (1996). « Ordre normal et ordre applicatif » . Structure et interprétation des programmes informatiques (deuxième éd.). Cambridge, Mass. : MIT Press. ISBN 0-262-01153-0.
  26. ^ un b Sturm, Oliver (11 avril 2011). Programmation fonctionnelle en C# : techniques de programmation classiques pour les projets modernes . John Wiley et fils. p. 91. ISBN 978-0-470-74458-1.
  27. ^ Marlow, Simon. “Pourquoi ne puis-je pas obtenir une trace de la pile ?” . Atelier des implémenteurs Haskell 2012 . Récupéré le 25 août 2021 .
  28. ^ Nilsson, Henrik (1999). “Traçage pièce par pièce: débogage abordable pour les langages fonctionnels paresseux”. Actes de la quatrième conférence internationale ACM SIGPLAN sur la programmation fonctionnelle – ICFP ’99 : 36–47. CiteSeerX 10.1.1.451.6513 . doi : 10.1145/317636.317782 .
  29. ^ “Java est passe-par-valeur, bon sang!” . Récupéré le 24/12/2016 .
  30. ^ Liskov, Barbara; Atkinson, Russ; Fleur, Tobie ; Mousse, Eliot ; Schaffert, Craig; Scheifler, Craig; Snyder, Alan (octobre 1979). “Manuel de référence CLU” (PDF) . Laboratoire d’informatique . Massachusetts Institute of Technology. Archivé de l’original (PDF) le 2006-09-22 . Récupéré le 19/05/2011 .
  31. ^ Lundh, Fredrik. “Appel par objet” . effbot.org . Récupéré le 19/05/2011 .
  32. ^ “Java est passe-par-valeur, bon sang!” . Récupéré le 24/12/2016 .
  33. ^ Manuel de référence CLU (1974) , p. 14-15. sfnp error: no target: CITEREFCLU_Reference_Manual1974 (help)
  34. ^ Remarque : dans le langage CLU, “variable” correspond à “identifiant” et “pointeur” dans l’usage standard moderne, et non au sens général/habituel de variable .
  35. ^ “CA1021 : évitez les paramètres” . Microsoft.
  36. ^ “RPC : version 2 de la spécification du protocole d’appel de procédure à distance” . tools.ietf.org . IETF . Récupéré le 7 avril 2018 .
  37. ^ McCollin, Thomas Gwynfryn; Morell, Tobias. “Un jeu de paradigmes: une étude d’utilisabilité des idiomes fonctionnels dans la programmation de gameplay” (PDF) . Université d’Alborg. p. 6 . Récupéré le 11 janvier 2022 .
  38. ^ un Schauser b , Klaus E.; Goldstein, Seth C. (1995). “Combien de non-strictité les programmes indulgents exigent-ils ?” (PDF) . Actes de la septième conférence internationale sur les langages de programmation fonctionnels et l’architecture informatique – FPCA ’95 : 216–225. doi : 10.1145/224164.224208 . Récupéré le 7 janvier 2022 .
  39. ^ Ennals, Robert; Jones, Simon Peyton (août 2003). « Évaluation optimiste : une stratégie d’évaluation rapide pour les programmes non stricts » .

Lectures complémentaires

  • Abelson, Harold ; Sussman, Gerald Jay (1996). Structure et interprétation des programmes informatiques (deuxième éd.). Cambridge, Massachusetts : The MIT Press. ISBN 978-0-262-01153-2.
  • Baker-Finch, Clem ; roi, David ; Hall, Jon; Trinder, Phil (1999-03-10). “Une sémantique opérationnelle pour les appels parallèles par besoin” (ps) . Rapport de recherche . Faculté de mathématiques et d’informatique, The Open University. 99 (1).
  • Ennals, Robert; Peyton Jones, Simon (2003). Évaluation optimiste : une stratégie d’évaluation rapide pour les programmes non stricts (PDF) . Conférence internationale sur la programmation fonctionnelle. Presse ACM.
  • Ludäscher, Bertram (2001-01-24). “Notes de cours CSE 130” . CSE 130 : Langages de programmation : principes et paradigmes .
  • En lignePierce, Benjamin C. (2002). Types et langages de programmation . Presse du MIT . ISBN 0-262-16209-1.
  • Sestoft, Peter (2002). Mogensen, T; Schmidt, D; Sudborough, IH (éd.). Démonstration de la réduction du calcul lambda (PDF) . L’essence du calcul : complexité, analyse, transformation. Essais dédiés à Neil D. Jones . Notes de cours en informatique. Vol. 2566. Springer-Verlag. p. 420–435. ISBN 3-540-00326-6.
  • “Appel par valeur et appel par référence en programmation C” . Appel par valeur et appel par référence en programmation C expliqué . Archivé de l’original le 2013-01-21.

Liens externes

  • Le visualiseur interactif en ligne de la géométrie de l’interaction , implémentant une machine à base de graphes pour plusieurs stratégies d’évaluation courantes.
You might also like
Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More