次元 python リストに真理値が1つしかないことを確認するにはどうすればよいですか?




8 Answers

輸入を必要としないもの:

def single_true(iterable):
    i = iter(iterable)
    return any(i) and not any(i)

あるいは、もっと読みやすいバージョンかもしれません:

def single_true(iterable):
    iterator = iter(iterable)
    has_true = any(iterator) # consume from "i" until first true or it's exhuasted
    has_another_true = any(iterator) # carry on consuming until another true value / exhausted
    return has_true and not has_another_true # True if exactly one true found

この:

  • iは本当の価値があることを確認するために見える
  • 他の真の値が存在しないことを確認するために、イテラブル内のその点を見続ける
python

Pythonでは、真理値が1つだけでなければならないリストがあります(つまり、 bool(value) is True )。 これをチェックする巧妙な方法はありますか? 今、リスト全体を繰り返して、手動でチェックしています:

def only1(l)
    true_found = False
    for v in l:
        if v and not true_found:
            true_found=True
        elif v and true_found:
             return False #"Too Many Trues"
    return true_found

これは控えめで、非常に非常態ではないようです。 これを行うためのより洗練された方法がありますか?




短絡動作を保持する1行の答え:

from itertools import ifilter, islice

def only1(l):
    return len(list(islice(ifilter(None, l), 2))) == 1

これは、比較的早い時点で2つ以上の真の値を持つ非常に大きなイテラブルに対しては、他の選択肢よりもはるかに高速です。

ifilter(None, itr)は真理bool(x)要素のみを返すiterableを返しますbool(x)True返す場合はxTrue )。 islice(itr, 2)は、 itr最初の2つの要素のみをitr iterableをitrます。 これをリストに変換し、その長さが1に等しいことを確認することで、2つの要素を見つけた後に追加の要素をチェックする必要なしに、正確に1つのtruthy要素が存在することを検証できます。

タイミングの比較は次のとおりです。

  • セットアップコード:

    In [1]: from itertools import islice, ifilter
    
    In [2]: def fj(l): return len(list(islice(ifilter(None, l), 2))) == 1
    
    In [3]: def david(l): return sum(bool(e) for e in l) == 1
    
  • 短絡動作を示す:

    In [4]: l = range(1000000)
    
    In [5]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [6]: %timeit david(l)
    1 loops, best of 3: 194 ms per loop
    
  • 短絡が発生しない大きなリスト:

    In [7]: l = [0] * 1000000
    
    In [8]: %timeit fj(l)
    100 loops, best of 3: 10.2 ms per loop
    
    In [9]: %timeit david(l)
    1 loops, best of 3: 189 ms per loop
    
  • 小さなリスト:

    In [10]: l = [0]
    
    In [11]: %timeit fj(l)
    1000000 loops, best of 3: 1.77 us per loop
    
    In [12]: %timeit david(l)
    1000000 loops, best of 3: 990 ns per loop
    

だから、 sum()アプローチは非常に小さなリストでは高速ですが、入力リストが大きくなるにつれて、短絡が不可能であっても私のバージョンはより速くなります。 大きな入力で短絡が可能な場合、性能の違いは明らかです。




>>> l = [0, 0, 1, 0, 0]
>>> has_one_true = len([ d for d in l if d ]) == 1
>>> has_one_true
True



これはうまくいくと思われ、 listだけでなく、どのような繰り返し可能なものも処理できるはずです。 可能な限り短絡して効率を最大化します。 Python 2と3の両方で動作します。

def only1(iterable):
    for i, x in enumerate(iterable):  # check each item in iterable
        if x: break                   # truthy value found
    else:
        return False                  # no truthy value found
    for x in iterable[i+1:]:          # one was found, see if there are any more
        if x: return False            #   found another...
    return True                       # only a single truthy value found

testcases = [  # [[iterable, expected result], ... ]
    [[                          ], False],
    [[False, False, False, False], False],
    [[True,  False, False, False], True],
    [[False, True,  False, False], True],
    [[False, False, False, True],  True],
    [[True,  False, True,  False], False],
    [[True,  True,  True,  True],  False],
]

for i, testcase in enumerate(testcases):
    correct = only1(testcase[0]) == testcase[1]
    print('only1(testcase[{}]): {}{}'.format(i, only1(testcase[0]),
                                             '' if correct else
                                             ', error given '+str(testcase[0])))

出力:

only1(testcase[0]): False
only1(testcase[1]): False
only1(testcase[2]): True
only1(testcase[3]): True
only1(testcase[4]): True
only1(testcase[5]): False
only1(testcase[6]): False



if sum([bool(x) for x in list]) == 1

(あなたの値はすべてbooleanishであると仮定します)。

これはちょうどそれを合計するほうが速いでしょう

sum(list) == 1   

リストのデータ型によっては問題が発生することがあります。




def only1(l)
    sum(map(lambda x: 1 if x else 0, l)) == 1

説明: map関数は、リストを別のリストにマッピングし、 True => 1およびFalse => 0ます。 TrueまたはFalseの代わりに0と1のリストがあります。 今度はこのリストを単純に合計し、1の場合はTrue値が1つだけです。




これはあなたが探しているものですか?

sum(l) == 1



短絡がないものの、本当に何かのために働くべきものがあります。 私は、相互に排他的な議論を禁じるきれいな方法を探している間にそれを見つけました:

if sum(1 for item in somelist if item) != 1:
    raise ValueError("or whatever...")



Related


Tags

python