Pourquoi la plupart des langues traditionnelles ne prennent-elles pas en charge la syntaxe «x <y <z» pour les comparaisons booléennes à 3 voies?

34

Si je veux comparer deux nombres (ou d'autres entités bien ordonnées), je le ferais avec x < y. Si je veux comparer trois d'entre eux, l'étudiant en algèbre du secondaire suggérera d'essayer x < y < z. Le programmeur en moi répondra alors par "non, ce n'est pas valide, vous devez le faire x < y && y < z".

La plupart des langues que j'ai rencontrées ne semblent pas supporter cette syntaxe, ce qui est étrange compte tenu de sa fréquence en mathématiques. Python est une exception notable. JavaScript ressemble à une exception, mais il ne s'agit en réalité que d'un sous-produit regrettable de la priorité des opérateurs et des conversions implicites; dans node.js, 1 < 3 < 2évalue true, car c’est vraiment (1 < 3) < 2 === true < 2 === 1 < 2.

Ma question est donc la suivante: pourquoi n’est-il x < y < zpas couramment disponible dans les langages de programmation, avec la sémantique attendue?

JesseTG
la source
1
Voici le fichier de grammaire, qu'ils gardent bien dans la documentation Python - je ne pense pas que ce soit si difficile: docs.python.org/reference/grammar.html
Aaron Hall
Je ne connais pas d'autres langues aussi bien que Python, mais je peux parler de la simplicité de son interprétation par Python. Je devrais peut-être répondre. Mais je ne suis pas d'accord avec la conclusion de gnasher729 selon laquelle il ferait des dégâts.
Aaron Hall
@ErikEidt - La demande consiste à pouvoir écrire des expressions mathématiques de la manière dont nous avons été enseignés au lycée (ou plus tôt). Tous ceux qui ont une inclination mathématique savent ce que signifie $ a <b <c <d $. Ce n’est pas parce qu’une fonctionnalité existe que vous devez l’utiliser. Ceux qui ne l'aiment pas peuvent toujours créer une règle personnelle ou une règle de projet interdisant son utilisation.
David Hammen
2
Je pense que l’essentiel est que c’est mieux pour l’équipe C # (à titre d’exemple) d’explorer LINQ et, à l’avenir, peut-être des types d’enregistrement et des correspondances de motifs plutôt que d’ajouter du sucre syntaxique qui permettrait d’économiser 4 frappes de touche et pas vraiment ajoutez n'importe quelle expressivité (vous pouvez aussi écrire des méthodes d'aide comme static bool IsInRange<T>(this T candidate, T lower, T upper) where T : IComparable<T>si cela vous dérangeait de voir ça &&)
sara
1
SQL est assez "mainstream" et vous pouvez écrire "x entre 1 et 10"
JoelFan

Réponses:

30

Ce sont des opérateurs binaires qui, lorsqu'ils sont chaînés, produisent normalement et naturellement un arbre de syntaxe abstrait comme:

Arbre de syntaxe abstraite normale pour les opérateurs binaires

Lors de l'évaluation (ce que vous faites depuis le début), cela produit un résultat booléen x < y, vous obtenez alors une erreur de type en essayant de le faire boolean < z. Afin x < y < zde fonctionner comme vous avez discuté, vous devez créer un cas spécial dans le compilateur pour produire un arbre de syntaxe tel que:

Arbre de syntaxe de cas particulier

Non pas que ce ne soit pas possible. C'est évidemment le cas, mais cela ajoute une certaine complexité à l'analyseur pour une affaire qui n'arrive pas souvent. En gros, vous créez un symbole qui agit parfois comme un opérateur binaire et parfois efficacement, comme un opérateur ternaire, avec toutes les implications du traitement des erreurs et autres. Cela laisse beaucoup de place aux problèmes que les concepteurs de langage préfèrent éviter si possible.

Karl Bielefeldt
la source
1
"alors vous obtenez une erreur de type en essayant de faire booléen <z" - pas si le compilateur permet l'enchaînement en évaluant y sur place pour la comparaison z. "Cela ajoute beaucoup d'espace aux problèmes que les concepteurs de langages préfèrent éviter si possible." En fait, Python n’a aucun problème à faire cela, et la logique d’analyse se limite à une seule fonction: hg.python.org/cpython/file/tip/Python/ast.c#l1122 - pas beaucoup d’espace pour les choses à faire faux. "agit parfois comme un opérateur binaire et parfois efficacement comme un opérateur trinaire", en python, toute la chaîne de comparaison est ternaire.
Aaron Hall
2
Je n'ai jamais dit que ce n'était pas faisable, juste un travail supplémentaire avec une complexité supplémentaire. Les autres langues ne doivent pas écrire de code séparé uniquement pour gérer leurs opérateurs de comparaison. Vous l'obtenez gratuitement avec d'autres opérateurs binaires. Vous devez juste spécifier leur priorité.
Karl Bielefeldt
Oui, mais ... il y a déjà un opérateur Ternary disponible dans de nombreuses langues?
JensG
1
@JensG La dénotation ternaire signifie qu'il faut 3 arguments. Dans le contexte de votre lien, il s'agit d'un opérateur de condition ternaire. Apparemment "trinaire" dans un terme inventé pour un opérateur qui semble prendre 2 mais en prend 3. Mon principal problème avec cette réponse est qu’il s’agit principalement de FUD.
Aaron Hall
2
Je suis l'un des votants sur cette réponse acceptée. (@JesseTG: Veuillez ne pas accepter cette réponse.) Cette question confond ce que x<y<zsignifie, ou plus important encore x<y<=z. Cette réponse s'interprète x<y<zcomme un opérateur trinaire. C’est exactement ainsi que cette expression mathématique bien définie ne doit pas être interprétée. x<y<zest plutôt un raccourci pour (x<y)&&(y<z). Les comparaisons individuelles sont encore binaires.
David Hammen
37

Pourquoi n'est-il x < y < zpas couramment disponible dans les langages de programmation?

Dans cette réponse, je conclus que

  • bien que cette construction soit triviale à implémenter dans la grammaire d'une langue et crée de la valeur pour les utilisateurs de la langue,
  • les principales raisons pour lesquelles cela n'existe pas dans la plupart des langues sont dues à son importance par rapport à d'autres caractéristiques et à la réticence des organes directeurs des langues à
    • déranger les utilisateurs avec des changements potentiellement décisifs
    • pour mettre en œuvre la fonctionnalité (par exemple: la paresse).

introduction

Je peux parler du point de vue d'un pythoniste sur cette question. Je suis un utilisateur d'une langue avec cette fonctionnalité et j'aime bien étudier les détails de la mise en œuvre de la langue. Au-delà de cela, je connais un peu le processus de changement de langages tels que C et C ++ (la norme ISO est régie par un comité et mis à jour par année.) Et j'ai vu à la fois Ruby et Python mettre en œuvre des modifications radicales.

Documentation et implémentation de Python

Dans la documentation / grammaire, nous voyons que nous pouvons chaîner un nombre quelconque d'expressions avec des opérateurs de comparaison:

comparison    ::=  or_expr ( comp_operator or_expr )*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

et la documentation indique en outre:

Les comparaisons peuvent être chaînées de façon arbitraire, par exemple, x <y <= z est équivalent à x <y et y <= z, sauf que y est évalué une seule fois (mais dans les deux cas, z n'est pas du tout évalué lorsque x <y est trouvé être faux).

Equivalence Logique

Alors

result = (x < y <= z)

est logiquement équivalent en termes d’évaluation de x, yet z, à l’exception d’une yévaluation deux fois:

x_lessthan_y = (x < y)
if x_lessthan_y:       # z is evaluated contingent on x < y being True
    y_lessthan_z = (y <= z)
    result = y_lessthan_z
else:
    result = x_lessthan_y

Là encore, la différence est que y n’est évalué qu’une fois avec (x < y <= z).

(Notez que les parenthèses sont complètement inutiles et redondantes, mais je les ai utilisées au profit de celles venant d'autres langues, et le code ci-dessus est assez légal en Python.)

Inspection de l'arbre de syntaxe abstraite analysé

Nous pouvons vérifier comment Python analyse les opérateurs de comparaison chaînés:

>>> import ast
>>> node_obj = ast.parse('"foo" < "bar" <= "baz"')
>>> ast.dump(node_obj)
"Module(body=[Expr(value=Compare(left=Str(s='foo'), ops=[Lt(), LtE()],
 comparators=[Str(s='bar'), Str(s='baz')]))])"

Nous pouvons donc voir que ce n'est vraiment pas difficile à analyser pour Python ou tout autre langage.

>>> ast.dump(node_obj, annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE()], [Str('bar'), Str('baz')]))])"
>>> ast.dump(ast.parse("'foo' < 'bar' <= 'baz' >= 'quux'"), annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE(), GtE()], [Str('bar'), Str('baz'), Str('quux')]))])"

Et contrairement à la réponse actuellement acceptée, l’opération ternaire est une opération de comparaison générique, qui prend la première expression, une itérable de comparaisons spécifiques et une itérable de noeuds d’expression à évaluer si nécessaire. Simple.

Conclusion sur Python

Personnellement, je trouve la sémantique de la gamme assez élégante et la plupart des professionnels de Python que je connais encourageraient plutôt l’utilisation de cette fonctionnalité, au lieu de la considérer comme préjudiciable - la sémantique est clairement énoncée dans la documentation réputée (comme indiqué ci-dessus).

Notez que le code est lu beaucoup plus qu'il n'est écrit. Les modifications qui améliorent la lisibilité du code doivent être acceptées et non ignorées en soulevant des spectres génériques de peur, d’incertitude et de doute .

Alors, pourquoi x <y <z n'est-il pas couramment disponible dans les langages de programmation?

Je pense qu'il y a une convergence de raisons qui sont centrées sur l'importance relative de la fonctionnalité et la relative impulsion / inertie de changement permise par les gouverneurs des langues.

Des questions similaires peuvent être posées sur d'autres fonctionnalités linguistiques plus importantes

Pourquoi l'héritage multiple n'est-il pas disponible en Java ou en C #? Il n'y a pas de bonne réponse ici à l'une ou l'autre question . Les développeurs étaient peut-être trop paresseux, comme le prétend Bob Martin, et les raisons données ne sont que des excuses. Et l'héritage multiple est un sujet assez important en informatique. C'est certainement plus important que le chaînage des opérateurs.

Des solutions simples existent

Le chaînage des opérateurs de comparaison est élégant, mais nullement aussi important que l'héritage multiple. Et tout comme Java et C # ont des interfaces comme solution de contournement, il en va de même pour chaque langage permettant des comparaisons multiples - vous chaînez simplement les comparaisons avec des booléens "et" s, ce qui fonctionne assez facilement.

La plupart des langues sont gouvernées par un comité

La plupart des langues évoluent par comité (plutôt que d’avoir un dictateur bienveillant pour la vie, comme celui de Python). Et je suppose que cette question n’a tout simplement pas été suffisamment appuyée pour pouvoir en sortir de ses comités respectifs.

Les langues qui n'offrent pas cette fonctionnalité peuvent-elles changer?

Si une langue le permet x < y < zsans la sémantique mathématique attendue, cela constituerait un changement radical. Si cela ne le permettait pas en premier lieu, il serait presque trivial d'ajouter.

Briser les changements

En ce qui concerne les langues avec les changements les plus récents: nous mettons à jour les langages avec les changements de comportement, mais les utilisateurs ont tendance à ne pas aimer cela, en particulier les utilisateurs de fonctionnalités qui peuvent être cassées. Si un utilisateur se fie au comportement précédent de x < y < z, il protestera probablement fort. Et comme la plupart des langues sont gouvernées par un comité, je doute que nous aurions beaucoup de volonté politique pour soutenir un tel changement.

Aaron Hall
la source
Honnêtement, je ne conteste pas la sémantique fournies par langues que les opérations de comparaison de chaîne tels que `x <y <z` mais il est trivial pour un développeur de carte mentalement x < y < zà (x < y) && (y < z). En choisissant des lentes, le modèle mental pour la comparaison chaînée est le calcul général. La comparaison classique n’est pas la mathématique en général, mais la logique booléenne. x < yproduit une réponse binaire {0}. y < zproduit une réponse binaire {1}. {0} && {1}produit la réponse descriptive. La logique est composée, pas naïvement chaînée.
K. Alan Bates
Pour mieux communiquer, j'ai préfacé la réponse par une seule phrase qui résume directement le contenu entier. C'est une longue phrase, alors je l'ai divisée en balles.
Aaron Hall
2
La principale raison pour laquelle peu de langages implémentent cette fonctionnalité est qu’avant Guido, personne n’y avait pensé. Les langages hérités du C ne peuvent pas obtenir ce "bon" (mathématiquement correct) maintenant principalement parce que les développeurs du C l'ont mal compris (mathématiquement faux) il y a plus de 40 ans. Il y a beaucoup de code qui dépend de la nature contre-intuitive de la façon dont ces langues interprètent x<y<z. Une langue a déjà eu la chance d’obtenir quelque chose de semblable, et une chance est au début de la langue.
David Hammen
1
@ K.AlanBates Vous faites 2 points: 1) l'enchaînement d'opérateurs est bâclé et 2) que le sucre syntaxique n'a pas de valeur. Au premier point: j'ai démontré que le chaînage d'opérateurs est déterministe à 100%, n'est-ce pas? Certains programmeurs sont peut-être trop paresseux mentalement pour développer leur capacité à comprendre le concept? Au deuxième point: il me semble que vous argumentez directement contre la lisibilité? Le sucre syntaxique n'est-il pas généralement considéré comme une bonne chose lorsqu'il améliore la lisibilité? S'il est normal de penser ainsi, pourquoi un programmeur ne voudrait-il pas communiquer de la sorte? Le code doit être écrit pour être lu, non?
Aaron Hall
2
I have watched both Ruby and Python implement breaking changes.Pour ceux qui sont curieux, voici un changement radical en C # 5.0 impliquant des variables de boucle et des fermetures: blogs.msdn.microsoft.com/ericlippert/2009/11/12/…
user2023861 Le
13

Les langages informatiques tentent de définir les unités les plus petites possibles et vous permettent de les combiner. La plus petite unité possible serait quelque chose comme "x <y" qui donne un résultat booléen.

Vous pouvez demander un opérateur ternaire. Un exemple serait x <y <z. Maintenant, quelles combinaisons d'opérateurs permettons-nous? Évidemment, x> y> z ou x> = y> = z ou x> y> = z ou peut-être que x == y == z devrait être autorisé. Qu'en est-il de x <y> z? x! = y! = z? Que signifie le dernier, x! = Y et y! = Z ou que tous les trois sont différents?

Maintenant, promotion des arguments: en C ou C ++, les arguments seraient promus en un type commun. Alors qu'est-ce que x <y <z signifie sur x est double, mais y et z sont long long int? Tous trois promus de doubler? Ou y est pris double une fois et aussi longtemps l'autre fois? Que se passe-t-il si en C ++ l'un des opérateurs ou les deux sont surchargés?

Et enfin, permettez-vous un nombre quelconque d'opérandes? Comme un <b> c <d> e <f> g?

Eh bien, tout devient très compliqué. Maintenant, ce qui ne me dérangerait pas, c’est que x <y <z produise une erreur de syntaxe. Parce que son utilité est faible comparée aux dégâts causés aux débutants qui ne peuvent pas comprendre ce que fait x <y <z.

gnasher729
la source
4
En bref, c’est une fonction difficile à concevoir.
Jon Purdy
2
Ce n'est pas vraiment une raison pour expliquer pourquoi aucun langage bien connu ne contient cette fonctionnalité. En fait, il est assez facile de l'inclure dans une langue de manière bien définie. Il suffit de la visualiser sous forme de liste connectée par des opérateurs de type similaire, au lieu que chaque opérateur soit binaire. La même chose peut être faite pour les sommes x + y + z, avec la seule différence que cela n'implique aucune différence sémantique. C'est donc qu'aucune langue bien connue ne s'est jamais souciée de le faire.
cmaster
1
Je pense qu’en Python, c’est un peu une optimisation ( x < y < zêtre équivalent à, ((x < y) and (y < z))mais avec yseulement évalué une fois ) que j’imagine que les langages compilés optimisent leur cheminement. "Parce que son utilité est faible comparée aux dommages causés aux débutants qui ne peuvent pas comprendre ce que fait x <y <z." Je pense que c'est incroyablement utile. Je vais sans doute -1 pour ça ...
Aaron Hall
Si son objectif est de concevoir un langage qui élimine tout ce qui peut semer la confusion chez les programmeurs les plus fous, un tel langage existe déjà: COBOL. Je préférerais utiliser python, moi-même, où l'on peut effectivement écrire a < b > c < d > e < f > g, avec le sens "évident" (a < b) and (b > c) and (c < d) and (d > e) and (e < f) and (f > g). Ce n’est pas parce que vous pouvez écrire que vous devriez. L'élimination de ces monstruosités relève de la révision du code. D'autre part, l'écriture 0 < x < 8en python a l'évidence (pas de guillemets effrayants), ce qui signifie que x est compris entre 0 et 8, exclusif.
David Hammen
@DavidHammen, ironiquement, COBOL permet en effet un <b <c
JoelFan
10

Dans de nombreux langages de programmation, x < yune expression binaire accepte deux opérandes et donne un résultat booléen unique. Par conséquent, si enchaînant plusieurs expressions, true < zet false < zne sera pas de sens, et si ces expressions sont avec succès, ils sont susceptibles de produire un résultat erroné.

Il est beaucoup plus facile de penser x < yà un appel de fonction qui prend deux paramètres et produit un seul résultat booléen. En fait, c'est le nombre de langues implémentées sous le capot. C'est composable, facilement compilable, et ça marche.

Le x < y < zscénario est beaucoup plus compliqué. Maintenant , le compilateur, en effet, a à la mode trois fonctions: x < y, y < zet le résultat de ces deux valeurs ANDED ensemble, le tout dans le cadre d'une sans doute la grammaire du langage ambigu .

Pourquoi l'ont-ils fait dans l'autre sens? Parce que c'est une grammaire non ambiguë, beaucoup plus facile à mettre en œuvre et beaucoup plus facile à obtenir.

Robert Harvey
la source
2
Si vous concevez la langue, vous avez la possibilité d’en faire le bon résultat.
JesseTG
2
Bien sûr, cela répond à la question. Si la question est vraiment de savoir pourquoi , la réponse est "parce que c'est ce que les concepteurs de langage ont choisi". Si vous pouvez trouver une meilleure réponse que cela, allez-y. Notez que Gnasher a essentiellement dit exactement la même chose dans le premier paragraphe de sa réponse .
Robert Harvey
3
Encore une fois, vous coupez les cheveux en quatre. Les programmeurs ont tendance à le faire. "Voulez-vous sortir les poubelles?" "Non." "Voulez-vous sortir la poubelle?" "Oui."
Robert Harvey
2
Je conteste également le dernier paragraphe. Python prend en charge les comparaisons de chaînes et son analyseur est LL (1). Il n’est pas forcément difficile de définir et d’implémenter la sémantique: Python dit simplement que cela e1 op1 e2 op2 e3 op3 ...équivaut à, e1 op e2 and e2 op2 e3 and ...sauf que chaque expression n’est évaluée qu’une seule fois. (BTW cette règle simple a pour effet secondaire déroutant que des déclarations comme a == b is Truen'ont plus l'effet
2
@RobertHarvey re:answerC’est là que j’ai immédiatement réfléchi pour commenter la question principale. Je ne considère pas le support pour x < y < zajouter une valeur spécifique à la sémantique de la langue. (x < y) && (y < z)est plus largement pris en charge, est plus explicite, plus expressif, plus facilement digéré en ses constituants, plus composable, plus logique, plus facilement refactorisé.
K. Alan Bates
6

La plupart des langues traditionnelles sont (au moins partiellement) orientées objet. Fondamentalement, le principe sous-jacent à OO est que les objets envoient des messages à d'autres objets (ou eux-mêmes), et le destinataire de ce message a le contrôle complet sur la manière de répondre à ce message.

Voyons maintenant comment nous pourrions mettre en œuvre quelque chose comme

a < b < c

On pourrait l'évaluer strictement de gauche à droite (associative de gauche):

a.__lt__(b).__lt__(c)

Mais maintenant, nous appelons __lt__le résultat de a.__lt__(b), qui est un Boolean. Ça n'a aucun sens.

Essayons droit-associatif:

a.__lt__(b.__lt__(c))

Non, ça n'a pas de sens non plus. Maintenant, nous avons a < (something that's a Boolean).

Ok, pourquoi ne pas le traiter comme un sucre syntaxique? Faisons une chaîne de n <comparaisons envoyer un message n-1-aire. Cela pourrait signifier, nous envoyons le message __lt__à a, en passant bet ccomme arguments:

a.__lt__(b, c)

Ok, ça marche, mais il y a une étrange asymétrie ici: il afaut décider si c'est moins que b. Mais bne décide pas s’il est inférieur à c, mais cette décision est également prise par a.

Qu'en est-il de l'interpréter comme un message n-aire envoyé à this?

this.__lt__(a, b, c)

Finalement! Cela peut marcher Cela signifie toutefois que l'ordre des objets n'est plus une propriété de l'objet (par exemple, si elle aest inférieure à ce qui bn'est pas une propriété de aou de b), mais plutôt une propriété du contexte (c'est-à-dire this).

D'un point de vue général, cela semble étrange. Cependant, par exemple à Haskell, c'est normal. Il peut y avoir plusieurs implémentations différentes de la Ordclasse de types, par exemple, et le fait de savoir si la valeur aest inférieure à bdépend de l'instance de la classe de types qui se trouve dans la portée.

Mais en réalité, ce n'est pas si étrange du tout! Java ( Comparator) et .NET ( IComparer) ont des interfaces qui vous permettent d’injecter votre propre relation de commande dans des algorithmes de tri, par exemple. Ainsi, ils reconnaissent pleinement qu'un ordre n'est pas quelque chose qui est fixé à un type mais dépend du contexte.

Autant que je sache, il n'y a actuellement aucune langue qui effectue une telle traduction. Il existe toutefois un précédent: Ioke et Seph ont tous deux ce que leur concepteur appelle des "opérateurs trinaires" - des opérateurs syntaxiquement binaires, mais sémantiquement ternaires. En particulier,

a = b

n'est pas interprété comme envoyant le message =à apasser bcomme argument, mais plutôt comme envoyant le message =au "sol actuel" (un concept similaire mais pas identique à this) passant aet bcomme arguments. Donc, a = best interprété comme

=(a, b)

et pas

a =(b)

Cela pourrait facilement être généralisé aux opérateurs n-aires.

Notez que ceci est vraiment particulier aux langages OO. Dans OO, nous avons toujours un seul objet qui est en dernier ressort responsable de l'interprétation d'un message envoyé et, comme nous l'avons vu, il n'est pas immédiatement évident de déterminer a < b < cquel objet doit être.

Cela ne s'applique cependant pas aux langages procéduraux ou fonctionnels. Par exemple, dans Scheme , Common Lisp et Clojure , la <fonction est n-aire et peut être appelée avec un nombre arbitraire d'arguments.

En particulier, <ne signifie pas "inférieur à", mais ces fonctions sont interprétées légèrement différemment:

(<  a b c d) ; the sequence a, b, c, d is monotonically increasing
(>  a b c d) ; the sequence a, b, c, d is monotonically decreasing
(<= a b c d) ; the sequence a, b, c, d is monotonically non-decreasing
(>= a b c d) ; the sequence a, b, c, d is monotonically non-increasing
Jörg W Mittag
la source
3

C'est simplement parce que les concepteurs de langage n'y ont pas pensé ou n'ont pas pensé que c'était une bonne idée. Python le fait comme vous l'avez décrit avec une grammaire simple (presque) LL (1).

Neil G
la source
1
Cela va encore analyser une grammaire normale dans à peu près n'importe quelle langue traditionnelle; cela ne sera tout simplement pas compris correctement pour la raison donnée par @RobertHarvey.
Mason Wheeler
@MasonWheeler Non, pas nécessairement. Si les règles sont écrites de manière à ce que les comparaisons soient interchangeables avec d'autres opérateurs (par exemple, parce qu'ils ont la même priorité), vous n'obtiendrez pas le bon comportement. Le fait que Python mette toutes les comparaisons à un niveau est ce qui lui permet de traiter ensuite la séquence comme une conjonction.
Neil G
1
Pas vraiment. Mettez 1 < 2 < 3en Java ou en C # et vous n’avez pas de problème avec la priorité des opérateurs; vous avez un problème avec les types non valides. Le problème est que cela va toujours analyser exactement comme vous l'avez écrit, mais vous avez besoin d'une logique de casse spéciale dans le compilateur pour passer d'une séquence de comparaisons individuelles à une comparaison chaînée.
Mason Wheeler
2
@MasonWheeler Ce que je dis, c'est que le langage doit être conçu pour que cela fonctionne. Une partie de cela consiste à bien comprendre la grammaire. (Si les règles sont écrites de manière à ce que les comparaisons soient interchangeables avec d'autres opérateurs, par exemple, parce qu'elles ont la même priorité, vous n'obtiendrez pas le comportement correct.) Une autre partie de cela consiste à interpréter l'AST comme une conjonction, à laquelle C ++ ne fait pas. Le point principal de ma réponse est que c'est une décision du concepteur de langage.
Neil G
@MasonWheeler Je pense que nous sommes d'accord. Je soulignais simplement qu'il n'est pas difficile d'obtenir la grammaire correcte pour cela. C'est juste une question de décider à l'avance que vous voulez que cela fonctionne de cette façon.
Neil G
2

Le programme C ++ suivant compile avec nary un aperçu de clang, même avec des avertissements définis au niveau le plus élevé possible ( -Weverything):

#include <iostream>
int main () { std::cout << (1 < 3 < 2) << '\n'; }

La suite du compilateur gnu, par contre, me le dit gentiment comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses].

Ma question est donc la suivante: pourquoi x <y <z n’est-il pas couramment disponible dans les langages de programmation, avec la sémantique attendue?

La réponse est simple: compatibilité ascendante. Il y a une grande quantité de code dans la nature qui utilise l'équivalent de 1<3<2et s'attend à ce que le résultat soit vrai.

Un concepteur de langage n'a qu'une seule chance d'obtenir ce "bon", et c'est à ce moment précis que le langage est conçu pour la première fois. Le faire "mal" signifie initialement que les autres programmeurs vont rapidement tirer parti de ce comportement "mauvais". Le fait de "bien" la deuxième fois rompra cette base de code existante.

David Hammen
la source
+1 car ce programme génère "1" à la suite d'une expression évidemment fausse en mathématiques. Bien que ce soit artificiel, un exemple du monde réel avec des noms de variables incompréhensibles deviendrait un cauchemar de débogage si cette fonctionnalité de langage était ajoutée.
Seth Battin
@SethBattin - Ce n'est pas un cauchemar de débogage en Python. if x == y is True : ...Mon seul problème en Python est , à mon avis: les personnes qui écrivent ce type de code méritent d’être soumises à une forme de torture extra-spéciale et extraordinaire qui (s’il était en vie maintenant) provoquerait l’évanouissement de Torquemada.
David Hammen