((x == a and y == b)または(x == b and y == a))を表現するよりエレガントな方法はありますか?


108

((x == a and y == b) or (x == b and y == a))Pythonで評価しようとしていますが、少し冗長に見えます。よりエレガントな方法はありますか?


8
オブジェクトの種類に依存しますx,y, a,b:それらは整数/浮動小数点数/文字列、任意のオブジェクト、または何ですか?それらが組み込み型であり、両方x,ya,b並べ替えた順序で保持できる場合は、2番目のブランチを回避できます。セットを作成すると、4つの要素のそれぞれx,y, a,bがハッシュされることに注意してください。これは、オブジェクトの種類に完全に依存して、重要な場合とそうでない場合があります。
smci

18
一緒に仕事をしている人と、あなたが開発しているプロジェクトの種類を覚えておいてください。私はあちこちで少しPythonを使用しています。誰かがここで答えの1つをコーディングした場合、私は完全に何が起こっているのかグーグルする必要があります。あなたの条件文は、何があってもほとんど読めます。
Areks

3
代替品を気にすることはありません。それらはもっと簡潔ですが、明確ではありません(IMHO)。私はすべて遅くなると思います。
Barmar

22
これは、古典的なXYAB問題です。
Tashus、

5
そして一般に、順序に依存しないオブジェクトのコレクションを扱う場合は、sets / dicts / etcを使用します。これは本当にXYの問題であり、それが引き出されたより大きなコードベースを確認する必要があります。私((x == a and y == b) or (x == b and y == a))は不自然に見えるかもしれないことに同意しますが、1)その意図は非常に明確で、Python以外のすべてのプログラマーにとって理解可能であり、不可解ではありません。代替。したがって、「よりエレガント」には深刻な欠点もあります。
smci

回答:


151

要素がハッシュ可能である場合は、セットを使用できます。

{a, b} == {y, x}

18
@Grahamいいえ、そうではありません。右側のセットにちょうど2つのアイテムがあるからです。両方がaの場合、bはありません。両方がbの場合、aはありません。どちらの場合も、セットを等しくすることはできません。
ホブ、

12
一方、両側に3つの要素があり、それらが1対1で一致するかどうかをテストする必要がある場合、セットは機能しません。{1, 1, 2} == {1, 2, 2}。その時点で、sortedまたはが必要Counterです。
user2357112はモニカの

11
私はこれを読むのが難しいと思います( "{}"を "()"として読まないでください)。それをコメントするのに十分です-そして、目的は失われます。
エドゥアール

9
私はそれがpythonであることを知っていますが、値を比較するためだけに2つの新しいセットを作成することは私にはやり過ぎのようです...それを行う関数を定義し、必要なときに呼び出すだけです。
marxin

5
@marxin関数は、2つの単純なセット構築よりもさらに過剰になります。また、4つの引数を指定すると読みにくくなります。
グローアイ

60

あなたが得ることができる最高のことは、それらをタプルにパッケージ化することです:

if (a, b) == (x, y) or (a, b) == (y, x)

または、それをセットルックアップでラップするかもしれません

if (a, b) in {(x, y), (y, x)}

いくつかのコメントで言及されたばかりなので、いくつかのタイミングを実行しました。検索が失敗した場合、タプルとセットは同じように動作するように見えます。

from timeit import timeit

x = 1
y = 2
a = 3
b = 4

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
32.8357742

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
31.6169182

検索が成功した場合、タプルは実際には高速ですが、

x = 1
y = 2
a = 1
b = 2

>>> timeit(lambda: (a, b) in {(x, y), (y, x)}, number=int(5e7))
35.6219458

>>> timeit(lambda: (a, b) in ((x, y), (y, x)), number=int(5e7))
27.753138700000008

私はメンバーシップのルックアップを行っているため、セットを使用することを選択しました。概念的には、セットはタプルよりもそのユースケースに適しています。特定のユースケースで2つの構造の有意差を測定した場合は、より速いものを使用します。ここではパフォーマンスは重要ではないと思います。


12
タプルメソッドはとてもきれいに見えます。セットを使用することによるパフォーマンスへの影響が気になります。でもできif (a, b) in ((x, y), (y, x))ますか?
ブリリアント

18
@Brilliandパフォーマンスへの影響が心配な場合、Pythonはあなたのための言語ではありません。:-D
Sneftel

最初の方法、2つのタプルの比較が本当に好きです。解析は簡単で(一度に1つの句)、各句は非常に単純です。その上、それは比較的効率的でなければなりません。
Matthieu M.

set@Brilliandからのタプルソリューションへの回答でソリューションを好む理由はありますか?
user1717828

セットはオブジェクトがハッシュ可能であることを要求するので、タプルは依存性の少ないより一般的なソリューションになります。パフォーマンスは、一般的には重要ではないかもしれませんが、高価なハッシュと等価性チェックを伴う大きなオブジェクトを処理する場合にも非常に異なって見える場合があります。
デュケリング

31

タプルはそれを少し読みやすくします:

(x, y) == (a, b) or (x, y) == (b, a)

これは手掛かりを与えます:シーケンスx, yがシーケンスと等しいかどうかをチェックしていますが、順序はa, b無視しています。平等を設定するだけです!

{x, y} == {a, b}

,リストではなくタプルを作成します。これ(x, y)(a, b)同じタプル、あるx, ya, b
kissgyorgy

「リスト」とは、「要素の順序付けされたシーケンス」という意味であり、Python listタイプの意味ではありません。確かにこれは混乱を招くため編集されました。
トーマス

26

アイテムがハッシュ可能ではないが、順序の比較をサポートしている場合は、次のことを試してみてください。

sorted((x, y)) == sorted((a, b))

これはハッシュ可能なアイテムでも機能するので(そうですか?)、よりグローバルなソリューションです。
カールウィトフト

5
@CarlWitthoft:いいえcomplex。たとえば、ハッシュ可能でソートできないタイプがあります。
dan04

26

私の意見では、最もエレガントな方法は

(x, y) in ((a, b), (b, a))

これは、{a, b} == {y, x}他の回答で示されているように、セットを使用するよりも優れた方法です。なぜなら、変数がハッシュ可能かどうかを考える必要がないからです。


これは以前の回答とどう違うのですか?
scohe001

5
@ scohe001これは、以前の回答がセットを使用するタプルを使用しています。その以前の回答ではこのソリューションを検討していましたが、推奨としてリストすることを拒否しました。
ブリリアント

25

これらが数値の場合は、を使用できます(x+y)==(a+b) and (x*y)==(a*b)

これらが同等のアイテムである場合は、を使用できますmin(x,y)==min(a,b) and max(x,y)==max(a,b)

しかし((x == a and y == b) or (x == b and y == a))、明確で、安全で、より一般的です。


2
はは、対称多項式ftw!
Carsten S

2
これにより、オーバーフローエラーのリスクが生じると思います。
Razvan Socol

3
これが正解、特に最後の文です。単純にしてください。これには、複数の新しいイテラブルやそのようなものは必要ありません。セットを使用したい場合は、セットオブジェクトの実装を確認しこれをタイトループで実行しようとしていると想像してください...
Z4-tier

3
@RazvanSocol OPは、データ型が何であるかを述べていませんでした。この回答は、型に依存するソリューションを限定します。
Z4ティア

21

3つ以上の変数の一般化として、使用できますitertools.permutations。それはの代わりです

(x == a and y == b and z == c) or (x == a and y == c and z == b) or ...

我々は書ける

(x, y, z) in itertools.permutations([a, b, c])

そしてもちろん、2つの変数バージョン:

(x, y) in itertools.permutations([a, b])

5
すばらしい答えです。一度に1つの順列しか作成されず、「in」チェックが停止してすぐにTrueが返されるため、これは非常にメモリ効率が良いことをここで指摘する価値があります(以前にジェネレーターであまり行ったことがない人)。一致が見つかりました。
robertlayton

2
この方法の複雑さは次のとおりであることも指摘する価値がありO(N*N!)ます。11変数の場合、これが完了するまでに1秒以上かかることがあります。(私はより高速なメソッドを投稿しましたが、それでもを受け取りO(N^2)、1万個の変数で1秒を占有し始めます。したがって、これは高速または一般的に(書き込み可能ハッシュ可能性/順序付け可能)のどちらかであるようですが、両方ではありません:P)
Aleksi Torhamo

15

タプルを使用してデータを表し、次のようにセットが含まれているかどうかを確認できます。

def test_fun(x, y):
    test_set = {(a, b), (b, a)}

    return (x, y) in test_set

3
ワンライナーとして記述され、リスト(ハッシュ不可のアイテムの場合)を使用して、これが最良の答えであると考えます。(私は完全なPython初心者ですが)。
エドゥアール

1
@Édouardさて、本質的には別の答えがあります(リストの代わりにタプルを使用すると、とにかく効率的です)。
ブリリアント、

10

あなたはすでに最も読みやすいソリューションを手に入れました。これを表現する方法は他にもありますが、文字数は少ないかもしれませんが、読むのは簡単ではありません。

値が実際に最善の方法を表すかどうかに応じて、話す名前のある関数でチェックラップします。別の方法として、またはそれに加えて、専用の上位クラスオブジェクトでオブジェクトx、y、a、bをモデル化して、クラス等価チェックメソッドまたは専用のカスタム関数で比較のロジックと比較できます。


3

OPは2つの変数の場合のみに関係しているようですが、StackOverflowは後で同じ質問を検索する人のためのものでもあるので、ここで一般的なケースに少し詳しく取り組みます。以前の1つの回答にはすでにを使用した一般的な回答が含まれていますitertools.permutations()が、その方法では、アイテムごとに順列O(N*N!)があるため、比較が行わN!Nます。(これがこの回答の主な動機でした)

最初に、ここで提示した方法の動機として、以前の回答のいくつかの方法が一般的なケースにどのように適用されるかを要約します。私はAを参照し(x, y)Bを参照するために使用します。(a, b)これは、任意の長さ(ただし同じ長さ)のタプルにすることができます。

set(A) == set(B)高速ですが、値がハッシュ可能である場合にのみ機能し、タプルの1つに重複する値が含まれていないことを保証できます。(たとえば{1, 1, 2} == {1, 2, 2}、@ Daniel Mesejoの回答の下で@ user2357112によって指摘されたように)

以前の方法は、セットではなくカウント付きのディクショナリを使用することで、重複する値を処理するように拡張できます(これには、すべての値をハッシュ可能にする必要があるという制限があるため、たとえば、変更可能な値listは機能しません)。

def counts(items):
    d = {}
    for item in items:
        d[item] = d.get(item, 0) + 1
    return d

counts(A) == counts(B)

sorted(A) == sorted(B)ハッシュ可能な値は必要ありませんが、少し遅く、代わりに順序可能な値が必要です。(つまり、complex動作しません)

A in itertools.permutations(B)ハッシュ可能または順序付け可能な値は必要ありませんが、すでに述べたように、O(N*N!)複雑であるため、11項目しかなくても、完了するまでに1秒以上かかることがあります。

それで、一般的になる方法はありますが、それはかなり速くなりますか?なぜそうなのか、各アイテムの量が同じであることを「手動で」チェックすることによって(これは複雑であるO(N^2)ため、これは大規模な入力にも適していません。私のマシンでは、1万個のアイテムが1秒以上かかることがありますが、 10項目のような小さい入力、これは他のものと同じくらい高速です)

def unordered_eq(A, B):
    for a in A:
        if A.count(a) != B.count(a):
            return False
    return True

最高のパフォーマンスを得るには、dict最初に-basedメソッドを試して、sortedハッシュ化できない値が原因で失敗した場合は-basedメソッドにフォールバックし、count順序付けできない値が原因で失敗した場合は最後に-basedメソッドにフォールバックすることをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.