Pourquoi les langues exigent-elles des parenthèses autour des expressions lorsqu'elles sont utilisées avec «if» et «while»?

67

Les langages tels que C, Java et C ++ nécessitent tous des parenthèses autour d'une expression entière lorsqu'ils sont utilisés dans un iffichier 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, trueest une expression unique en elle-même. La parenthèse ne transforme pas sa signification de quelque manière que je sache. Pourquoi cette syntaxe étrange existe-t-elle et pourquoi est-elle si courante? Y a-t-il un avantage à cela dont je ne suis pas au courant?

Velovix
la source
20
Pascal n'a pas besoin de parenthèses (car cela nécessite a THEN).
JimmyB
30
Python, pas Ruby.
smci
31
Je pense que C utilise des parenthèses car les accolades sont facultatives pour un corps à déclaration unique. Ou peut-être une meilleure façon de le dire est que les accolades ne font pas partie de la ifdéclaration, elles créent simplement une déclaration composée.
Fred Larson
7
Curieusement, Go nécessite des accolades mais pas des parenthèses.
Kos
25
La question est un peu tautologique. Pourquoi les bouches d'égout rondes sont-elles tout autour? Pourquoi tous les frères sont-ils des hommes? Pourquoi les langues nécessitant le paren ont-elles toutes besoin de parens? Les plaques d'égout rondes sont rondes par définition; les frères sont des hommes par définition; les langues qui nécessitent des parens ont besoin de parens par définition.
Eric Lippert

Réponses:

155

Il doit y avoir un moyen de dire où la condition se termine et où la branche commence. Il y a différentes façons de le faire.

Dans certaines langues, il n'y a pas conditionals du tout , par exemple dans Smalltalk, auto, novlangue, Io, Ioke, Seph et Fantaisie. Le branchement conditionnel est simplement implémenté comme une méthode normale comme toute autre méthode. La méthode est implémentée sur des objets booléens et est appelée sur un booléen. De cette façon, la condition est simplement le destinataire de la méthode et les deux branches sont deux arguments, par exemple dans Smalltalk:

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

Si 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 sont juste 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 fonctions normales, et il y a rien de spécial n'est 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, Oberon actif, Component Pascal, Zonnon, Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

En 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 exige que les branches soient des blocs et n'autorise pas les expressions ou les déclarations, ce qui rend les accolades obligatoires. Par conséquent, les parenthèses ne sont pas obligatoires, mais vous pouvez 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 exemple Python:

if aBooleanExpression: return 23
else: return 42

La ligne du bas est: vous avez besoin d' un moyen de dire où la condition se termine et où la branche commence. Il y a beaucoup de façons de le faire, les parenthèses en sont une.

Jörg W Mittag
la source
8
Très bon aperçu.
Peter - Réintégrer Monica
2
bien sûr, dans un langage où les expressions nues ne sont pas une instruction (par exemple, quelque chose comme l'ancienne version de BASIC où une valeur calculée doit être assignée à une variable ou passée à une autre instruction) ou s'il n'y a pas d'opérateur de préfixe, vous devriez toujours pouvoir pour identifier la fin d'une expression et le début d'une déclaration de toute façon. Je pouvais certainement voir une variante BASIC gérer sans délimiteur à la fin d'une instruction IF.
Periata Breatta
4
De plus, comme C a été conçu dans les années 70 et que le calcul était coûteux, l'ajout d'une petite parenthèse faciliterait probablement l'écriture de l'analyseur.
Machado
4
Re: Lisp: "en fait, les macros". En pratique, IF est une forme spéciale dans Scheme et CL (juste pour être complet).
Coredump
1
@ Leushenko: Et, par exemple, dans MISC, qui est paresseux par défaut, toutes les formes conditionnelles ne sont que des fonctions normales, ni des macros ni des formes spéciales. (En effet, AFAIR, MISC n'a aucune forme spéciale?)
Jörg W Mittag
70

Les parenthèses ne sont nécessaires que si vous utilisez des accolades.

if true ++ x;

Par exemple, devient ambigu sans eux.

Telastyn
la source
28
@ RobertHarvey - Les parenthèses sont obligatoires dans presque toutes les langues que je connais. Certainement C et ses parents. Le PO demande pourquoi ils sont requis - et c'est parce que le langage deviendrait ambigu sinon.
Telastyn
25
Juste au dessus de ma tête, les parenthèses ne sont pas obligatoires ifdans: code de base, assembleur, python, bash / zsh, tcl, batch, brainfuck ou machine. L'absence de parenthèses ne rend ifambigu que si le langage a été conçu pour en dépendre.
candied_orange
12
Je suis étonné que personne ne mentionne la version la plus logique et lisible - en Pascal (y compris Delphi) c'est if Condition then ....
Ulrich Gerhardt
18
Go est un bon exemple de faire le contraire. Il rend les accolades {}obligatoires et ne nécessite donc pas de parens autour de l'expression. Non seulement les parens ne sont pas obligatoires, mais si je me souviens bien, leur ajout causerait une erreur de compilation - ils sont interdits
slebetman
10
@ Eiko Laissez-moi reformuler. L'exemple dans la réponse est syntaxiquement ambigu, même s'il est sémantiquement non ambigu (comme vous l'avez noté). Mais puisque la phase d'analyse a lieu avant l'analyse sémantique, l'analyseur rencontrera l'ambiguïté - et il doit soit deviner sans connaissance, soit échouer. Si (pour une raison quelconque) l’analyseur choisit de ne pas échouer, l’analyseur sémantique fonctionnera avec l’arbre résultant, quel qu’il soit. Je n'ai pas vu de compilateur dans lequel l'analyseur sémantique est disposé à demander à l'analyseur syntaxique de réparer le sous-arbre et de faire un choix différent pour la construction syntaxiquement ambiguë.
Theodoros Chatzigiannakis
21

Les parenthèses dans une ifinstruction 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 ifinstruction sont utilisées pour délimiter l'expression booléenne; c'est-à-dire différencier l'expression booléenne du reste de la ifdéclaration.

Dans une ifinstruction, les parenthèses n'exécutent pas de fonction de regroupement (même si, dans cette ifinstruction, vous pouvez toujours utiliser des parenthèses pour regrouper des expressions arithmétiques. L'ensemble de parenthèses externe sert alors à délimiter toute l'expression booléenne). Les rendre obligatoires simplifie le compilateur, car celui-ci peut compter sur ces parenthèses toujours présentes.

Robert Harvey
la source
Je ne vois pas comment cela simplifie le compilateur. La règle `IF '(' expression ')' statement` n'est pas plus simple que IF primary_expression statement. Notez que ce dernier est également sans ambiguïté.
user58697
@ user58697: Non, seul ce dernier a l'ambiguïté dans laquelle un opérateur postfixé primary_expressionne peut pas être distingué d'un opérateur préfixe dans une expression-statement. Pour copier la réponse de Telastyn, if true ++ x;. En outre, si des instructions vides existent, elles if a & f;peuvent être soit une instruction vide et binaire &à l'intérieur de la condition, soit unaires &au début de l'instruction. Mais quand on fait correspondre les parenthèses, il y a exactement un match pour l'ouverture (
MSalters
@MSalters Un opérateur postfixe n'analyse pas en tant que primaire. Une expression primaire est l' un des IDENTIFIER, CONSTANT, STRING_LITERALet '(' expression ')'.
user58697
@ user58697: Vous semblez avoir une langue en tête. Et il semble y avoir une règle selon laquelle les parenthèses ne sont pas nécessaires si et seulement si les conditions sont un "IDENTIFIANT, CONSTANT ou STRING_LITERAL". Je ne suis pas sûr que cela facilite les choses.
MSalters
16

Comme d'autres l'ont déjà partiellement souligné, cela est dû au fait que les expressions sont également des instructions valides. Dans le cas d'un bloc comportant une seule instruction, vous pouvez supprimer des accolades. Cela signifie que ce qui suit est ambigu:

if true
    +x;

Parce que cela 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 les parenthèses mais ont toujours un marqueur de condition de fin:

si vrai : + x

Cependant, vous avez raison de dire que nous pourrions définir un langage dans lequel les parenthèses ne sont jamais obligatoires: un langage dans lequel une expression n'est pas une instruction valide n'aura pas ce problème.

Malheureusement, cela signifie que des choses comme:

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

ne seraient pas des déclarations valides, il vous faudrait donc introduire une syntaxe étrange pour pouvoir effectuer de telles actions sans créer d'expressions. Un moyen simple de faire cela est de simplement ajouter l'expression par un marqueur tel que [statement]:

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

Maintenant l'ambiguïté disparaît puisqu'il faut écrire:

if true
    [statement] ++x;

Mais comme vous pouvez le constater, je ne vois pas un tel langage être répandu, car placer la parenthèse autour d'une if-condition (ou d'une :fin) est bien mieux que de placer un tel marqueur pour chaque expression.


Remarque : l’utilisation d’un [statement]marqueur est la syntaxe la plus simple à laquelle je puisse penser. Cependant, vous pouvez avoir deux syntaxes complètement distinctes pour les expressions et les déclarations sans aucune ambiguïté et ne nécessitant pas un tel marqueur. Le problème est le suivant: le langage serait extrêmement étrange, car pour faire la même chose dans une expression ou dans une déclaration, il faudrait utiliser une syntaxe complètement différente.

Par exemple, force est de penser que les instructions doivent obligatoirement utiliser des symboles unicode (au lieu d’ forutiliser une variante des lettres unicode f, oet r), les expressions devant être ASCII seulement.

Bakuriu
la source
2
Il existe actuellement un langage qui utilise un tel marqueur d'instruction d'expression: si vous souhaitez évaluer une expression pour ses effets secondaires, vous devez explicitement définir discardsa valeur dans Nim . Cependant, cela n'est fait que pour des raisons de sécurité, pas pour des raisons syntaxiques.
amon
@amon Nice, je n'en savais rien. Quoi qu'il en soit, comme je l'ai dit, le marqueur n'est pas vraiment nécessaire, c'est simplement un moyen simple de parvenir à cette distinction sans inventer des syntaxes non intuitives.
Bakuriu
1
@amon - de nombreuses variantes BASIC ont également une séparation stricte entre les expressions et les déclarations. Les expressions ne sont autorisées qu'aux endroits où la valeur sera réellement utilisée (par exemple, des affectations de variables, des instructions telles que PRINT qui effectuent une action, etc.). Les procédures qui ne sont pas utilisées pour calculer une valeur sont appelées par un mot clé (généralement "CALL", bien qu'au moins une variante à ma connaissance utilise "PROC") qui préfixe leur nom. Etc. BASIC délimite généralement la fin de l'expression dans une instruction IF avec "THEN", mais je ne vois aucune raison technique pour laquelle cette exigence n'a pas pu être supprimée.
Periata Breatta
1
Il existe un langage très populaire dans les années 80 qui a des blocs sans parenthèses, sans accolades, points-virgules ou autre marqueur et accepte les expressions comme des déclarations partout. Certaines déclarations agissent comme des expressions (opérateurs composés comme + = et ++). C’est mauvais, il ya un pré-processeur idiot avant le compilateur ( ?simbol par exemple est une fonction après PP). Il n'y a pas ;. Bien sûr, il faut un marqueur pour la ligne suivante, mais cela est déconseillé. harbour.github.io/doc/clc53.html#if-cmd . Le compilateur est rapide et simple (créé avec Bison / Flex).
Maniero
@bigown Ils y parviennent en utilisant une syntaxe distincte pour les conditions logiques. Ainsi, les conditions pour if, whileecc sont limitées par rapport aux expressions génériques utilisées dans d'autres langues. Bien sûr: si vous avez plus de deux catégories syntaxiques (comme une déclaration, une expression, une expression logique, une expression de café, ...), vous pouvez échanger un peu de liberté.
Bakuriu 25/02/2017
10

Il est courant que les langues de la famille C exigent ces parenthèses, mais elles ne sont pas universelles.

L' un des changements syntaxiques plus notables de Perl 6 est qu'ils ont modifié la grammaire afin que vous ne devez pas donner les parenthèses autour if, foret les conditions de déclarations similaires. Donc, quelque chose comme ceci 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. Dans ce cas, il s’agit simplement de grouper des expressions ordinaires au lieu d’une partie obligatoire de la syntaxe, telles qu’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 de telles choses et de s'interroger sur leur suppression.

Matthew Walton
la source
Bien que votre réponse donne un aperçu des autres langues, elle ne répond pas "Pourquoi cette syntaxe étrange existe-t-elle et pourquoi est-elle si courante?"
Vous avez tout à fait raison. Je cherchais vraiment à ajouter un contexte à la question, ce qui n’est pas chose facile à faire sur cette plate-forme. Je suppose qu'en fin de compte, si je réponds à la question, c'est "simplement parce que, comme il n'y a aucune raison technique, la grammaire n'aurait pas pu s'adapter à son absence". Mais ce n'est pas une réponse très utile.
Matthew Walton
En Perl 5, il y a les deux. Pour une construction normale if ou en boucle avec BLOCK, les parens sont requis, par exemple, dans if ( $x == 4 ) { ... }ou foreach my $foo ( @bar ) { ... }. Lorsque la notation postfixée est utilisée, les parenthèses sont facultatives, comme dans return unless $foo;ou ++$x while s/foo/bar/g;.
simbabque
6

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

C, et de nombreux dérivés de C et ressemblances, présente une particularité en ce que la valeur d'une affectation est la valeur assignée. Une conséquence de ceci est qu'une affectation peut être utilisée lorsqu'une valeur 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++; }

(ce qui est implicitement traité comme while (n < m && *p1++ = *p2++ != 0) { n++; }parce que C considère que non-zéro est vrai; à propos, 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 valables ne sont pas nécessairement utiles (et les compilateurs modernes avertissent spécifiquement des assignations dans les conditions conditionnelles, parce que c'est une erreur courante), mais certaines d'entre elles sont réellement utiles.

L'analyse de telles déclarations serait probablement beaucoup plus difficile s'il n'existait pas de moyen non ambigu de déterminer le début et la fin de l'expression conditionnelle.

Les parenthèses étaient déjà utilisées pour délimiter les noms de fonction des arguments de fonction, alors je suppose qu’elles semblaient être un choix naturel, ainsi que de délimiter les mots-clés à partir des arguments de mots-clés.

Bien sûr, d'autres syntaxes pourraient être définies pour faire la même chose. Mais cela augmenterait la complexité, en particulier dans l’analyseur qui devrait alors traiter deux ensembles différents de syntaxe pour la même chose. À l'époque de la conception de C, la puissance de calcul (à la fois en termes de capacité de calcul, de mémoire de travail et de capacité de stockage) était extrêmement limitée; tout ce qui réduisait la complexité à un coût faible ou nul en 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 si on donnait une connaissance familière du langage, cela nuirait à la lisibilité par rapport à une autre syntaxe capable d'exprimer les mêmes choses.

un CVn
la source
5

La raison est principalement l'histoire.

Au moment de la rédaction du premier compilateur C, les ordinateurs ne disposaient que d’un nombre très limité de RAM, de CPU et de compilateurs. 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é effectuée.

Dans les langages 'c like', conditionals ( if, while, etc) ne nécessitent pas blockde code off explicite , vous pouvez simplement utiliser une instruction simple.

if (a == d) doIt()

ou vous pouvez combiner des déclarations dans un compound statementen les mettant dans{}

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

Ian
la source
3

Java et C ++ ont tous deux été développés après que C soit devenu un langage de programmation très populaire. Lors de la conception de chacun de ces langages, il a été pris en compte qu’il serait intéressant pour les programmeurs C et de les encourager à utiliser le nouveau langage. (J'étais l'un des programmeurs C qu'ils ont réussi à séduire.) De plus, C ++ a été conçu pour être (presque) interchangeable avec le code C. Afin de soutenir ces objectifs, les deux C ++ et Java ont adopté une grande partie de la syntaxe de C, y compris les parenthèses autour des conditions de if, whileet des switchdéclarations.

D'où la raison pour laquelle toutes ces langues nécessitent des parenthèses autour des conditions de ces déclarations, c'est parce que C le fait, et la question est vraiment de savoir pourquoi C a besoin de 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 en être l' auteur principal). Comme indiqué dans cet article, C a été développé au début des années 1970 en tant que langage de programmation système pour ordinateurs avec un espace extrêmement limité dans la mémoire principale. Il était souhaitable de disposer d'un langage de niveau supérieur au langage d'assemblage, mais compte tenu des ressources disponibles, la facilité d'analyse du langage était également importante. Exiger les parenthèses rendrait l'identification du code conditionnel relativement facile.

On pourrait également en déduire que la capacité d'écrire des programmes en utilisant moins de caractères était considérée comme un avantage et que deux parenthèses prennent moins de place que le mot clé THENutilisé à l'époque dans FORTRAN et d'autres langages de haut niveau; En fait, puisque les parenthèses pouvaient également remplacer les espaces comme délimiteurs de symboles, if(a==b)quatre caractères entiers étaient-ils plus courts 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, écrire et comprendre les programmes écrits en C, la facilité avec laquelle un compilateur pourrait 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 entre parenthèses autour des conditions de if, while, et des switch déclarations était comment les gens ont choisi de trouver cet équilibre dans la conception de C.

Comme en témoignent plusieurs autres réponses, une fois les circonstances particulières dans lesquelles C a été développé, toutes sortes de formes de syntaxe alternatives ont été utilisées pour les conditionnelles de différents langages de programmation. Donc, entre parenthèses, il ne s’agit en réalité que d’une décision de conception prise par quelques personnes sous certaines contraintes à une certaine époque de l’histoire.

David K
la source
Je ne suis pas sûr qu'il soit juste de dire que le C ++ a été conçu de la manière "pour inciter ces programmeurs à utiliser un nouveau langage". Tu te souviens de C avec les cours ?
un CVn
@ MichaelKjörling Certes, les développeurs Java ont été beaucoup plus explicites à propos du "wooing". Mais notons que l'article cité cite, comme l'une des raisons pour lesquelles Stroustrup a choisi de commencer par C comme base de son langage, que C était largement utilisé. Une des raisons pour lesquelles cela a motivé à rester proche de C était que le code existant pouvait facilement être adapté (comme je l’ai déjà dit) - mais les codeurs existants pouvaient également s’adapter facilement.
David K
@ MichaelKjörling Je suppose que le libellé original de ma réponse suggérait que le "wooing" était un facteur plus important dans la conception du langage qu'il ne l'était réellement. J'ai modifié la réponse pour tenter de préciser que c'était simplement une chose qui était prise en compte dans la conception du langage.
David K
3

Beaucoup de gens ici pensent que sans les parenthèses, la syntaxe serait ambiguë et impliquerait en silence qu'il s'agirait d'une situation mauvaise voire impossible.

En fait, les langues ont de nombreux moyens de gérer les ambiguïtés. La priorité des opérateurs n’est qu’un exemple de ce sujet.

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 n’exige pas de parenthèses autour de la condition (les rendant ainsi facultative) et qui crée toujours un code valide dans tous les cas. L’exemple de if a ++ b;pourrait être interprété comme équivalent à if (a) ++b;ou à if (a++) b;tout ce qui semble plus approprié.

La question de savoir pourquoi Dennis Ritchie a choisi de rendre le () obligatoire (et donc de forger ce meme pour de nombreuses langues dérivées) est plutôt linguistique. J'imagine que l'idée d'indiquer clairement que la condition est une expression plutôt qu'un ordre était le père de la pensée.

Et en fait, C a été conçu pour être analysable à l’aide d’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.

Alfe
la source
Je vois un vote négatif sur ma réponse. S'il vous plaît soyez si gentil d'expliquer dans un commentaire ce que vous n'avez pas aimé à ce sujet. Peut-être que je peux l’améliorer alors.
Alfe
0

Les parenthèses entourant les ifconditions ne sont pas obligatoires en Fortran, Cobol, PL / 1, Algol, Algo-68, Pascal, Modula, XPL, PL / M, MPL, ... ou dans toute autre langue comportant un thenmot clé. thensert à délimiter le conditionde ce qui suit statement.

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 analysées de manière traditionnelle.

utilisateur207421
la source
Fortran ne nécessitent entre parenthèses dans toutes les versions de son SI, dont une structure.
Netch