「aまたはbまたはcのすべてではない場合」のPython構文


130

ゼロまたは3つのコマンドライン引数を受け取ることができるpythonスクリプトがあります。(デフォルトの動作で実行するか、3つの値すべてを指定する必要があります。)

次のようなものの理想的な構文は何ですか?

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a):


4
多分 `if len(sys.argv)== 0のようなものから始めます:
Edgar Aroutiounian

6
@EdgarAroutiounian len(sys.argv)は常に少なくとも1になりますargv[0]。実行可能ファイルはとして含まれます。
RoadieRich 2013年

10
質問の本文が質問のタイトルと一致しません。「aまたはbまたはcのすべてではない」または「a、b、cの1つだけの場合」(指定した式と同じ)をチェックしますか?
Doug McClean 2013年

2
a + b + cについて何と言えますか?
gukoff 2013年

6
ちょっと待ってください。引数は0または3です。if not (a and b and c)(argsはゼロ)、そしてif a and b and c(argsは3つすべて)と言ってくれませんか?
2013年

回答:


236

最小限のフォームの場合は、次のようにします。

if (not a or not b or not c) and (a or b or c):

質問のタイトルを翻訳します。

更新:VolatilityとSuprによって正しく述べられているように、De Morganの法則を適用して同等のものを取得できます。

if (a or b or c) and not (a and b and c):

私や、他のプログラマにとってより重要な形式を使用することをお勧めします。1つ目は「何かがあるが、何かあること」も意味し、2つ目は「何かがあるが、すべてではない」という意味です。ハードウェアでこれを最適化または実行する場合は、2番目を選択します。ここでは、最も読みやすいものを選択します(テストする条件とその名前も考慮に入れます)。最初を選びました。


3
すべての素晴らしい答えですが、これは簡潔さのために優れた短絡をもたらします。皆さんありがとう!
クリスウィルソン

38
私はそれをさらに簡潔にして行きますif not (a and b and c) and (a or b or c)
ボラティリティ2013年

208
またはif (a or b or c) and not (a and b and c)、タイトルと完全に一致させることもできます;)
Supr

3
@HennyH私は、質問が「少なくとも1つの条件は真であるが、すべてではない」を求めていると考えています。
ボラティリティ2013年

63
@Suprif any([a,b,c]) and not all([a,b,c])
eternalmatt

238

どうですか:

conditions = [a, b, c]
if any(conditions) and not all(conditions):
   ...

その他のバリエーション:

if 1 <= sum(map(bool, conditions)) <= 2:
   ...

2
sum(conditions)2たとえば、これらのいずれかが返された場合、エラーになりますTrue
eumiro 2013年

7
本当に醜いものが必要でしょうsum(map(bool, conditions))
jamylak 2013年

5
すべての条件が事前に評価されているため、これは短絡ではないことに注意してください。
georg 2013年

14
@PaulScheltema最初のフォームは誰にとってもより理解しやすいです。
cmh 2013年

6
この「任意およびすべてではないが、」ただ存在と「truthy」は引数であることargの重要な違いを意識すること、boolean型メソッドの最善かつ最も明確である
WIM

115

この質問にはすでに非常に賛成の回答と受け入れられた回答がたくさんありましたが、ブール問題を表現するさまざまな方法に気を取られて、重要なポイントを逃しました:

ゼロまたは3つのコマンドライン引数を受け取ることができるpythonスクリプトがあります。(デフォルトの動作で実行するか、3つの値すべてを指定する必要があります)

このロジックは、そもそもコードの責任ではなく、argparseモジュールで処理する必要があります。複雑なifステートメントを作成する必要はありません。代わりに、次のような引数パーサーを設定することをお勧めします。

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser()
parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z'])
args = parser.parse_args()
print(args.foo)

そして、はい、それは結局のところオプションなので、位置引数ではなくオプションであるべきです


編集: コメントでのLarsHの懸念に対処するために、位置引数が3または0のインターフェイスが必要であることが確実である場合に、それを記述する方法の例を以下に示します。オプションの引数は optionsでなければならないため、以前のインターフェースの方がスタイルが良いと私は考えていますが、ここでは完全性のために代替のアプローチを示します。usageパーサーを作成するときは、上書きするkwargに注意してくださいargparse。そうしないと、誤解を招く使用方法のメッセージが自動生成されます。

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\n')
parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z'])
args = parser.parse_args()
if len(args.abc) != 3:
  parser.error('expected 3 arguments')
print(args.abc)

以下に使用例をいくつか示します。

# default case
wim@wim-zenbook:/tmp$ ./three_or_none.py 
['x', 'y', 'z']

# explicit case
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']

# example failure mode
wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments

4
はい、意図的に追加しました。引数を定位置にして、正確に3または0が消費されるように強制することは可能ですが、これは良いCLIにはならないため、お勧めしません。
2013年

8
別の問題。あなたはそれが良いCLIであるとは信じておらず、その点について議論することができ、OPは説得されるかもしれません。しかし、あなたの答えは、仕様の変更について言及する必要があるほど十分に逸脱しています。変更に言及せずに、使用可能なツールに合わせて仕様を曲げているようです。
LarsH

2
@LarsHわかりました。質問に含まれている元のインターフェースに適合する例を追加しました。現在、利用可能な仕様を満たすためにツールを曲げています...;)
wim

2
これが私が賛成した唯一の答えです。本当の質問に答えるための+1 。
Jonathon Reinhart 2013年

1
+1。CLIの形式は重要な接線の問題であり、別の人が言ったように完全に分離しているわけではありません。私はあなたの投稿と他の投稿に賛成しました-あなたの投稿は問題の根源になり、エレガントな解決策を提供しますが、他の投稿は文字通りの質問に答えます。そして、どちらの種類の回答も役に立ち、+ 1に値します。
ベン・リー

32

私は行きます:

conds = iter([a, b, c])
if any(conds) and not any(conds):
    # okay...

これはかなり効率的に短絡するはずだと思います

説明

condsイテレータを作成することにより、の最初の使用はany短絡し、アイテムがtrueの場合はイテレータが次の要素を指すようにします。それ以外の場合は、リスト全体を消費してになりますFalse。次anyはイテラブルの残りのアイテムを取り、他の真の値がないことを確認します...存在する場合、ステートメント全体が真になることはできないため、一意の要素が1つありません(したがって、短絡再び)。ラストanyは戻るFalseか、イテラブルを使い果たしてTrue

注:上記は単一の条件のみが設定されているかどうかをチェックします


1つ以上の項目が設定されているかどうかを確認したいが、すべての項目が設定されていない場合は、以下を使用できます。

not all(conds) and any(conds)

5
わかりません。それは次のようになります:TrueでTrueでない場合。理解してください。
rGil 2013年

1
@rGil:「一部のリンゴが赤で、一部が赤くない場合」のように読みます-「一部のリンゴは赤ですが、すべてではない」と言うのと同じです。
georg 2013年

2
説明を書いても動作を理解できません... 期待される出力はありますが、[a, b, c] = [True, True, False]あなたのコードは "prints"すべきではありませんか?FalseTrue
awesoon 2013年

6
これはかなり賢い方法ですが、事前に設定した条件の数がわからない場合はこのアプローチを使用しますが、条件付きの既知の固定リストでは、読みやすさを損なうだけの価値はありません。
ふわふわした

4
これは短絡しません。リストは、に渡される前に完全に作成されiterます。anyそしてall、リストを遅延消費します、真ですが、リストはそこに着くまでにすでに完全に評価されています!
icktoofay 2013年

22

英語の文:

「aまたはbまたはcであるが、すべてではない場合」

このロジックに変換します。

(a or b or c) and not (a and b and c)

「but」という単語は通常、接続詞、つまり「and」を意味します。さらに、「それらすべて」は、条件の組み合わせに変換されます。この条件その条件、およびその他の条件です。「not」は、その接続全体を反転します。

受け入れられた答えに同意しません。著者は仕様に最も簡単な解釈を適用することを怠り、式を単純化して少数の演算子に簡略化するためにドモルガンの法則を適用することを怠った:

 not a or not b or not c  ->  not (a and b and c)

答えは「最小限の形」であると主張しながら。


実際には、そのフォームは最小限です。それはだ、最小限のPoSフォームの発現のため。
Stefano Sanfilippo 2013年

10

これはTrue、3つの条件のうち1つだけがの場合に返されますTrue。たぶん、あなたがあなたのサンプルコードで望んだことでしょう。

if sum(1 for x in (a,b,c) if x) == 1:

@defuzの答えほどきれいではありません
jamylak

10

について:(ユニークな状態)

if (bool(a) + bool(b) + bool(c) == 1):

2つの条件も許可すると、それを実行できることに注意してください。

if (bool(a) + bool(b) + bool(c) in [1,2]):

1
記録として、質問には2つの条件が求められます。すべてではないが少なくとも1つ=すべての1つまたはすべての2つ
MariusBalčytis2013年

私見あなたは2番目のものをと綴るべき1 <= bool(a) + bool(b) + bool(c) <= 2です。
モニカ

6

明確にするために、どのくらいのパラメーターが論理的にTRUEであるか(ストリング引数の場合-空ではない)に基づいて決定したいですか?

argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)

次に、あなたは決定を下しました:

if ( 0 < argsne < 3 ):
 doSth() 

ロジックがより明確になりました。


5

そして、なぜそれらを数えないのですか?

import sys
a = sys.argv
if len(a) = 1 :  
    # No arguments were given, the program name count as one
elif len(a) = 4 :
    # Three arguments were given
else :
    # another amount of arguments was given

5

少し不可解なことを気にしない場合は、1〜2個のtrueステートメントがある場合はfalse 0 < (a + b + c) < 3が返されtrue、すべてがfalseの場合、またはfalseがない場合はfalse が返されます。

また、関数を使用してブールを評価する場合、変数を1回だけ評価するので、これは簡略化されます。つまり、関数をインラインで記述でき、変数を一時的に格納する必要がありません。(例:0 < ( a(x) + b(x) + c(x) ) < 3。)


4

質問では、3つの引数すべて(aとbとc)が必要か、どれも必要ないか(aまたはbまたはc)は必要ないと述べています

これは与える:

(aおよびbおよびc)またはない(aまたはbまたはc)


4

私が理解しているように、3つの引数を受け取る関数がありますが、受け取らない場合はデフォルトの動作で実行されます。1つまたは2つの引数を指定したときに何が起こるかについては説明していなかったので、デフォルトの動作を行うだけであると想定します。その場合、次の答えが非常に有利だと思います。

def method(a=None, b=None, c=None):
    if all([a, b, c]):
        # received 3 arguments
    else:
        # default behavior

ただし、1つまたは2つの引数を異なる方法で処理する場合は、次のようにします。

def method(a=None, b=None, c=None):
    args = [a, b, c]
    if all(args):
        # received 3 arguments
    elif not any(args):
        # default behavior
    else:
        # some args (raise exception?)

注:これは、 " False"値がこのメソッドに渡されないことを前提としています。


引数の真理値をチェックすることは、引数が存在するかどうかをチェックすることとは別の問題です
wim

@wimだから、あなたの答えに合うように質問を変換しています。argparseモジュールは質問とは何の関係もありません。別のインポートを追加します。OPがargparseをまったく使用する予定がない場合、それはそれらをまったく助けません。また、「スクリプト」がスタンドアロンではなく、モジュール、またはコードのより大きなセット内の関数である場合、すでに引数パーサーがあり、そのより大きなスクリプト内のこの特定の関数をデフォルトまたはカスタマイズできます。OPからの情報が限られているため、メソッドの動作はわかりませんが、OPがブール値を渡していないと想定しても安全です。
インバーローズ2013

「ゼロまたは3つのコマンドライン引数を受け取ることができるpythonスクリプトがある」と質問は明確に述べ、「3つの引数を受け取る関数がある」とは言いませんでした。argparseモジュールは、Pythonでコマンドライン引数を処理するための推奨される方法であるため、自動的に質問に関係するすべてのことを行います。最後に、Pythonは「バッテリーが含まれている」-そのモジュールが標準ライブラリの一部である場合、「別のインポートを追加する」ことの欠点はありません。
2013年

@wim質問は非常に不明確です(たとえば、ボディがタイトルと一致しません)。質問は不明瞭で、これをいくつかの解釈に対して有効な回答にすることができます。
2013

2

条件のイテレータを使用すると、アクセスが遅くなる可能性があります。ただし、各要素に複数回アクセスする必要はなく、常にすべてを読み取る必要はありません。無限のジェネレーターで機能するソリューションは次のとおりです。

#!/usr/bin/env python3
from random import randint
from itertools import tee

def generate_random():
    while True:
        yield bool(randint(0,1))

def any_but_not_all2(s): # elegant
    t1, t2 = tee(s)
    return False in t1 and True in t2 # could also use "not all(...) and any(...)"

def any_but_not_all(s): # simple
    hadFalse = False
    hadTrue = False
    for i in s:
        if i:
            hadTrue = True
        else:
            hadFalse = True
        if hadTrue and hadFalse:
            return True
    return False


r1, r2 = tee(generate_random())
assert any_but_not_all(r1)
assert any_but_not_all2(r2)

assert not any_but_not_all([True, True])
assert not any_but_not_all2([True, True])

assert not any_but_not_all([])
assert not any_but_not_all2([])

assert any_but_not_all([True, False])
assert any_but_not_all2([True, False])

0

すべての指定boolがの場合True、またはすべての指定boolFalse...の場合
彼らはすべて等しくなっています!

だから、私たちは異なると評価された二つの要素を見つける必要があるbool
少なくとも一つがあることを知っているTrueと、少なくとも一つをFalse

私の短い解決策:

not bool(a)==bool(b)==bool(c)

私は、私の知る限り原因、短絡、それを信じてa==b==c等号a==b and b==c

私の一般的な解決策:

def _any_but_not_all(first, iterable): #doing dirty work
    bool_first=bool(first)
    for x in iterable:
        if bool(x) is not bool_first:
            return True
    return False

def any_but_not_all(arg, *args): #takes any amount of args convertable to bool
    return _any_but_not_all(arg, args)

def v_any_but_not_all(iterable): #takes iterable or iterator
    iterator=iter(iterable)
    return _any_but_not_all(next(iterator), iterator)

複数のイテラブルを処理するコードもいくつか記述しましたが、無意味だと思うので、ここから削除しました。ただし、ここでは引き続き利用できます


-2

これは基本的に「一部(すべてではない)」の機能です(any()およびall()組み込み関数)。

これは、結果にFalses True s があるべきであることを意味します。したがって、次のことができます。

some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False))

# one way to test this is...
test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412

# Some test cases...
assert(some(()) == False)       # all() is true, and any() is false
assert(some((False,)) == False) # any() is false
assert(some((True,)) == False)  # any() and all() are true

assert(some((False,False)) == False)
assert(some((True,True)) == False)
assert(some((True,False)) == True)
assert(some((False,True)) == True)

このコードの利点の1つは、結果の(ブール値)アイテムを1回繰り返すだけでよいことです。

1つの欠点は、これらすべての真理式が常に評価され、/ 演算子のように短絡しないことです。orand


1
これは不必要な複雑化だと思います。なぜ普通の古いセットの代わりにfrozensetなのですか?.issuperset長さ2をチェックするだけでなく、boolとにかくTrueとFalse以外を返すことができないのはなぜですか。単にdefを使用するのではなく、ラムダ(read:anonymous function)を名前に割り当てるのはなぜですか?
2013年

1
ラムダ構文は、一部の人にとってより論理的です。をreturn使用する場合に必要なため、いずれにしても同じ長さですdef。このソリューションの一般性はいいと思います。ブール値に限定する必要はありません。本質的には、「リストにこれらの要素がすべて含まれていることをどのように確認するか」です。なぜset可変性が必要ないのですか?パフォーマンスが必要ない場合は、常に不変性が優れています。
Janus Troelsen

@JanusTroelsenターゲット通りです!これらが私がこのようにした理由のいくつかです。それは私にそれをより簡単かつ明確にします。私はPythonを私のコーディング方法に適応させる傾向があります:-)。
アバフェイ2013年

しかし、無限のジェネレータでは機能しません:P私の回答を参照してください:)ヒント:tee
Janus Troelsen

@JanusTroelsen私はこれを実現します:-)。私は実際には最初にそれを逆にしました(セットにTrue / Falseを、メソッドのパラメーターに反復可能を使用)が、これは無限ジェネレーターでは機能せず、ユーザーが気付かない可能性があることに気付きました(この事実は(まだ)反復可能なset method paramsのドキュメントで言及されています)、そして少なくともこのように、無限の反復子をとらないことは明らかです。私は知っていましたがitertools.tee、1)コピー貼り付けを正当化するのに十分なほど単純で小さいワンライナーを探していました、2)そのテクニックを使用する回答をすでに提供していました:-)
Abbafei
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.