Dans de nombreuses langues, la syntaxe function_name(arg1, arg2, ...)
est utilisée pour appeler une fonction. Lorsque nous voulons appeler la fonction sans aucun argument, nous devons le faire function_name()
.
Je trouve étrange qu'un compilateur ou un interpréteur de script ait besoin ()
de le détecter avec succès en tant qu'appel de fonction. Si une variable est connue pour être appelable, pourquoi ne serait-il pas function_name;
suffisant?
Par contre, dans certaines langues, on peut faire: function_name 'test';
ou même function_name 'first' 'second';
appeler une fonction ou une commande.
Je pense que les parenthèses auraient été meilleures si elles n’avaient été nécessaires que pour déclarer l’ordre de priorité. À d’autres endroits, elles étaient facultatives. Par exemple, faire if expression == true function_name;
devrait être aussi valide que if (expression == true) function_name();
.
La chose la plus agaçante à mon avis est de le faire 'SOME_STRING'.toLowerCase()
quand clairement, la fonction prototype ne nécessite aucun argument. Pourquoi les concepteurs ont-ils décidé de ne pas utiliser le plus simple 'SOME_STRING'.lower
?
Disclaimer: Ne vous méprenez pas, j'aime la syntaxe C-like! ;) Je demande simplement si cela pourrait être mieux. Requérir ()
présente des avantages en termes de performances, ou facilite-t-il la compréhension du code? Je suis vraiment curieux de savoir quelle est exactement la raison.
la source
()
mais ce qui ressort dans votre message est laif (expression == true)
déclaration. Vous vous inquiétez des superflus()
, mais vous utilisez un superflu== true
:)Réponses:
Pour les langages qui utilisent des fonctions de première classe , il est assez courant que la syntaxe de référence à une fonction soit la suivante:
alors que l'acte d' appeler cette fonction est:
a
dans l'exemple ci-dessus serait une référence à la fonction ci-dessus (et vous pourriez l'appeler en faisanta()
), tout enb
contenant la valeur de retour de la fonction.Bien que certaines langues puissent faire des appels de fonction sans parenthèses, il peut être difficile de dire si elles appellent la fonction ou font simplement référence à la fonction.
la source
make_list
regroupe ses arguments dans une liste, il y a une grande différence entre lef(make_list)
passage d'une fonction àf
une liste vide.SATInstance.solve
ou une autre fonction pure incroyablement coûteuse à une autre fonction sans essayer immédiatement de l'exécuter. Cela rend également les choses vraiment gênantes sisomeFunction
fait quelque chose de différent selon que l’someFunction
on déclare qu’il est pur. Par exemple, si j'ai (pseudocode)func f() {return g}
,func g() {doSomethingImpure}
etfunc apply(x) {x()}
puis lesapply(f)
appelsf
. Si je déclare alors quef
c'est pur, alors soudainapply(f)
passeg
àapply
,apply
appelleg
et fait quelque chose d'impur.apply(f)
, sig
est impur (etapply
aussi?), Alors tout est impur et se décompose bien sûr.foo.bar()
n'est pas une opération "méthode d'appelbar
d'objetfoo
" mais plutôt deux opérations, "déréférencer le champbar
d'objetfoo
et ensuite appeler l'objet renvoyé à partir de ce champ". Ainsi, l' opérateur.
est le sélecteur de champ et()
l' opérateur d'appel et ils sont indépendants.En effet, Scala le permet, bien qu’une convention soit suivie: si la méthode a des effets secondaires, les parenthèses doivent néanmoins être utilisées.
En tant qu’écrivain du compilateur, la présence garantie de parenthèses me conviendrait fort bien; Je saurais toujours que c'est un appel à une méthode et je n'aurais pas à construire une bifurcation pour les cas impairs.
En tant que programmeur et lecteur de code, la présence de parenthèses ne laisse aucun doute sur le fait qu'il s'agit d'un appel de méthode, même si aucun paramètre n'est transmis.
Le passage de paramètres n'est pas la seule caractéristique qui définit un appel de méthode. Pourquoi devrais-je traiter une méthode sans paramètre différente de la méthode qui a des paramètres?
la source
def foo()
est une méthode avec une liste de paramètres vide, etdef foo
est une méthode sans liste de paramètres et les deux sont des choses différentes ! En général, une méthode sans liste de paramètres doit être appelée sans liste d'arguments et une méthode avec une liste de paramètres vide doit être appelée avec une liste d'arguments vide. Appeler une méthode sans liste de paramètres avec un argument vide est en fait…apply
méthode de l'objet renvoyé par l'appel à la méthode avec une liste d'arguments vide, alors que l'appel d'une méthode avec une liste de paramètres vide sans liste d'arguments peut être interprété différemment en fonction du contexte, comme l'appelant la méthode avec une liste d'arguments vide, η-expansion dans une méthode partiellement appliquée (c.-à-d. "méthode de référence"), ou il pourrait ne pas compiler du tout. De plus, Scala suit le principe d'accès uniforme, en ne distinguant pas (syntaxiquement) entre appeler une méthode sans liste d'arguments et référencer un champ.Il s’agit en fait d’un choix assez subtil de choix de syntaxe. Je parlerai de langages fonctionnels, basés sur le calcul lambda typé.
Dans ces langues, chaque fonction a exactement un argument . Ce que nous considérons souvent comme des "arguments multiples" est en réalité un paramètre unique du type de produit. Ainsi, par exemple, la fonction qui compare deux entiers:
prend une seule paire d'entiers. Les parenthèses ne désignent pas les paramètres de la fonction; ils sont utilisés pour faire correspondre l'argument. Pour vous convaincre qu'il ne s'agit vraiment que d'un argument, nous pouvons utiliser les fonctions projectives au lieu du filtrage par motif pour déconstruire la paire:
Ainsi, les parenthèses sont vraiment une commodité qui nous permet de mettre en correspondance des motifs et d’économiser du texte. Ils ne sont pas nécessaires.
Qu'en est-il d'une fonction qui n'a apparemment aucun argument? Une telle fonction a effectivement du domaine
Unit
. Le membre unique deUnit
est habituellement écrit ainsi()
, c'est pourquoi les parenthèses apparaissent.Pour appeler cette fonction, il faudrait l'appliquer à une valeur de type
Unit
, qui doit être()
, pour finir par écriresay_hi ()
.Donc, il n’existe vraiment pas de liste d’arguments!
la source
()
ressemblent à des cuillères moins le manche.leq
fonction qui utilise<
plutôt que<=
est assez déroutant!()
pour la représenter. Je ne serais pas surpris si au moins une partie de la raison était de ressembler à une "fonction sans paramètre". Cependant, il existe une autre explication possible pour la syntaxe. Dans l'algèbre des types,Unit
c'est en fait l'identité des types de produits. Un produit binaire est écrit en tant que(a,b)
, nous pourrions écrire un produit unaire(a)
, une extension logique serait donc d'écrire le produit nullary simplement()
.En Javascript par exemple, utiliser un nom de méthode sans () renvoie la fonction elle-même sans l'exécuter. De cette façon, vous pouvez par exemple transmettre la fonction en tant qu'argument à une autre méthode.
En Java, le choix a été fait qu'un identifiant suivi de () ou (...) désigne un appel de méthode, tandis qu'un identifiant without () se réfère à une variable membre. Cela pourrait améliorer la lisibilité, car vous ne doutez pas qu'il s'agisse d'une méthode ou d'une variable membre. En fait, le même nom peut être utilisé à la fois pour une méthode et pour une variable membre, les deux seront accessibles avec leur syntaxe respective.
la source
Perl Best Practices
faitObject::toString
identifie une méthode.La syntaxe suit la sémantique, alors commençons par la sémantique:
En réalité, il y a plusieurs façons:
Avoir une syntaxe similaire pour des utilisations différentes est le meilleur moyen de créer un langage ambigu ou à tout le moins déroutant (et nous en avons assez).
En langage C et C-like:
func()
&func
(C créer un pointeur de fonction)someVariable::someMethod
par exemple (limité au récepteur de la méthode, mais toujours utile)Notez que chaque utilisation utilise une syntaxe différente, ce qui vous permet de les distinguer facilement.
la source
::
opérateur Java est un opérateur d'application partielle et non un opérateur de curry. L'application partielle est une opération qui prend une fonction et une ou plusieurs valeurs et renvoie une fonction acceptant moins de valeurs. Le currying ne fonctionne que sur les fonctions, pas sur les valeurs.Aucune des autres réponses n'a tenté de répondre à la question suivante: quelle redondance devrait-il y avoir dans la conception d'une langue? Parce que même si vous pouvez concevoir un langage de telle sorte que
x = sqrt y
x soit défini à la racine carrée de y, cela ne signifie pas nécessairement que vous devriez le faire.Dans une langue sans redondance, chaque séquence de caractères signifie quelque chose, ce qui signifie que si vous faites une seule erreur, vous n'obtiendrez pas de message d'erreur, votre programme agira de manière erronée, ce qui peut être très différent de ce que vous destiné et très difficile à déboguer. (Comme le savent tous ceux qui ont travaillé avec des expressions rationnelles.) Un peu de redondance est une bonne chose, car elle permet de détecter bon nombre de vos erreurs. Plus il y a de redondance, plus les diagnostics seront précis. Maintenant, bien sûr, vous pouvez aller trop loin (aucun de nous ne veut écrire en COBOL ces jours-ci), mais il existe un juste équilibre.
La redondance facilite également la lisibilité, car il y a plus d'indices. Il serait très difficile de lire un anglais sans redondance, de même que les langages de programmation.
la source
:=
comparaison==
,=
son utilisation n'étant utilisable que pour l'initialisation à la compilation, la probabilité que des erreurs de frappe se transforment en assignations serait grandement réduite.()
de l'invocation d'une fonction à argument zéro, mais aussi d'un jeton pour "prendre l'adresse de" une fonction. La première action est beaucoup plus courante, il est donc inutile de sauvegarder un jeton dans le cas le moins fréquent.Dans une langue avec des effets secondaires, l’OMI est vraiment utile pour différencier la lecture d’une variable (en théorie, sans effets secondaires)
et en appelant une fonction pouvant entraîner des effets secondaires
OTOH, s'il n'y a pas d'effets secondaires (autres que ceux encodés dans le système de types), il est alors inutile de faire la différence entre lire une variable et appeler une fonction sans argument.
la source
noun.verb
la syntaxe pourrait être aussi claire de sensnoun.verb()
et légèrement moins encombrée. De même, une fonctionmember_
renvoyant une référence de gauche peut offrir une syntaxe conviviale par rapport à une fonction de définition habituelle:obj.member_ = new_value
vsobj.set_member(new_value)
(le_
suffixe est un indice qui dit "expose", rappelant les_
préfixes des fonctions "cachées").noun.verb
appel de fonction signifie, comment le différenciez-vous d'un simple accès membre?Dans la plupart des cas, il s’agit de choix syntaxiques de la grammaire du langage. Il est utile que la grammaire de chaque construction individuelle soit (relativement) non ambiguë quand elle est prise dans son ensemble. (S'il y a des ambiguïtés, comme dans certaines déclarations C ++, il doit y avoir des règles spécifiques pour la résolution.) Le compilateur n'a pas la latitude de deviner; il est nécessaire de suivre la spécification de langue.
Visual Basic, sous différentes formes, différencie les procédures qui ne renvoient pas de valeur et les fonctions.
Les procédures doivent être appelées en tant qu'énoncé et ne nécessitent pas de parenthèse, mais uniquement des arguments séparés par des virgules, le cas échéant. Les fonctions doivent être appelées dans le cadre d'une expression et requièrent les parenthèses.
C'est une distinction relativement inutile qui rend la refactorisation manuelle entre les deux formes plus douloureuse que nécessaire.
(D'autre part Visual Basic utilise les mêmes parens
()
pour les références de tableau comme pour les appels de fonction, de sorte que les références de tableau ressemble à des appels de fonction. Et cela facilite refactoring manuelle d'un tableau dans un appel de fonction! Alors, nous pourrions réfléchir à d' autres langues utilisent[]
l » pour les références de tableau, mais je m'éloigne du sujet ...)Dans les langages C et C ++, le contenu des variables est automatiquement accessible en utilisant leur nom. Si vous souhaitez faire référence à la variable elle-même au lieu de son contenu, vous appliquez l'
&
opérateur unaire .Ce type de mécanisme pourrait également être appliqué aux noms de fonction. Le nom de la fonction brute pourrait impliquer un appel de fonction, alors qu'un
&
opérateur unaire serait utilisé pour désigner la fonction (elle-même) en tant que donnée. Personnellement, j'aime bien l'idée d'accéder à des fonctions sans argument sans effets secondaires sans la même syntaxe que les variables.Ceci est parfaitement plausible (comme le sont d’autres choix syntaxiques pour cela).
la source
call <procedure name> (<arguments>)
? Je suis sûr que c’était une façon valable d’utiliser les procédures à l’époque où j’ai programmé QuickBASIC dans une autre vie ...Call
une méthode où je n'ai presque jamais besoin de code de production "normal".Cohérence et lisibilité.
Si j'apprends que vous appelez la fonction
X
comme ceci:X(arg1, arg2, ...)
, alors je pense que cela fonctionne de la même façon pour aucun argument:X()
.Maintenant, en même temps, j'apprends que vous pouvez définir la variable comme un symbole et l'utiliser comme ceci:
Maintenant, qu'est-ce que je penserais quand je trouverais ça?
Votre proposition est aussi bonne que la mienne. Dans des circonstances normales, le
X
est une variable. Mais si nous prenions votre chemin, cela pourrait aussi être une fonction! Dois-je me rappeler si quel symbole correspond à quel groupe (variables / fonctions)?Nous pourrions imposer des contraintes artificielles, par exemple "Les fonctions commencent par une lettre majuscule. Les variables commencent par une lettre minuscule", mais cela n'est pas nécessaire et complique les choses, même si cela peut parfois aider certains des objectifs de conception.
Note 1: Les autres réponses ignorent complètement une dernière chose: la langue peut vous permettre d'utiliser le même nom pour la fonction et la variable et de faire la distinction par contexte. Voir à
Common Lisp
titre d'exemple. Fonctionx
et variablex
coexistent parfaitement bien.Side note 2: La réponse acceptée nous montre la syntaxe:
object.functionname
. Premièrement, les langages dotés de fonctions de premier ordre ne sont pas universels. Deuxièmement, en tant que programmeur, je considérerais cela comme une information supplémentaire:functionname
appartient à unobject
. Que ceobject
soit un objet, une classe ou un espace de noms importe peu, mais cela me dit qu'il appartient quelque part . Cela signifie que vous devez soit ajouter une syntaxe artificielleobject.
pour chaque fonction globale, soit en créer uneobject
pour contenir toutes les fonctions globales.Et de toute façon, vous perdez la possibilité d'avoir des espaces de noms séparés pour les fonctions et les variables.
la source
Pour ajouter aux autres réponses, prenons cet exemple C:
Si l'opérateur d'invocation était facultatif, il n'y aurait aucun moyen de faire la distinction entre l'attribution de fonction et l'appel de fonction.
Ceci est encore plus problématique dans les langues dépourvues de système de type, par exemple JavaScript, où l'inférence de type ne peut pas être utilisée pour déterminer ce qui est ou non une fonction.
la source
function cross(a, b) { return a + b; }