Question

Pourquoi certaines langues différencient-elles les méthodes qui renvoient une valeur et les méthodes qui n'en renvoient pas?

i.e. dans Oracle PL / SQL, où la principale différence entre une fonction et une procédure est que la fonction doit renvoyer une valeur, et que la procédure ne doit pas.

De même pour les langues qui ne le font pas, pourquoi pas?

EDIT: J'ai trouvé une question connexe qui pourrait intéresser les lecteurs de cette question:

Était-ce utile?

La solution

Parce que dans les conceptions originales de la théorie et de la pratique de l’informatique, les fonctions et les sous-programmes n’avaient pratiquement rien à voir les uns avec les autres.

FORTRAN est généralement considéré comme le premier langage qui a mis en œuvre les deux et a démontré les distinctions. (Les premiers LISP avaient également joué un rôle opposé dans ce domaine, mais ils n’avaient que peu d’impact en dehors du monde universitaire).

Suivant les traditions mathématiques (dont CS faisait encore partie dans les années 60), les fonctions n’étaient perçues que comme l’encapsulation de calculs mathématiques paramétrés uniquement destinés à restituer une valeur dans une expression plus large. Que vous pouvez appeler cela "nu" (F = AZIMUTH (SECONDS)) n’était qu’un cas d’usage trivial.

Les sous-programmes, en revanche, étaient considérés comme un moyen de nommer un groupe d’énoncés censés avoir un effet. Les paramètres ont considérablement augmenté leur convivialité et la seule raison pour laquelle ils ont été autorisés à renvoyer des valeurs de paramètres modifiées était de pouvoir signaler leur statut sans avoir à recourir à des variables globales.

Donc, ils n’avaient vraiment aucun lien conceptuel, à part l’encapsulation et les paramètres.

La vraie question est: "Comment autant de développeurs sont-ils arrivés à les voir de la même façon?"

Et la réponse à cette question est C.

Lorsque K + R a initialement conçu son langage de type assembleur de macros de haut niveau pour le PDP-11 (peut-être déjà démarré sur le PDP-8?), ils ne se faisaient pas des illusions sur l'indépendance du matériel. Pratiquement tous les " uniques " caractéristique du langage était le reflet du langage machine et de l’architecture PDP (voir i ++ et --i). L'une d'elles était la réalisation des fonctions et des sous-routines pouvant être (et a toujours été) implémentées de manière identique dans le PDP, à la différence que l'appelant a simplement ignoré la valeur de retour (dans R0 [, R1]) des sous-routines.

Ainsi est né le pointeur vide, et après que le langage C ait envahi le monde de la programmation, la perception erronée que cet artefact d’implémentation HW / OS (bien que vrai sur presque toutes les plates-formes suivantes) était la même que la sémantique du langage.

Autres conseils

Dans un environnement pur ou à effets, il existe un monde de différences, car il est évident que les méthodes "ne renvoient rien". ne sont utiles que pour leurs effets secondaires.

Ceci est analogue à la distinction entre expressions et déclarations, ce qui peut décombrer une langue et éliminer une classe de programmes généralement erronés (ce qui explique pourquoi C ne le fait pas;)).

Pour donner un petit exemple, lorsque vous faites une distinction claire entre expressions et déclarations, si (x = 3) , par opposition à si (x == 3) est syntaxiquement incorrect (pour utiliser une instruction dans laquelle une expression était attendue) et pas simplement une erreur de type (pour utiliser un entier où un booléen était attendu). Cela a également pour avantage de ne pas autoriser si (x = true) qui serait autorisé par une règle de type dans un contexte où les affectations sont des expressions qui ont la valeur de leur opérande de droite.

Dans un langage qui encapsule les effets avec des monades, la distinction importante devient celle entre:

  • fonctions renvoyant () , qui sont des fonctions pures et ne peuvent renvoyer qu'une seule valeur vide inutile appelée () ou diverge
  • fonctions qui retournent IO () (ou unité dans une autre monade) qui sont des fonctions sans "résultat". sauf les effets dans la monade IO (ou celle choisie)

Excusez-moi de répondre à une question posée il y a deux ans, en particulier avec quelque chose d'unique dans ma propre langue. Felix http: // felix-lang. org mais ici va quand même:)

Dans Felix, les fonctions et les procédures sont différentes, et les procédures n’ont pas que des effets secondaires et sont appelées dans des instructions, alors que les fonctions n’ont pas d’effets secondaires et sont utilisées dans des expressions (parce que Felix a aussi des générateurs qui sont des fonctions avec des effets secondaires ..:)

Non, le modèle d'exécution est fondamentalement différent, principalement pour des raisons de performances, mais pas entièrement. Le modèle est:

  • Les fonctions placent leur adresse de retour sur la pile de la machine, ainsi que la valeur de retour.
  • Les procédures utilisent une liste liée sur le tas. Le code de procédure est plat, il n’utilise pas la pile de la machine.

Ceci est généralement inefficace, alors pourquoi le faire? La réponse est: La procédure de Felix est potentiellement une co-routine (fibres). Ils peuvent basculer le contrôle sur une autre procédure en accédant à un canal. Cela provoque un échange de contrôle.

  • Pour des raisons de performances, copier la pile de machines sur un échange de contrôle n'est pas une option.
  • Pour des raisons de gestion de la mémoire, échanger des pointeurs de pile n'est pas une option non plus.

Le système d’exploitation échange généralement des pointeurs de pile contre des threads, ce qui est relativement rapide, mais pose un problème fondamental sur les machines à adresses linéaires: vous devez limiter la taille maximale de la pile à une valeur ridiculement petite ou limiter le nombre de threads. à une valeur ridiculement petite. Sur un ordinateur 32 bits, l'espace d'adressage est insuffisant pour même envisager cette solution. Sur une machine 64 bits, l’échange de pile a plus de potentiel, mais bien sûr, les demandes des utilisateurs dépassent toujours le matériel 3 jours après sa publication ..:)

Felix échange simplement un pointeur sur les piles basées sur les segments de mémoire. Les commutateurs de contexte sont donc extrêmement rapides et très peu d'espace d'adressage est gaspillé. Bien entendu, le coût est constitué d'allocations de segment de mémoire lors d'appels de procédure.

Dans le compilateur, une grande partie de l’architecture du modèle théorique est optimisée sur une "telle que si". Les performances et l’implémentation réelles peuvent donc être très différentes du modèle théorique, à condition que le compilateur puisse prouver que vous ne pouvez pas faire la différence .. autrement que de se voir refuser la possibilité de préparer une tasse de café à votre guise:)

Alors, vous avez une réponse différente quant à la raison pour laquelle les fonctions et les procédures peuvent être traitées différemment.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top