web-dev-qa-db-fra.com

Pourquoi les langues nécessitent-elles des parenthèses autour des expressions lorsqu'elles sont utilisées avec "if" et "while"?

Les langages comme C, Java et C++ nécessitent tous des parenthèses autour d'une expression entière lorsqu'ils sont utilisés dans un if, while ou switch.

if (true) {
    // Do something
}

par opposition à

if true {
    // Do something
}

Cela me semble étrange car les parenthèses sont redondantes. Dans cet exemple, true est une expression unique à part entière. La parenthèse ne transforme pas sa signification de quelque manière que je sache. Pourquoi cette étrange syntaxe existe-t-elle et pourquoi est-elle si courante? Y a-t-il un avantage dont je ne suis pas au courant?

68
Velovix

Il doit y avoir un moyen de dire où se termine la condition et où commence la branche. Il existe de nombreuses façons de procéder.

Dans certaines langues, il n'y a pas de conditions du tout , par ex. dans Smalltalk, Self, Newspeak, Io, Ioke, Seph et Fancy. La ramification conditionnelle est simplement implémentée comme une méthode normale comme toute autre méthode. La méthode est implémentée sur les objets booléens et est appelée sur un booléen. De cette façon, la condition est simplement le récepteur de la méthode, et les deux branches sont deux arguments, par ex. dans Smalltalk:

aBooleanExpression ifTrue: [23] ifFalse: [42].

Dans le cas où vous êtes plus familier avec Java, cela équivaut à ce qui suit:

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

Dans la famille de langages LISP, la situation est similaire: les conditionnelles ne sont que des fonctions normales (en fait, des macros) et le premier argument est la condition, les deuxième et troisième arguments sont les branches, donc ce ne sont que des arguments de fonction normaux, et il y a rien de spécial nécessaire pour les délimiter:

(if aBooleanExpression 23 42)

Certaines langues utilisent des mots clés comme délimiteurs, par exemple ALGOL, Ada, BASIC, Pascal, Modula-2, Oberon, Oberon-2, Active Oberon, Component Pascal, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

Dans Ruby, vous pouvez utiliser un mot-clé ou un séparateur d'expression (point-virgule ou nouvelle ligne):

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Go requiert que les branches soient des blocs et n'autorise pas les expressions ou les instructions, ce qui rend les accolades obligatoires. Par conséquent, les parenthèses ne sont pas nécessaires, bien que vous puissiez les ajouter si vous le souhaitez; Perl6 et Rust sont similaires à cet égard:

if aBooleanExpression { return 23 } else { return 42 }

Certaines langues utilisent d'autres caractères non alphanumériques pour délimiter la condition, par ex. Python:

if aBooleanExpression: return 23
else: return 42

L'essentiel est: vous avez besoin d'une façon de dire où la condition se termine et où la branche commence. Il y a plusieurs façons de le faire, les parenthèses n'en sont qu'une.

156
Jörg W Mittag

Les parenthèses ne sont inutiles que si vous utilisez des accolades.

if true ++ x;

Par exemple, devient ambigu sans eux.

70
Telastyn

Les parenthèses dans une instruction if n'ont pas la même signification que les parenthèses utilisées dans une expression arithmétique. Les parenthèses dans une expression arithmétique sont utilisées pour regrouper des expressions. Les parenthèses dans une instruction if sont utilisées pour délimiter l'expression booléenne; c'est-à-dire pour différencier l'expression booléenne du reste de l'instruction if.

Dans une instruction if, les parenthèses n'effectuent pas de fonction de regroupement (cependant, dans l'instruction if, vous pouvez toujours utiliser des parenthèses pour regrouper des expressions arithmétiques. L'ensemble de parenthèses externe sert ensuite à délimiter le expression booléenne entière). Les rendre nécessaires simplifie le compilateur, car le compilateur peut compter sur le fait que ces parenthèses sont toujours là.

21
Robert Harvey

Comme d'autres l'ont déjà partiellement souligné, cela est dû au fait que les expressions sont également des instructions valides, et dans le cas d'un bloc avec une seule instruction, vous pouvez supprimer les accolades. Cela signifie que les éléments suivants sont ambigus:

if true
    +x;

Parce qu'il pourrait être interprété comme:

if (true + x) {}

au lieu de:

if (true) {+x;}

Un certain nombre de langages (par exemple Python) vous permettent d'éviter la parenthèse mais ont toujours un marqueur de condition de fin:

si vrai: + x 

Cependant vous avez raison de dire que nous pourrions définir une langue où les parenthèses ne sont jamais requises: une langue où une expression est pas une déclaration valide n'aura pas ce problème.

Malheureusement, cela signifie que des choses comme:

 ++x;
 functionCall(1,2,3);

ne seraient pas des instructions valides, vous devriez donc introduire une syntaxe étrange pour pouvoir effectuer de telles actions sans créer d'expressions. Une façon simple de le faire est de simplement ajouter l’expression par un marqueur comme [statement]:

[statement] ++x;
[statement] functionCall(1,2,3);

Maintenant, l'ambiguïté disparaît car il faudrait écrire:

if true
    [statement] ++x;

Mais comme vous pouvez le voir, je ne vois pas un tel langage être répandu depuis la mise entre parenthèses autour d'une condition ifou une : à sa fin) est bien mieux que de placer un tel marqueur pour chaque expression.


--- (Note: l'utilisation d'un [statement] marker est juste la syntaxe la plus simple à laquelle je puisse penser. Cependant, vous pourriez avoir deux syntaxes complètement distinctes pour les expressions et les instructions sans ambiguïté entre elles qui ne nécessiteraient pas un tel marqueur. Le problème est: le langage serait extrêmement bizarre, car pour faire les mêmes choses dans une expression ou une déclaration, il faudrait utiliser une syntaxe complètement différente.

Une chose qui vient à l'esprit pour avoir deux syntaxes distinctes sans un marqueur aussi explicite serait, par exemple: forcer les instructions à utiliser des symboles Unicode (donc au lieu de for vous utiliseriez une variation unicode des lettres f, o et r), tandis que les expressions doivent être ASCII uniquement.

16
Bakuriu

Il est courant que les langages de la famille C nécessitent ces parenthèses, mais ils ne sont pas universels.

L'un des changements syntaxiques les plus notables de Perl 6 est qu'ils ont modifié la grammaire de sorte que vous n'ayez pas à donner les parenthèses autour de if, for et des conditions d'instructions similaires. Donc, quelque chose comme ça est parfaitement valable dans Perl 6:

if $x == 4 {
    ...
}

comme si

while $queue.pop {
    ...
}

Cependant, comme ce ne sont que des expressions, vous pouvez les mettre entre parenthèses si vous le souhaitez, auquel cas ce ne sont que des expressions de regroupement ordinaires au lieu d'une partie requise de la syntaxe comme elles sont en C, C #, Java etc.

Rust a une syntaxe similaire à Perl 6 dans ce département:

if x == 4 {
    ...
}

Il me semble qu'une caractéristique des langages plus modernes inspirés du C est de regarder des choses comme ça et de s'interroger sur leur suppression.

10
Matthew Walton

Il y a un aspect que je suis surpris qu'aucune des réponses existantes n'ait soulevé.

C, et de nombreux dérivés et sosies de C, a une particularité en ce que la valeur d'une affectation est la valeur affectée. Une conséquence de cela est que ne affectation peut être utilisée lorsqu'un est attendue.

Cela vous permet d'écrire des choses comme

if (x = getValue() == 42) { ... }

ou

if (x == y = 47) { ... }

ou

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(qui est implicitement traité comme while (n < m && *p1++ = *p2++ != 0) { n++; } parce que C traite non nul comme vrai; d'ailleurs, je pense que c'est à peu près strncpy () dans la bibliothèque standard C)

ou même

if (x = 17);

et tout est valide. Toutes les combinaisons syntaxiquement valides ne sont pas nécessairement utiles (et les compilateurs modernes avertissent spécifiquement des affectations à l'intérieur des conditions, car c'est une erreur courante) , mais une partie est réellement utile.

L'analyse de telles instructions serait probablement beaucoup plus difficile s'il n'y avait pas de moyen non ambigu pour déterminer où commence et se termine l'expression conditionnelle.

Les parenthèses étaient déjà utilisées pour délimiter les noms de fonction des arguments de fonction, donc je suppose qu'ils semblaient être un choix naturel également pour délimiter les mots-clés des arguments de mot-clé.

Bien sûr, des syntaxes alternatives pourraient être définies pour faire la même chose. Mais cela augmenterait la complexité, en particulier dans l'analyseur qui aurait alors besoin de traiter avec deux ensembles de syntaxe différents pour pratiquement la même chose. À l'époque de la conception de C, la puissance de calcul (en termes de capacité de calcul des nombres, de mémoire de travail et de capacité de stockage) était extrêmement limitée; tout ce qui réduisait la complexité à peu ou pas de frais de lisibilité était presque certainement un changement bienvenu.

L'utilisation de parenthèses peut sembler un peu archaïque aujourd'hui, mais ce n'est pas comme cela, étant donné que quelqu'un connaît bien le langage, cela nuit à la lisibilité par rapport à une autre syntaxe capable d'exprimer les mêmes choses.

6
a CVn

La raison est principalement historique.

Au moment où le premier compilateur C a été écrit, les ordinateurs ont un ram, un processeur et des compilateurs très limités où ils étaient écrits "à la main" avec peu d'outils pour aider les rédacteurs du compilateur. Par conséquent les règles complexes étaient coûteuses à implémenter dans un compilateur. C++, C #, Java, etc. ont tous été conçus pour être faciles à apprendre pour les programmeurs C, par conséquent, aucune modification "inutile" n'a été apportée.

Dans les langues "c like", les conditions (if, while, etc) ne nécessite pas de code explicite block off, vous pouvez simplement utiliser une simple instruction.

if (a == d) doIt()

ou vous pouvez combiner des instructions dans un compound statement en les mettant dans {}

Nous aimons que le compilateur trouve l'erreur que nous commettons et donne un message d'erreur que nous pouvons comprendre.

5
Ian

Beaucoup ici pensent que sans les parenthèses, la syntaxe serait ambiguë et implique silencieusement que ce serait en quelque sorte mauvais ou même une situation impossible.

En fait, les langues ont de nombreuses façons de gérer les ambiguïtés. La priorité des opérateurs n'est qu'une des instances de cette rubrique.

Non, l'ambiguïté n'est pas la raison des parenthèses. Je suppose que l'on pourrait simplement créer une version de C qui ne nécessite pas les parenthèses autour de la condition (les rendant ainsi facultatives) et qui crée toujours du code valide dans tous les cas. L'exemple de if a ++ b; Pourrait être interprété comme étant équivalent à if (a) ++b; ou if (a++) b;, ce qui semble plus approprié.

La question de savoir pourquoi Dennis Ritchie a choisi de rendre le () obligatoire (et donc d'inventer ce mème pour de nombreuses langues dérivées) est plutôt linguistique. Je suppose que l'idée d'indiquer clairement que la condition est un expression plutôt qu'un commande était le père de la pensée.

Et en fait, C a été conçu pour être analysable en utilisant un analyseur en un seul passage. L'utilisation d'une syntaxe avec des parenthèses obligatoires autour de la condition prend en charge cet aspect.

3
Alfe

Java et C++ ont tous deux été développés après que C soit devenu un langage de programmation très populaire. Une considération dans la conception de chacun de ces langages était qu'elle ferait appel aux programmeurs C et séduirait ces programmeurs pour utiliser le nouveau langage. (J'étais l'un des programmeurs C qu'ils ont réussi à courtiser.) C++ a également été conçu pour être (presque) interchangeable avec le code C. Pour atteindre ces objectifs, C++ et Java ont adopté une grande partie de la syntaxe de C, y compris les parenthèses autour des conditions de if, while et switch instructions.

D'où la raison pour laquelle tous ces langages nécessitent des parenthèses autour des conditions de ces déclarations parce que C le fait, et la question est vraiment juste pourquoi C nécessite ces parenthèses.

Les origines du langage C sont décrites dans cet article de Dennis Ritchie, l'un des principaux auteurs de son développement (certains pourraient même dire le principal auteur de son développement). Comme indiqué dans cet article, C a été initialement développé au début des années 1970 en tant que langage de programmation système pour les ordinateurs avec un espace extrêmement limité dans la mémoire principale. Il était souhaitable d'avoir une langue de niveau supérieur à la langue de l'assembly, mais étant donné les ressources disponibles pour travailler, la facilité d'analyse de la langue était également importante. L'exigence de parenthèses faciliterait l'identification du code conditionnel.

On pourrait également en déduire que la possibilité d'écrire des programmes en utilisant moins de caractères a été considérée comme un avantage, et deux parenthèses prennent moins de place que le mot clé THEN qui était utilisé dans FORTRAN et d'autres langages de haut niveau à cette époque; en fait, puisque les parenthèses pouvaient également remplacer les espaces comme délimiteurs de symboles, if(a==b) était quatre caractères entiers plus court que IF a==b THEN.

En tout état de cause, il fallait trouver un équilibre entre la facilité avec laquelle les êtres humains seraient capables de lire, d'écrire et de comprendre des programmes écrits en C, la facilité avec laquelle un compilateur pouvait analyser et compiler des programmes écrits en C et le nombre de kilo-octets (!) serait nécessaire à la fois pour la source du programme et le compilateur lui-même. Et les parenthèses autour des conditions des instructions if, while et switch étaient la façon dont les gens ont choisi de trouver cet équilibre dans la conception de C.

Comme en témoignent plusieurs autres réponses, une fois que vous supprimez les circonstances particulières dans lesquelles C a été développé, toutes sortes de formes alternatives de syntaxe ont été utilisées pour les conditions de divers langages de programmation. Donc, les parenthèses se résument vraiment à une décision de conception qui a été prise par quelques personnes sous certaines contraintes à un certain moment de l'histoire.

3
David K

Les parenthèses autour de if les conditions ne sont pas requises dans Fortran, Cobol, PL/1, ALGOL, Algo-68, Pascal, Modula, XPL, PL/M, MPL, ... ou dans tout autre langage qui a un then mot clé. then sert à délimiter le condition des statement suivants.

La parenthèse fermante en C, etc. fonctionne comme then, et la première est formellement redondante.

Les remarques ci-dessus s'appliquent aux langues traditionnellement analysées.

0
user207421