言語が短絡評価を使用しないのはなぜですか? 使わないメリットはありますか?
パフォーマンスの問題が発生する可能性があるようですが、本当ですか? なんで?
関連質問 :短絡評価を使用する利点
短絡評価を使用しない理由:
関数、プロパティ Gets、または演算子メソッドに副作用がある場合、動作が異なり、結果が異なるためです。そしてこれは、A) 言語標準、B) 言語の以前のバージョン、または C) 言語の典型的なユーザーのデフォルトの仮定と矛盾する可能性があります。これらは、VB が短絡しない理由です。
ユーザーが入力した順序ではなく、コンパイラが式、演算子、および部分式を自由に並べ替えて整理できるようにしたい場合があるためです。これらは、SQL が短絡しない理由です。 (または少なくとも、SQL を使用するほとんどの開発者が考える方法ではありません)。したがって、SQL (および他のいくつかの言語)は短絡する可能性がありますが、それが決定された場合のみであり、暗黙的に指定した順序である必要はありません。
ほとんどの開発者が C、C++、C#、Java などに期待する「自動で暗黙的な順序固有の短絡」について質問していると仮定しています。VB と SQL の両方に、順序固有を明示的に強制する方法があります。短絡。ただし、通常、人々がこの質問をするとき、それは「私が意図したことを実行してください」という質問です。つまり、「なぜそれは私の望みどおりにならないのですか?」という意味で、私が書いた順序で自動的に短絡します。
私が考えることができる利点の 1 つは、一部の操作には、発生すると予想される副作用がある可能性があることです。
例:
if (true || someBooleanFunctionWithSideEffect()) {
...
}
しかし、それは通常、眉をひそめています。
デフォルトでは、Ada はそれを行いません。短絡評価を強制するには、 or の代わりに or を使用する必要and then
がor else
ありand
ますor
。
問題は、実際に速度が低下する状況がいくつかあることです。2 番目の条件の計算が速く、最初の条件が「and」の場合はほぼ常に true、「or」の場合は false である場合、追加のチェック分岐命令は一種の無駄です。ただし、分岐予測子を備えた最新のプロセッサでは、これはあまり当てはまらないことを理解しています。もう 1 つの問題は、後半が安価であるか失敗する可能性が高いことをコンパイラがたまたま知っている可能性があり、それに応じてチェックの順序を変更したい場合があることです (短絡動作が定義されている場合は実行できません)。
2 番目のテストに副作用がある場合、コードの予期しない動作につながる可能性があるという反対意見を聞いたことがあります。私見ですが、言語をよく知らない場合にのみ「予期しない」ことになりますが、これについて議論する人もいます。
この問題について実際の言語設計者が何を言わなければならないかに興味がある場合は、Ada 83 (元の言語) の根拠からの抜粋を次に示します。
A や B などのブール式のオペランドは、任意の順序で評価できます。項 B の複雑さによっては、項 A の値が TRUE の場合にのみ B を評価する方が効率的である場合があります (一部のマシンではすべてではありません)。ただし、これはコンパイラによる最適化の決定であり、この最適化が常に行われると仮定するのは正しくありません。他の状況では、前の条件が満たされた場合にのみ各条件が評価される (意味を持つ) 条件の結合を表現したい場合があります。これらのことは両方とも、短絡制御フォームで行うことができます...
Algol 60 では、条件式を使用しないと完全な評価が実行されないため、短絡評価の効果を得ることができます。これは、多くの場合、従うのが面倒な構造につながります...
いくつかの言語では、ブール条件の評価方法が定義されていません。結果として、短絡評価に基づくプログラムは移植できなくなります。これは、ブール演算子を短絡制御フォームから分離する必要があることを明確に示しています。
100回のうち99回は、パフォーマンスのために短絡演算子を好むと思います。
しかし、私がそれらを使用しない場所で見つけた 2 つの大きな理由があります。(ちなみに、私の例は C であり、&& と || はショートサーキットであり、& と | はショートサーキットではありません。)
1.) 最初の関数が返す値に関係なく、if ステートメントで 2 つ以上の関数を呼び出したい場合。
if (isABC() || isXYZ()) // short-circuiting logical operator
//do stuff;
その場合、isABC() が false を返す場合にのみ isXYZ() が呼び出されます。しかし、何があっても isXYZ() を呼び出したい場合があります。
したがって、代わりに次のようにします。
if (isABC() | isXYZ()) // non-short-circuiting bitwise operator
//do stuff;
2.) 整数でブール演算を実行している場合。
myNumber = i && 8; // short-circuiting logical operator
必ずしも以下と同じではありません:
myNumber = i & 8; // non-short-circuiting bitwise operator
この状況では、短絡演算子が必ずしも式全体を評価するとは限らないため、実際には異なる結果が得られる可能性があります。そして、それは基本的にブール演算には役に立たなくなります。したがって、この場合、代わりに非短絡 (ビット単位) 演算子を使用します。
ほのめかしたように、これらの 2 つのシナリオは私にとって本当にまれです。しかし、どちらのタイプの演算子にも実際のプログラミング上の理由があることがわかります。幸いなことに、今日の一般的な言語のほとんどは両方を備えています。VB.NET にも AndAlso および OrElse 短絡演算子があります。今日の言語に両方がない場合、それは時代遅れであり、プログラマーを本当に制限していると言えます。
Bill は、ショートサーキットを使用しない正当な理由をほのめかしましたが、それをより詳細に説明しました。高度に並列化されたアーキテクチャでは、制御パスの分岐に問題がある場合があります。
NVIDIA の CUDA アーキテクチャを例に取ります。グラフィックス チップは SIMT アーキテクチャを使用します。つまり、同じコードが多くの並列スレッドで実行されます。ただし、これは、すべてのスレッドが毎回同じ条件分岐を行う場合にのみ機能します。異なるスレッドが異なるコード パスを取る場合、評価はシリアル化されます。これは、スレッドの一部が待機しなければならず、他のスレッドが代替コード ブランチを実行するため、並列化の利点が失われることを意味します。
短絡には実際にはコードの分岐が含まれるため、CUDA などの SIMT アーキテクチャでは短絡操作が有害になる可能性があります。
– しかし、ビルが言ったように、それはハードウェアの考慮事項です。言語に関する限り、私はあなたの質問に断固としてノーと答えます。短絡を防ぐことは意味がありません。
On SQL Server boolean operator short-circuitの例を見てください。これは、ブール ショート サーキットが使用されていない場合、SQL の特定のアクセス パスがより効率的である理由を示しています。私のブログの例では、ブール値の短絡に実際に依存すると、SQL で短絡を想定した場合にコードが壊れる可能性があることを示していますが、SQL が最初に右側を評価する理由を読むと、それが正しいことがわかり、これにより、アクセス パスが大幅に改善されます。
ストレッチとして:
言語を(素晴らしさを犠牲にして)超安全にしたい場合は、短絡評価を削除します。何か「安全な」ことが起こるまでに一定の時間がかかる場合、タイミング攻撃を使用してそれを台無しにすることができます。短絡評価では、実行に異なる時間がかかるため、攻撃の穴が開いてしまいます。この場合、短絡評価を許可しないことでさえ、より安全なアルゴリズムを作成するのに役立つことが期待されます (とにかくタイミング攻撃について)。
右側を評価したい場合:
if( x < 13 | ++y > 10 )
printf("do something\n");
おそらく、x < 13 であるかどうかにかかわらず、y をインクリメントする必要がありました。ただし、これを行うことに対する良い議論は、副作用のない条件を作成する方が、通常はプログラミングの実践として優れているということです。
Adaプログラミング言語は、コンパイラがコンストラクトを最適化し、場合によっては並列化できるようにするために、短絡しないブール演算子 ( AND
, )と、プログラマが望む場合にOR
短絡を明示的に要求する演算子( AND THEN
, )の両方をサポートしていました。OR ELSE
このような二重のアプローチの欠点は、言語をもう少し複雑にすることです (同じ「両方をやろう!」という流れで行われる 1000 の設計上の決定により、プログラミング言語は全体的に非常に複雑になります;-)。
言語 Lustreは短絡評価を使用しません。if-then-elses では、then 分岐と else 分岐の両方がティックごとに評価され、条件の評価に応じて条件分岐の結果と見なされます。
その理由は、この言語や他の同期データフロー言語には、過去を語る簡潔な構文があるからです。将来のサイクルで必要になった場合に、それぞれの過去を利用できるように、各分岐を計算する必要があります。言語は関数型であるはずなので、それは問題ではありませんが、そこから C 関数を呼び出すことができます (そして、おそらく、それらが思ったよりも頻繁に呼び出されることに気付くでしょう)。
Lustre では、
if (y <> 0) then 100/y else 100
典型的な初心者の間違いです。式 100/y は y=0 のサイクルでも評価されるため、ゼロによる除算は避けられません。
これが現在どの言語でも起こっていることだとは思いませんが、操作の両側を異なるスレッドにフィードすることはかなり興味深いでしょう。ほとんどのオペランドは、互いに干渉しないように事前に決定されている可能性があるため、異なる CPU にハンドオフするのに適しています。
この種のことは、複数の分岐を評価して 1 つを選択する傾向がある高度に並列化された CPU では重要です。
ちょっと言い過ぎですが、あなたは「言語はなぜ」ではなく、「なぜ言語なのか」と尋ねました。
読みやすさの問題には有効だと思います。誰かがショート サーキット評価を十分に明白ではない方法で利用すると、メンテナーが同じコードを見てロジックを理解するのが難しくなる可能性があります。
メモリが機能する場合、erlang は standard および/or、andalso/orelse の 2 つの構造を提供します。これは、「はい、これはショートサーキットであることを知っています。あなたもそうすべきです」という意図を明確にしますが、他のポイントと同様に、コードから意図を導出する必要があります。
例として、メンテナーが次の行に出くわしたとします。
if(user.inDatabase() || user.insertInDatabase())
user.DoCoolStuff();
意図が「ユーザーがデータベースにない場合は、彼/彼女/それを挿入し、それが機能する場合はクールなことをする」ことを認識するのに数秒かかります。
他の人が指摘しているように、これは実際には副作用のあることをするときにのみ関係があります。
短絡はアプリケーション IE の動作を変更する可能性があるため:
if(!SomeMethodThatChangesState() || !SomeOtherMethodThatChangesState())
多くの回答が副作用について語っています。(私の意見では) 短絡によって読みやすさが向上する、副作用のない Python の例を次に示します。
for i in range(len(myarray)):
if myarray[i]>5 or (i>0 and myarray[i-1]>5):
print "At index",i,"either arr[i] or arr[i-1] is big"
短絡により、myarray[-1] にアクセスしようとしなくなります。Python の配列は 0 から始まるため、例外が発生します。もちろん、コードは短絡なしで記述できます。たとえば、次のようになります。
for i in range(len(myarray)):
if myarray[i]<=5: continue
if i==0: continue
if myarray[i-1]<=5: continue
print "At index",i,...
しかし、短絡バージョンの方が読みやすいと思います。
パフォーマンスの問題については知りませんが、それを回避する (または少なくとも過度に使用する) ための 1 つの考えられる議論は、他の開発者を混乱させる可能性があるということです。
副作用の問題についてはすでに素晴らしい回答がありますが、質問のパフォーマンスの側面については何も見ませんでした。
短絡評価を許可しない場合、パフォーマンスの問題は、結果が変わらなくても両側を評価する必要があることです。これは通常は問題になりませんが、次の 2 つの状況のいずれかで関連する可能性があります。
短絡評価は、式の一部の条件付き評価を自動的に提供します。
主な利点は、式が単純化されることです。
パフォーマンスは向上する可能性がありますが、非常に単純な式ではペナルティが発生する可能性もあります。
別の結果として、式の評価の副作用が影響を受ける可能性があります。
一般に、副作用に依存することは良い方法ではありませんが、特定のコンテキストでは、推奨される解決策になる可能性があります。
VB6 は短絡評価を使用しません。新しいバージョンが使用するかどうかはわかりませんが、疑わしいと思います。これは、古いバージョンでもそうではなかったためであり、VB6 を使用していたほとんどの人はそれが起こるとは予想しておらず、混乱を招くためだと思います。
これは、私がスパゲッティ コードを書いた初心者の VB プログラマーから抜け出し、真のプログラマーになるための旅を続けるのを非常に困難にした理由の 1 つにすぎません。