ほとんどの主流言語が、3者間ブール比較の「x <y <z」構文をサポートしないのはなぜですか?


34

2つの数値(または他の適切に配列されたエンティティ)を比較したい場合は、で比較しx < yます。それらのうち3つを比較したい場合は、高校の代数の生徒が試してみることをお勧めしx < y < zます。私のプログラマーは、「いいえ、それは無効です、あなたはしなければなりませんx < y && y < z」と答えます。

私が出会ったほとんどの言語はこの構文をサポートしていないようです。これは数学でどれほど一般的であるかを考えると奇妙です。Pythonは注目に値する例外です。JavaScript 例外のように見えますが、実際には、演算子の優先順位と暗黙の変換の単なる残念な副産物です。node.jsでは、と1 < 3 < 2評価されtrueます(1 < 3) < 2 === true < 2 === 1 < 2

だから、私の質問はこれです:なぜx < y < z期待されるセマンティクスで、プログラミング言語で一般的に利用できないのですか?


1
これは文法ファイルであり、Pythonのドキュメントに簡単に貼り付けられています
アーロンホール

私はPythonを知っているだけでなく他の言語も知りませんが、Pythonの解釈のシンプルさについて話すことができます。おそらく答えるべきです。しかし、gnasher729が損害を与えるという結論には同意しません。
アーロンホール

@ErikEidt-高校(またはそれ以前)で教えられた方法で数式を書くことができるようになりました。数学的に傾倒している人は誰でも、$ a <b <c <d $の意味を知っています。機能が存在するからといって、それを使用する必要があるわけではありません。それを好まない人は、常にその使用を禁止する個人またはプロジェクトのルールを作ることができます。
デビッドハンメン

2
結局のところ、C#チーム(例として)がLINQを探索し、将来的にはレコードタイプとパターンマッチングを行う方が、4つのキーストロークを節約する構文シュガーを追加するよりも優れていると思います表現力を追加します(s static bool IsInRange<T>(this T candidate, T lower, T upper) where T : IComparable<T>を本当にわざわざ見ているようなヘルパーメソッドを書くこともできます&&
サラ

1
SQLは非常に「主流」であり、「xは1〜10」と書くことができます
-JoelFan

回答:


30

これらは二項演算子であり、連鎖すると、通常および自然に次のような抽象構文ツリーを生成します。

二項演算子の通常の抽象構文木

(リーフから行う)評価時に、これはからブール値の結果を生成しx < y、その後、タイプエラーが発生しようとしますboolean < z。ためにはx < y < z、あなたが述べたように仕事に、あなたは次のように構文木を生成するコンパイラでの特殊なケースを作成する必要があります。

特別な場合の構文木

これができないというわけではありません。それは明らかにそうですが、実際にはそれほど頻繁には起こらないケースのために、パーサーに複雑さを追加します。あなたは基本的に、時には二項演算子のように振る舞い、時には三項演算子のように振る舞うシンボルを作成します。これにより、言語設計者が可能であれば回避したいことをうまく行かないための多くのスペースが追加されます。


1
「その後、boolean <zを実行しようとすると型エラーが発生します」-コンパイラがz比較のためにyをインプレースで評価することで連鎖を許可する場合は例外です。「それにより、言語設計者が可能であれば回避したいことをうまく行かないための多くのスペースが追加されます。」実際には、Pythonはこれをやっても問題がなく、解析のためのロジックは、単一の機能に限定されていますhg.python.org/cpython/file/tip/Python/ast.c#l1122 -どこへ行くもののためのスペースのない多くのことを違う。「二項演算子のように動作することもあれば、三項演算子のように効果的に動作することもあります。」Pythonでは、比較チェーン全体が三項です。
アーロンホール

2
私はそれが実行可能ではないと言ったことは一度もありませんでした。他の言語では、比較演算子を処理するためだけに別のコードを記述する必要はありません。他の二項演算子で無料で入手できます。優先順位を指定するだけです。
カールビーレフェルト

はい、しかし...そこにある、すでに三項演算子の言語の多くで利用できますか?
JensG

1
@JensG 3進数の表記は、3つの引数を取ることを意味します。リンクのコンテキストでは、それは三項条件演算子です。明らかに2をとるが実際には3をとる演算子の造語で「三項」と思われます。この答えの主な問題は、ほとんどがFUDであるということです。
アーロンホール

2
私は、この受け入れられた答えに対する支持者の一人です。(@JesseTG:この回答を受け入れないでください。)この質問は、何をx<y<z意味するのか、またはもっと重要なことですx<y<=z。この答えx<y<zは三項演算子として解釈されます。それはまさにこの明確に定義された数式が解釈されるべきではない方法です。x<y<z代わりにの省略形です(x<y)&&(y<z)。個々の比較はまだバイナリです。
デビッドハンメン

37

x < y < zプログラミング言語で一般的に利用できないのはなぜですか?

この回答では、

  • この構造は言語の文法で実装するのは簡単であり、言語ユーザーに価値をもたらしますが、
  • これがほとんどの言語に存在しない主な理由は、他の機能との相対的な重要性と、いずれかの言語の統治機関の不本意によるものです。
    • 潜在的に重大な変更を伴うユーザーを混乱させる
    • 機能を実装するために移動します(例:怠iness)。

前書き

この質問については、Pythonistの観点から話すことができます。私はこの機能を備えた言語のユーザーであり、その言語の実装の詳細を勉強したいと思っています。さらに、CやC ++(ISO規格は委員会によって管理され、年ごとにバージョン管理されます)などの言語を変更するプロセスにある程度精通しています。

Pythonのドキュメントと実装

docs / grammarから、比較演算子を使用して任意の数の式を連鎖できることがわかります。

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

さらにドキュメントには次のように記載されています。

比較は任意に連鎖できます。たとえば、x <y <= zはx <yおよびy <= zと同等です。ただし、yは1回だけ評価されます(ただし、両方の場合、x <yが見つかったときにzはまったく評価されません)間違っている)。

論理的等価

そう

result = (x < y <= z)

論理的であると同等の評価の観点xyおよびz例外を除いて、y二回評価されます。

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

繰り返しますが、違いはyがで1回だけ評価されること(x < y <= z)です。

(注、括弧は完全に不要で冗長ですが、他の言語から来るもののために括弧を使用しました。上記のコードは非常に合法的なPythonです。)

解析された抽象構文ツリーの検査

Pythonが連鎖比較演算子をどのように解析するかを調べることができます。

>>> 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')]))])"

したがって、これはPythonや他の言語で解析するのは本当に難しいことではないことがわかります。

>>> 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')]))])"

そして、現在受け入れられている答えとは反対に、三項演算は一般的な比較演算であり、最初の式、特定の比較の反復可能、および必要に応じて評価する式ノードの反復可能を取ります。シンプル。

Pythonの結論

私は個人的に、範囲のセマンティクスは非常にエレガントであり、私が知っているほとんどのPython専門家は、それが損傷することを考慮するのではなく、機能の使用を奨励するでしょう-セマンティクスは評判の良いドキュメント(上記のように)で非常に明確に述べられています。

コードは、書かれているよりもはるかに多く読み取られることに注意してください。コードの読みやすさを改善する変更は、恐れ、不確実性、疑いの一般的なスペクターを上げることで割引されるのではなく、受け入れられるべきです。

では、なぜプログラミング言語でx <y <zが一般的に利用できないのですか?

私は、機能の相対的な重要性と、言語の知事によって許可された相対的な変化の勢い/慣性を中心とする理由の合流があると思います。

他のより重要な言語機能について同様の質問をすることができます

JavaまたはC#で多重継承が利用できないのはなぜですか?ここでどちらの質問にも良い答えはありません。ボブ・マーティンが主張するように、おそらく開発者は怠け者であり、与えられた理由は単に言い訳にすぎません。また、多重継承は、コンピューターサイエンスにおいて非常に大きなトピックです。確かに、演算子チェーンよりも重要です。

簡単な回避策があります

比較演算子チェーンはエレガントですが、多重継承ほど重要ではありません。JavaとC#が回避策としてインターフェイスを備えているように、すべての言語で複数の比較を行います。比較をブール値の「and」で単純に連鎖させるだけで十分に機能します。

ほとんどの言語は委員会によって管理されています

ほとんどの言語は委員会によって進化しています(Pythonのように賢明なBenevolent Dictator For Lifeを持つのではなく)。そして、私は、この問題がそれぞれの委員会から抜け出すのに十分な支援を見ていなかったと推測します。

この機能を提供しない言語は変更できますか?

x < y < z予想される数学的セマンティクスなしで言語が許可する場合、これは重大な変更になります。そもそもそれを許可しなかった場合、追加するのはほとんど簡単です。

重大な変更

重大な変更を伴う言語について:重大な動作の変更を伴う言語を更新しますが、ユーザーはこれを好まない傾向があります。特に、機能が壊れる可能性があるユーザーはそうです。ユーザーがの以前の動作に依存している場合、x < y < z大声で抗議する可能性があります。また、ほとんどの言語は委員会によって管理されているため、このような変更をサポートする政治的意思を得ることができないと思います。


正直なところ、 `x <y <z`のような比較演算を連鎖する言語によって提供されるセマンティクスに問題はありませんが、開発者が精神的ににマッピングx < y < zするのは簡単です(x < y) && (y < z)。つまらない、連鎖比較のメンタルモデルは一般的な数学です。古典的な比較は数学一般ではなく、ブール論理です。x < yバイナリの回答を生成します{0}y < zバイナリの回答を生成します{1}{0} && {1}説明的な答えを生成します。ロジックは単純な連鎖ではなく構成されています。
K.アラン・ベイツ

コミュニケーションをよりよくするために、コンテンツ全体を直接要約する1つの文で答えを序文にしました。長い文なので、箇条書きにしました。
アーロンホール

2
いくつかの言語がこの機能を実装する主な理由は、Guidoの前には誰もそれについて考えもしなかったからです。Cを継承する言語は、主にCの開発者が40年以上前に「間違った」(数学的に間違っている)ため、この「正しい」(数学的に正しい)を得ることができません。それらの言語の解釈方法の直感に反する性質に依存するコードがたくさんありますx<y<z。言語にはかつてこのようなことをするチャンスがあり、そのチャンスは言語の始まりにあります。
デビッドハンメン

1
@ K.AlanBates 2つのポイントを作成します。1)演算子の連鎖はずさんで、2)構文糖には値がありません。最初に:演算子チェーンが100%決定論的であることを実証しましたが、そうではないでしょうか?おそらく、一部のプログラマーは、構成要素を理解する能力を拡大するには精神的に怠tooすぎますか?2番目のポイント:読みやすさを直接主張しているように聞こえますか?読みやすさを向上させる場合、構文糖は通常良いものと見なされませんか?このように考えるのが普通なら、なぜプログラマーはこうしてコミュニケーションしたくないのでしょうか?コードは読むように書かれるべきですよね?
アーロンホール

2
I have watched both Ruby and Python implement breaking changes.好奇心が強い人のために、ループ変数とクロージャーを含むC#5.0の重大
2009

13

コンピューター言語は、可能な限り最小の単位を定義し、それらを結合できるようにします。可能な最小単位は、ブール値を返す「x <y」のようなものです。

三項演算子を要求できます。例は、x <y <zです。では、どのような演算子の組み合わせを使用できますか?明らかにx> y> zまたはx> = y> = zまたはx> y> = zまたはx == y == zを許可する必要があります。x <y> zはどうですか?x!= y!= z?最後の1つは、x!= yとy!= zの意味、または3つすべてが異なることを意味しますか?

引数の昇格:CまたはC ++では、引数は共通の型に昇格されます。それでは、x <y <zはxはdoubleですが、yとzはlong long intを意味しますか?3つすべてが倍増しましたか?または、yは1回はdouble、もう1回はlong longとみなされますか?C ++で片方または両方の演算子がオーバーロードされるとどうなりますか?

最後に、オペランドをいくつでも許可していますか?a <b> c <d> e <f> gのように?

まあ、それはすべて非常に複雑になります。気にしないのは、x <y <zで構文エラーが発生することです。なぜなら、x <y <zが実際に何をするのかわからない初心者に与えられる損害に比べて、その有用性は小さいからです。


4
要するに、うまく設計するのは難しい機能です。
ジョンパーディ

2
これは、この機能がよく知られている言語に含まれていない理由を説明する理由ではありません。実際のところ、明確に定義された方法で言語に組み込むのは非常に簡単です。すべての演算子がバイナリではなく、同様のタイプの演算子で接続されたリストとして表示するだけです。sumsについても同じことができますがx + y + z、意味的な違いを意味しない唯一の違いがあります。ですから、そうすることを気にかけている有名な言語はないということです。
cmaster

1
Pythonでは、それは少し最適化されていると思います(x < y < z同等である((x < y) and (y < z))一度yだけ評価されます)、コンパイルされた言語が最適化されると思います。「その有用性は、x <y <zが実際に何をするのか理解できない初心者に与えられる損害と比較して小さいためです。」信じられないほど便利だと思います。おそらくつもり-1そのための...
アーロン・ホール

最も愚かなプログラマにとって混乱を招く可能性のあるすべてのものを排除する言語を設計することが目標である場合、そのような言語はすでに存在します:COBOL。私はむしろ1が実際に書くことができるのpython、自分自身を、使用したいa < b > c < d > e < f > g「明らかに」という意味で、(a < b) and (b > c) and (c < d) and (d > e) and (e < f) and (f > g)。書くことができるからといって、そうすべきだという意味ではありません。そのような怪物を排除することは、コードレビューの範囲です。一方、0 < x < 8Python で書くことは、xが0から8の間にあることを意味する明白な(怖い引用符なし)ことを意味します。
デビッドハンメン

@DavidHammen、皮肉なことに、COBOLは実際に<b <cを許可しています
-JoelFan

10

多くのプログラミング言語でx < yは、2つのオペランドを受け入れ、単一のブール結果に評価されるバイナリ式です そのため、複数の式を連鎖、場合true < zfalse < z意味を成さなくなり、そしてそれらの式が正常に評価された場合、彼らは間違った結果を生成する可能性が高いです。

それは考えるためにはるかに簡単だx < yとして、関数呼び出しの2つのパラメータを受け取り、単一のブール結果を生成します。実際、多くの言語が内部でそれを実装しています。構成可能で、簡単にコンパイルでき、機能します。

x < y < zシナリオは、はるかに複雑です。さて、コンパイラは、実際には、ファッションにあり3:関数をx < yy < zと、これら2つの値の結果は、すべて間違いなくの文脈の中で、互いにAND あいまいな言語の文法

なぜ彼らは他の方法でそれをしましたか?明確な文法であるため、実装がはるかに簡単で、修正がはるかに簡単です。


2
言語を設計している場合、正しい結果を得る機会があります。
-JesseTG

2
もちろん、質問に答えます。質問が本当に理由である場合、答えは「それが言語設計者が選択したものだから」です。それよりも良い答えを思い付くことができるなら、それを選んでください。Gnasherが本質的に彼の答えの最初の段落でまったく同じことを言ったことに注意してください。
ロバートハーベイ

3
繰り返しますが、髪を分割しています。プログラマはそうする傾向があります。「ごみを出しますか?」「いいえ」「ごみを出しますか?」"はい。"
ロバートハーヴェイ

2
私も最後の段落に異議を唱えます。Pythonはチェーン比較をサポートし、そのパーサーはLL(1)です。また、セマンティクスを定義して実装することも必ずしも難しくありません。Python e1 op1 e2 op2 e3 op3 ...e1 op e2 and e2 op2 e3 and ...、各式が1回だけ評価されることを除いて、同等であると述べています。(ちなみに、この単純な規則には、ステートメントa == b is Trueが意図した効果を持たないという紛らわしい副作用があります。)

2
@RobertHarvey re:answerこれは、メインの質問に対する私のコメントのために、私の心がすぐに行き着いた場所でした。x < y < z言語セマンティクスに特定の値を追加するためのサポートは考えていません。 (x < y) && (y < z)より広くサポートされ、より明確で、より表現力があり、構成要素に簡単に消化され、より構成可能で、より論理的で、より簡単にリファクタリングされます。
K.

6

ほとんどの主流言語は(少なくとも部分的に)オブジェクト指向です。基本的に、OOの基本原則は、オブジェクトが他のオブジェクト(またはそれ自体)にメッセージを送信し、そのメッセージの受信者がそのメッセージへの応答方法を完全に制御することです。

さて、次のような実装方法を見てみましょう

a < b < c

厳密に左から右(左結合)に評価できます。

a.__lt__(b).__lt__(c)

しかし、今、私たちは呼ん__lt__の結果にa.__lt__(b)あります、Boolean。それは意味がありません。

右結合を試してみましょう:

a.__lt__(b.__lt__(c))

いや、それも理にかなっていない。今、私たちは持っていa < (something that's a Boolean)ます。

さて、それを構文糖として扱うのはどうですか。n個の<比較のチェーンを作成して、n-1-aryメッセージを送信しましょう。これは、メッセージ__lt__aに渡しbc引数として、引数として渡すことを意味します。

a.__lt__(b, c)

さて、それは機能しますが、ここに奇妙な非対称性があります:aそれがより小さいかどうかを決定するようになりbます。しかし、bそれが未満であるかどうかを決定することができませんc、その代わりにその決定行われaます。

それをn-aryメッセージとして解釈するとどうなりthisますか?

this.__lt__(a, b, c)

最後に!これは機能します。これは、オブジェクトの順序は、もはやオブジェクトのプロパティであること、しかし、意味ない(例えばかどうかaよりも小さいbの財産でもないaのもb)代わりのプロパティコンテキスト(つまりthis)。

奇妙に思える主流の観点から。ただし、たとえばHaskellでは、これは正常です。Ordたとえば、typeclassの複数の異なる実装が存在する可能性があり、aがより小さいかどうかbは、どのtypeclassインスタンスがスコープ内にあるかによって異なります。

しかし、実際には、そうではないこと、すべての奇妙な!Java(Comparator)と.NET(IComparer)の両方には、ソートアルゴリズムなどに独自の順序関係を挿入できるインターフェースがあります。したがって、順序付けは型に固定されたものではなく、コンテキストに依存することを完全に認めています。

私の知る限り、現在、このような翻訳を行う言語はありません。ただし、優先順位があります。IokeSephの両方に、設計者が「三項演算子」と呼ぶものがあります。これは、構文的には二項ですが、意味的には三項です。特に、

a = b

されていないメッセージを送信するものとして解釈=a渡すb引数としてではなく、メッセージを送信するように=、「現在のグラウンド」(類似しているが同一ではない概念にthis渡す)ab引数として。だから、a = bとして解釈されます

=(a, b)

ではなく

a =(b)

これは、n項演算子に簡単に一般化できます。

これはオブジェクト指向言語に特有のものであることに注意してください。オブジェクト指向では、メッセージ送信の解釈を最終的に担当する単一のオブジェクトが常に存在します。これまで見てきたように、a < b < cどのオブジェクトがそうあるべきかはすぐにはわかりません。

ただし、これは手続き型言語または関数型言語には適用されません。たとえば、SchemeCommon Lisp、およびClojureでは、<関数はn項であり、任意の数の引数で呼び出すことができます。

特に、<ではない:「未満」、むしろ、これらの機能が少し異なって解釈されている意味

(<  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

3

言語設計者がそれを考えなかったか、それが良いアイデアだとは思わなかったからです。Pythonは、単純な(ほぼ)LL(1)文法で説明したとおりにそれを行います。


1
これは、ほとんどすべての主流言語の通常の文法で解析されます。@RobertHarveyが与えた理由のために正しく理解されないだけです。
メイソンウィーラー

@MasonWheelerいいえ、必ずしもそうではありません。比較が他の演算子と交換できるようにルールが記述されている場合(たとえば、優先順位が同じであるため)、正しい動作が得られません。Pythonがすべての比較を1つのレベルに配置しているという事実により、Pythonはシーケンスを接続詞として扱うことができます。
ニールG

1
あんまり。1 < 2 < 3JavaまたはC#に入れれば、演算子の優先順位に問題はありません。無効なタイプに問題があります。問題は、これはまだ記述したとおりに正確に解析されることですが、個々の比較のシーケンスから連鎖比較に変換するには、コンパイラに特殊なケースのロジックが必要です。
メイソンウィーラー

2
@MasonWheeler私が言っているのは、それが機能するためには言語を設計する必要があるということです。その一部は、文法を正しくすることです。(比較が他の演算子と交換可能であるようにルールが記述されている場合、たとえば、それらは同じ優先順位を持っているため、正しい動作を得ることができません。)その別の部分は、ASTしません。私の答えの主なポイントは、それが言語設計者の決定だということです。
ニールG

@MasonWheeler私たちは同意すると思います。文法を正しく理解するのは難しくないことを強調しただけです。この方法で動作させるかどうかを事前に決定するだけです。
ニールG

2

次のC ++プログラムは、警告を可能な限り最高レベル(-Weverything)に設定しても、clangからの覗き見なしでコンパイルします。

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

一方、gnuコンパイラスイートはそれをうまく警告しますcomparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses]

だから、私の質問はこれです:なぜx <y <zは、期待されるセマンティクスで、プログラミング言語で一般的に利用できないのですか?

答えは簡単です。下位互換性。相当するものを使用1<3<2し、結果が真のようになると予想する膨大な量のコードが出回っています。

言語設計者には、この「正しい」ことを実現するチャンスは1つしかありません。それが、言語が最初に設計された時点です。最初に「間違った」ということは、他のプログラマーがその「間違った」振る舞いをかなり早く利用することを意味します。2回目に「正しく」取得すると、既存のコードベースが壊れます。


+1このプログラムは数学で明らかに偽である式の結果として「1」を出力するためです。不自然ですが、この言語機能が追加された場合、理解できない変数名を使用した実際の例はデバッグの悪夢になります。
セスBattin

@SethBattin-これはPythonのデバッグの悪夢ではありません。Pythonの唯一の問題はif x == y is True : ...、私の意見です。この種のコードを書いた人は、(もし彼が生きていれば)Torquemada自身を失神させるような特別で特別な種類の拷問にさらされるに値します。
デビッドハンメン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.