コンパクトな書き方(a + b == cまたはa + c == bまたはb + c == a)


136

ブール式を記述するためのよりコンパクトな方法またはpythonicな方法はありますか

a + b == c or a + c == b or b + c == a

思いついた

a + b + c in (2*a, 2*b, 2*c)

しかし、それは少し奇妙です。


16
もっとコンパクト?たぶん。もっとPythonic?ありそうもない。
chepner

126
将来の自分に好意を示し、それを元の形に保ちます。これがこの目的をすぐに伝えるのはそれだけです。変更しないでください。「シンプルは複雑よりも優れています。」、「読みやすさが重要です。」「実装の説明が難しい場合、それは悪い考えです。」
突く

21
Pythonic ==読めない?
nhaarman 2015

3
@wwiiそれらは相互に排他的ではありません。a = 0、b = 0、c = 0を参照してください;)
Honza Brabec

1
@phresnelが言ったこと。式を「簡略化」しようとする代わりに、わかりやすい名前を付けて関数で囲みます。
頭足類

回答:


206

Zen of Pythonを見てみると、次のことが強調されます。

禅のPython、ティム・ピーターズ

醜いよりも美しい方がいいです。
明示的は暗黙的よりも優れています。
シンプルは複雑よりも優れています。
複雑は複雑よりも優れています。
ネストよりもフラットの方が適しています。
疎は密よりも優れています。
読みやすさが重要です。
特別なケースは、ルールを破るほど特別なものではありません。
実用性は純粋さを上回りますが。
エラーがサイレントに渡ることはありません。
明示的に沈黙させない限り。
あいまいな状況に直面して、推測する誘惑を拒否してください。
それを行うには、明白な方法が1つ(できれば1つだけ)あるはずです。
あなたがオランダ人でない限り、その方法は最初は明白ではないかもしれませんが。
今は決してないよりはましです。
多くの場合、今。
実装の説明が難しい場合は、悪い考えです。
実装が説明しやすい場合は、良いアイデアかもしれません。
名前空間は非常に魅力的なアイデアの1つです。もっと多くのことをしましょう。

最もPythonicなソリューションは、最も明確で、最も単純で、最も簡単に説明できるものです。

a + b == c or a + c == b or b + c == a

さらに良いことに、このコードを理解するのにPythonを知る必要さえありません!それはだことは簡単。これは、予約なしで最良のソリューションです。それ以外は知的オナニーです。

さらに、これは回路を短絡させるすべての提案の中の唯一のものであるため、これも同様に最もパフォーマンスの高いソリューションです。の場合a + b == c、追加と比較は1回だけ行われます。


11
さらに、括弧を入れて意図を明確にしてください。
ブライアンオークリー

3
その意図は括弧なしですでに非常に明確です。かっこを使用すると読みにくくなります-優先順位が既にこれをカバーしているのに、なぜ著者がかっこを使用しているのですか?
Miles Rout

1
賢くなりすぎようとすることについてのもう1つの注意事項:考慮しなかった条件がないために、予期しないバグが発生する可能性があります。言い換えれば、新しいコンパクトソリューションは同等であると考えるかもしれませんが、すべての場合ではありません。それ以外の方法でコーディングする説得力のある理由(パフォーマンス、メモリの制約など)がない限り、明快さが最も重要です。
Rob Craig

それは式が何のためにあるかに依存します。「明示的は暗黙的より優れている」を見てください。「ソート優先」アプローチは、プログラムまたは他の1つが何をしているかをより明確に表現している可能性があります。質問では判断できないと思います。
Thomas Ahle、2015

101

次の3つの等式を解く:

a in (b+c, b-c, c-b)

4
これの唯一の問題は副作用です。bまたはcがより複雑な式である場合、それらは複数回実行されます。
Silvio Mayolo 2015

3
@Kroltan私のポイントは、私が実際に彼の質問に答えたことで、「よりコンパクトな」表現を求めていました。参照:en.m.wikipedia.org/wiki/Short-circuit_evaluation
Alex Varga

24
このコードを読んでいる人は、おそらく「賢い」と言ってあなたを呪います。
Karoly Horvath

5
@SilvioMayoloオリジナルも同じです
イズカタ

1
@AlexVarga、「私のポイントは私が実際に彼の質問に答えたことでした」。あなたがした; 文字数が30%少なくなります(演算子間にスペースを入れます)。私はあなたの答えが間違っていたと言っているのではなく、それがどれほど慣用的(pythonic)であるかについてコメントしているだけです。素敵な答え。
Paul Draper

54

Pythonには、シーケンスのすべての要素に対してを実行するany関数がありorます。ここでは、ステートメントを3要素のタプルに変換しました。

any((a + b == c, a + c == b, b + c == a))

or個々の条件を計算することは高価であるならば、それは良いかもしれない、あなたのオリジナルの構造を維持するため、短絡です。


2
any()そしてall()短絡も。
TigerhawkT3 2015

42
@ TigerhawkT3ただし、この場合ではありません。3つの式はタプルが存在する前に評価され、タプルはany実行前に存在します。
突く

13
ああ、なるほど。そこにジェネレータまたは同様のレイジーイテレータがあるときだけだと思います。
TigerhawkT3、2015

4
anyそして、それらが与えられたイテラブルを検査するallプロセスを「短絡」します。ただし、その反復可能オブジェクトがジェネレーターではなくシーケンスである場合、関数呼び出しが発生する前に完全に評価されています
Karl Knechtel、

これは、複数行に分割するのは簡単だという利点(ダブルインデント引数に持ちany、シングルインデント):if文)、数学が関与している時に、読みやすくするために多くを助けた
Izkata

40

正の数のみを処理していることがわかっている場合、これは機能し、かなりクリーンです。

a, b, c = sorted((a, b, c))
if a + b == c:
    do_stuff()

すでに述べたように、これは正の数に対してのみ機能します。しかし、それらがポジティブになることがわかっている場合、これは非常に読みやすいソリューションIMOです。これは、関数ではなくコード内で直接です。

これを行うと、少し繰り返し計算が行われる可能性があります。しかし、目標としてパフォーマンスを指定しませんでした:

from itertools import permutations

if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
    do_stuff()

またはpermutations()、計算を繰り返さなくても可能です。

if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
    do_stuff()

私はおそらくこれまたは他の解決策を関数に入れます。次に、コードで関数をきれいに呼び出すことができます。

個人的には、コードにもっと柔軟性が必要でない限り、質問の最初の方法を使用します。シンプルで効率的です。私はまだそれを関数に入れるかもしれません:

def two_add_to_third(a, b, c):
    return a + b == c or a + c == b or b + c == a

if two_add_to_third(a, b, c):
    do_stuff()

それはかなりPythonicであり、それを行うにはおそらく最も効率的な方法です(追加の関数呼び出しは別として)。とにかく、実際に問題を引き起こしていない限り、パフォーマンスについてあまり気にする必要はありません。


特に、a、b、cがすべて負でないと仮定できる場合。
cphlewis 2015

「いつもうまくいくとは限らない」というフレーズは少し混乱します。最初のソリューションは、数値が負でないことが確実にわかっている場合にのみ機能します。たとえば(a、b、c)=(-3、-2、-1)の場合、a + b!= cですが、b + c = aです。(-1、1、2)および(-2、-1、1)の場合も同様です。
usernumber

@usernumber、ご存知のとおり、私は以前に気づきました。なぜ修正しなかったのかわからない。
Cyphase 2015

OPの提案はすべての入力に対して機能するのに対し、上位のソリューションは入力の大きなクラスに対しては機能しません。「機能していない」のほうが「機能している」よりもPythonicの方が優れているのはなぜですか。
バリー

3
ああ、スナップ。「正の数値のみを扱っていることがわかっている場合、これは機能し、かなりクリーンです。」他のすべては任意の数値で機能しますが、正の数値のみを扱っていることがわかっている場合、一番上の数値は非常に読みやすい/ PythonのIMOです。
Cyphase 2015

17

3つの変数のみを使用する場合、最初の方法は次のとおりです。

a + b == c or a + c == b or b + c == a

すでに非常にpythonicです。

より多くの変数を使用することを計画しているなら、あなたの推論の方法は:

a + b + c in (2*a, 2*b, 2*c)

とても賢いですが、その理由を考えてみましょう。なぜこれが機能するのですか?
いくつかの簡単な算術を通して、次のことがわかります。

a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c

そして、これはイエスが、それが等しくなることを意味し、いずれかのA、B、またはCのための真の保持する必要があります2*a2*bまたは2*c。これは、任意の数の変数に当てはまります。

したがって、これをすばやく書くための良い方法は、変数のリストを用意し、その合計を2倍の値のリストと照合することです。

values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])

このように、方程式にさらに変数を追加するには、「n」個の方程式を記述するのではなく、「n」個の新しい変数で値リストを編集するだけです。


4
何についてa=-1b=-1c=-2、そしてa+b=c、しかし、a+b+c = -42*max(a,b,c)ある-2
エリックRenouf

本当にありがとうございます、私は腹筋を使用する必要があります。今その調整をしています。
ThatGuyRussell 2015

2
半ダースのabs()呼び出しでそれをペパリングした後、それはOPのスニペットよりもPythonicです(私は実際にはそれを大幅に読みにくくします)。
TigerhawkT3 2015

それは非常に本当です、私は今それを調整します
ThatGuyRussell

1
@ThatGuyRussell短絡させるには、ジェネレータを使用する必要があります...などのようにany(sum(values) == 2*x for x in values)、必要に応じて、前もってすべてのダブリングを行う必要はありません。
Barry

12

次のコードを使用すると、各要素を、その要素を除くリスト全体の合計から計算される他の要素の合計と繰り返し比較できます。

 l = [a,b,c]
 any(sum(l)-e == e for e in l)

2
ニース:) []2行目からブラケットを削除すると、元のように短絡することもあると思いますor...
psmears

1
これは基本的any(a + b + c == 2*x for x in [a, b, c])に、OPの提案にかなり近い
njzk2

それは似ていますが、このメソッドは任意の数の変数に拡張されます。ショートサーキットについて@psmearsの提案を取り入れました。
Arcanum、2015年

10

それを試して簡素化しないでください。代わりに、関数で何をしているのか名前を付けます。

def any_two_sum_to_third(a, b, c):
  return a + b == c or a + c == b or b + c == a

if any_two_sum_to_third(foo, bar, baz):
  ...

条件を「賢い」もので置き換えると短くなる可能性がありますが、読みやすくなるわけではありません。しかし、それをそのままにしておくことは、あまり読みにくいです。理由を知るのは難しいためです。この3つの条件をひと目で確認。これにより、何をチェックしているかが完全に明確になります。

パフォーマンスに関しては、このアプローチは関数呼び出しのオーバーヘッドを追加しますが、絶対に修正する必要があるボトルネックを見つけない限り、パフォーマンスの読みやすさを犠牲にすることはありません。また、状況に応じて一部の関数呼び出しを最適化してインライン化できる巧妙な実装があるため、常に測定してください。


1
関数は、同じコードを複数の場所で使用する場合、またはコードが複雑な場合にのみ記述してください。元の質問ではコードの再利用については触れられておらず、1行のコードの関数を記述することはやり過ぎであるだけでなく、実際には読みやすさを損ないます。
Igor Levicki 2015

5
FPスクールオブスクールから来たので、私はほとんど完全に同意せず、名前の付いた1行関数は、これまでにない読みやすさを向上させるための最良のツールの1つであると述べなければなりません。あなたが何かを取っている手順は、すぐに明快さを持っていない時はいつでも機能を作るどのような関数の名前は、あなたが指定することができますように、あなたがやっているより良いまだコメントができるより。
ジャック

どんな学校でも、盲目的に一連のルールを守ることは悪いことです。ソース内の別の部分にジャンプして、関数内に隠されているその1行のコードを読み取って、実際に名前で示されていることを実行していることを確認し、次に呼び出し先の場所に切り替える必要がある正しいパラメーターを渡すことを確認してください完全に不必要なコンテキストの切り替え。私の意見では、これを行うと可読性とワークフローの両方が損なわれます。最後に、関数の名前もコードのコメントも、コードド​​キュメントの適切な置き換えではありません。
Igor Levicki

9

Python 3:

(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...

任意の数の変数にスケーリングします。

arr = [a,b,c,d,...]
sum(arr)/2 in arr

ただし、一般に、4つ以上の変数がない限り、元のバージョンの方が読みやすいことに同意します。


3
これは、浮動小数点の丸めエラーのために、一部の入力に対して誤った結果を返します。
PTS

パフォーマンスと精度の理由から、除算は避けてください。
Igor Levicki 2015

1
@pts浮動小数点の丸めが原因で、実装が誤った結果を返さないのですか?a + b == c
osundblad

@osundblad:a、b、cがintの場合、(a + b + c)/ 2は丸めを行います(正しくない結果を返す可能性があります)が、a + b == cは正確です。
2015

3
2で除算すると、指数が1だけ減少するため、2 ^ 53(Pythonの浮動小数点数の小数部)未満の整数であれば正確になり、大きな整数の場合はdecimalを使用できます。たとえば、2 ^ 30未満の整数をチェックするには、実行[x for x in range(pow(2,30)) if x != ((x * 2)/ pow(2,1))]
Vitalii Fedorenko、2015

6
(a+b-c)*(a+c-b)*(b+c-a) == 0

2つの項の合計が3番目の項と等しい場合、因子の1つがゼロになり、積全体がゼロになります。


私はまったく同じことを考えていましたが、彼の元の提案の方がずっとクリーンであることは否定できません...
user541686

@Mehrdad-間違いなく。それは本当に違いはありません(a+b<>c) && (a+c<>b) && (b+c<>a) == false
mbeckish '19

乗算は、論理式や基本的な算術よりもコストがかかるだけです。
Igor Levicki 2015

@IgorLevicki-はい、ただしそれは非常に時期尚早な最適化の問題です。これは毎秒数万回実行されるのでしょうか?もしそうなら、あなたはおそらく何か他のものを見たいと思うでしょう。
mbeckish 2015

@mbeckish-なぜそれが時期尚早だと思いますか?コードは、後から考えて最適化するのではなく、最適化を念頭に置いて記述してください。ある日、一部のインターンがこのコードスニペットをコピーして、組み込みプラットフォームのパフォーマンスクリティカルループに貼り付けます。このループは、何百万ものデバイスで実行できますが、必ずしも動作が遅いとは限りませんが、おそらくより多くのバッテリー電力を浪費します。そのようなコードを書くことは、悪いコーディング慣行を助長するだけです。私の意見では、OPが尋ねるべきだったのは、その論理式を最適化する方法があるかどうかです。
Igor Levicki 2015

6

どうですか:

a == b + c or abs(a) == abs(b - c)

変数が符号なしの場合、これは機能しないことに注意してください。

コードの最適化の観点から(少なくともx86プラットフォームでは)、これは最も効率的なソリューションのようです。

最新のコンパイラーは、両方のabs()関数呼び出しをインライン化し、CDQ、XOR、およびSUB命令の巧妙なシーケンスを使用することにより、符号テストおよび後続の条件分岐を回避します。したがって、上記の高レベルコードは、低レイテンシ、高スループットのALU命令と2つの条件のみで表されます。


そして、私fabs()floatタイプに使用できると思います;)
shA.t 2015

4

Alex Vargaが提供するソリューション「a in(b + c、bc、cb)」はコンパクトで数学的に美しいですが、次の開発者がコードの目的をすぐに理解できないため、実際にはそのようにコードを記述しません。 。

マーク・ランサムのソリューション

any((a + b == c, a + c == b, b + c == a))

より明確ですが、それほど簡潔ではありません

a + b == c or a + c == b or b + c == a

他の誰かが見なければならないコードを書くとき、または私がそれを書いたときに私が考えていたことを忘れてしまったときに、後でずっと見なければならないコードを書くとき、短すぎるか賢いことは、善よりも害を及ぼす傾向があります。コードは読み取り可能でなければなりません。したがって、簡潔であるのは良いですが、次のプログラマが理解できないほど簡潔ではありません。


正直な質問:次のプログラマーがコードを読むことができないばか者であるといつも人々が仮定するのはなぜですか?私は個人的にその考えを侮辱します。すべてのプログラマーに露骨に明白になるようにコードを書く必要がある場合、それは、私たちが職業として、私たちの中で最もスキルの低い最低限の共通点に対応していることを意味します。もしそれを続けたら、彼らはどのようにして自分のスキルを向上させるでしょうか?これは他の職業では見られません。スキルレベルに関係なくすべてのミュージシャンが演奏できるように、作曲家が単純な楽譜を書いているのを最後に見たのはいつですか。
Igor Levicki 2015

6
問題は、プログラマーでさえ精神的エネルギーが限られていることです。そのため、限られた精神的エネルギーを、アルゴリズムとプログラムのより高いレベルの側面に費やしたいですか、それとも、より簡単に表現できる場合にコードの複雑な行が何を意味するかを理解することですか? ?プログラミングは難しいので、オリンピックランナーが重いバックパックを付けた状態でレースを実行できないのと同じように、不必要に自分を難しくしないでください。Steve McConellがCode Complete 2で述べているように、読みやすさはコードの最も重要な側面の1つです。
Paul J Abernathy

2

リクエストは、よりコンパクトな、またはよりパイソンのようなものです-私はもっとコンパクトで手を試してみました。

与えられた

import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
    return x + y == z

これはオリジナルより2文字少ない

any(g(*args) for args in f((a,b,c)))

テスト:

assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)

さらに、与えられた:

h = functools.partial(itertools.starmap, g)

これは同等です

any(h(f((a,b,c))))

まあ、それはオリジナルより2文字短いですが、OPが現在使用していると彼が言った直後にOPが与えたものではありません。オリジナルには多くの空白が含まれていますが、可能な限り省略しています。これが機能g()するために定義しなければならない関数の小さな問題もあります。それを考えると、かなり大きいと思います。
TigerhawkT3 2015

@ TigerhawkT3、それを短い式/行のリクエストとして解釈しました。さらなる改善については編集を参照してください。
wwii

4
非常に悪い関数名で、コードゴルフにのみ適しています。
0xc0de

@ 0xc0de-すみません、プレイしません。適切なものは、かなり主観的であり、状況に依存する可能性がありますが、私はコミュニティに委ねます。
wwii

元のコードよりも多くの文字がある場合、これがどのようにコンパクトになるかはわかりません。
Igor Levicki 2015

1

私は最もパイソン的な答えとして私が見るものを提示したいと思います:

def one_number_is_the_sum_of_the_others(a, b, c):
    return any((a == b + c, b == a + c, c == a + b))

最適化されていない一般的なケース:

def one_number_is_the_sum_of_the_others(numbers):
    for idx in range(len(numbers)):
        remaining_numbers = numbers[:]
        sum_candidate = remaining_numbers.pop(idx)
        if sum_candidate == sum(remaining_numbers):
            return True
    return False 

Zen of Pythonに関して、強調されたステートメントは他の回答よりもフォローされていると思います。

禅のPython、ティム・ピーターズ

醜いよりも美しい方がいいです。
明示的は暗黙的よりも優れています。
シンプルは複雑よりも優れています。
複雑は複雑よりも優れています。
ネストよりもフラットの方が優れています。
疎は密よりも優れています。
読みやすさが重要です。
特別なケースは、ルールを破るほど特別なものではありません。
実用性は純粋さを上回りますが。
エラーがサイレントに渡ることはありません。
明示的に沈黙させない限り。
あいまいな状況に直面して、推測する誘惑を拒否してください。
それを行うには、明白な方法が1つ(できれば1つだけ)あるはずです。
あなたがオランダ人でない限り、その方法は最初は明白ではないかもしれませんが。
今は決してないよりはましです。
多くの場合、今。
実装の説明が難しい場合は、悪い考えです。
実装が説明しやすい場合は、良いアイデアかもしれません。
名前空間は非常に魅力的なアイデアの1つです。もっと多くのことをしましょう。


1

私のプログラミングの古い習慣として、句の右側に複雑な式を配置すると、次のように読みやすくなると思います。

a == b+c or b == a+c or c == a+b

プラス()

((a == b+c) or (b == a+c) or (c == a+b))

また、複数行を使用することで、次のような意味も理解できると思います。

((a == b+c) or 
 (b == a+c) or 
 (c == a+b))

0

一般的には

m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();

入力変数の操作で問題がなければ、

c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();

ビットハックを利用したい場合は、「!」、「>> 1」、「<< 1」を使用できます。

除算は避けましたが、丸め誤差を避けるために2つの乗算を避けることができます。ただし、オーバーフローをチェックします


0
def any_sum_of_others (*nums):
    num_elements = len(nums)
    for i in range(num_elements):
        discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
        if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
            return True
    return False

print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False

関数は、同じコードを複数の場所で使用する場合、またはコードが複雑な場合にのみ記述してください。元の質問ではコードの再利用については触れられておらず、1行のコードの関数を記述することはやり過ぎであるだけでなく、実際には読みやすさを損ないます。
Igor Levicki 2015

読みやすさを損なうことには同意しません。適切な名前を選択すると、読みやすさが向上する場合があります(ただし、この回答で選択した名前の品質については説明しません)。さらに、コンセプトに名前を付けると便利です。関数に適切な名前を付けるように強制する限り、これを行う必要があります。機能は良いです。機能が機能にカプセル化されることから利益を得るのに十分複雑であるかどうかに関しては、それは主観的な判断です。
Hammerite 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.