私はゼロまたは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):
?
あなたが最小の形式を意味するならば、これと行きなさい:
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):
私にとってのアドバイスは、あなたにとっても他のプログラマーにとってもより重要な形式を使うことです。最初のものは"偽のものもあるが真のものもある"、2番目の"真のものはあるがすべてではない"を意味する。私がハードウェアでこれを最適化または実行するのであれば、2番目を選択します。ここでは最も読みやすいものを選択します(テストする条件とその名前も考慮に入れて)。私は最初を選びました。
どうですか?
conditions = [a, b, c]
if any(conditions) and not all(conditions):
...
その他の亜種
if 1 <= sum(map(bool, conditions)) <= 2:
...
この質問にはすでに多くの支持された回答と承認された回答がありましたが、これまでのところそれらはすべてブール問題を表現するためのさまざまな方法に気を取られ重要なポイントを逃しました。
私はゼロまたは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位置指定引数。 オプション引数はオプションであるべきだから、私は前のインターフェースがより良いスタイルであると思うのですが、これは別の方法です:完全のために。 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
私は行きます:
conds = iter([a, b, c])
if any(conds) and not any(conds):
# okay...
私はこれがかなり効率的に短絡するべきだと思います
説明
conds
をイテレータにすることによって、any
を最初に使用するときには短絡して、項目がtrueの場合はイテレータは次の要素を指すようになります。そうでなければ、それはリスト全体を消費してFalse
になります。次のany
はイテラブルの残りの項目を取り、他の真の値がないことを確認します。もしあれば、ステートメント全体が真となることはできません。再び回路)。最後のany
はFalse
を返すか、イテラブルを使い果たしてTrue
になります。
注:上記は条件が1つだけ設定されているかどうかをチェックします
1つ以上の項目がすべての項目に設定されているわけではないかどうかを確認したい場合は、次のようにします。
not all(conds) and any(conds)
英語の文:
「aまたはbまたはcだがすべてではない場合」
このロジックに変換されます。
(a or b or c) and not (a and b and c)
「しかし」という言葉は通常、接続詞、つまり「and」を意味します。さらに、「それらのすべて」は条件の接続詞に変換されます。この条件、andその条件、andその他の条件です。 "not"はその接続詞全体を逆にします。
私は受け入れられた答えに同意しません。作者は仕様に最も直接的な解釈を適用することを怠り、少数の演算子に表現を単純化するためにDe Morganの法則を適用することを怠った。
not a or not b or not c -> not (a and b and c)
答えは「最小限の形」であると主張しながら。
3つの条件のうち1つだけがTrue
である場合、これはTrue
を返します。おそらくあなたのサンプルコードに欲しいものがあります。
if sum(1 for x in (a,b,c) if x) == 1:
どうですか?(固有の条件)
if (bool(a) + bool(b) + bool(c) == 1):
注意してください、あなたも2つの条件を許可するなら、あなたはそれをすることができます
if (bool(a) + bool(b) + bool(c) in [1,2]):
明確にするために、あなたはどのくらいのパラメータが論理的に真であるかに基づいてあなたの決定をしたいです(文字列引数の場合 - 空ではない)?
argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0)
それからあなたは決断をしました:
if ( 0 < argsne < 3 ):
doSth()
今論理はより明確です。
そしてなぜそれらを数えないのですか?
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
少し戸惑うことを気にしないのであれば、0 < (a + b + c) < 3
を使って単純にロールバックできます。1〜2つのtrueステートメントがある場合はtrue
を返し、すべてfalseまたはない場合はfalseを返します。
これは、変数を1回だけ評価するため、ブールを評価するために関数を使用する場合も単純化されます。つまり、関数をインラインで記述でき、変数を一時的に格納する必要がないということです。 (例:0 < ( a(x) + b(x) + c(x) ) < 3
)
私がそれを理解しているように、あなたは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
"の値がこのメソッドに渡されないことを前提としています。
質問には、3つの引数すべて(aとbとc)、またはいずれも必要ない(aまたはbとc)のいずれも必要ないと記載されています。
これは与える:
(aとbとc)かどうか(aかbかc)
もしあなたが条件のイテレータを使って作業していると、アクセスが遅くなるかもしれません。しかし、各要素に複数回アクセスする必要はなく、またすべての要素を読む必要もありません。これは無限の発電機で働く解決策です:
#!/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])
指定されたすべてのbool
がTrue
の場合、または指定されたすべてのbool
がFalse
...の場合.
それらはすべて等しいものです。
そのため、異なるbool
sに評価される2つの要素を見つける必要があります。
少なくとも1つのTrue
と少なくとも1つのFalse
があることを知るため。
not bool(a)==bool(b)==bool(c)
私はそれが短絡していると信じています、故にAFAIK 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)
複数のイテラブルを扱うコードも書いたが、無意味だと思うのでここから削除した。しかし、まだ利用可能です ここ 。