[python] 목록에 하나의 진실한 값만 있는지 어떻게 확인할 수 있습니까?

파이썬에서는 단 하나의 진실 된 값 (즉, 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

이것은 우아하지 않고 비단뱀처럼 보입니다. 이를 수행하는 더 현명한 방법이 있습니까?



답변

가장 장황한 해결책이 항상 가장 비범 한 해결책은 아닙니다. 따라서 약간의 수정을 추가합니다 (일부 중복 된 부울 평가를 저장하기 위해).

def only1(l):
    true_found = False
    for v in l:
        if v:
            # a True was found!
            if true_found:
                # found too many True's
                return False
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

비교를위한 몇 가지 타이밍은 다음과 같습니다.

# file: test.py
from itertools import ifilter, islice

def OP(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

def DavidRobinson(l):
    return l.count(True) == 1

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

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

def moooeeeep(l):
    true_found = False
    for v in l:
        if v:
            if true_found:
                # found too many True's
                return False
            else:
                # found the first True
                true_found = True
    # found zero or one True value
    return true_found

내 출력 :

$ python -mtimeit -s 'import test; l=[True]*100000' 'test.OP(l)'
1000000 loops, best of 3: 0.523 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.DavidRobinson(l)'
1000 loops, best of 3: 516 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.FJ(l)'
100000 loops, best of 3: 2.31 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.JonClements(l)'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'import test; l=[True]*100000' 'test.moooeeeep(l)'
1000000 loops, best of 3: 0.449 usec per loop

보시다시피 OP 솔루션은 여기에 게시 된 대부분의 다른 솔루션보다 훨씬 낫습니다. 예상대로 가장 좋은 것은 단락 동작이있는 사용자, 특히 Jon Clements가 게시 한 솔루션입니다. 적어도 True긴 목록 에 두 개의 초기 값이있는 경우 .

True값 이 전혀없는 경우에도 동일 합니다.

$ python -mtimeit -s 'import test; l=[False]*100000' 'test.OP(l)'
100 loops, best of 3: 4.26 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.DavidRobinson(l)'
100 loops, best of 3: 2.09 msec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.FJ(l)'
1000 loops, best of 3: 725 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.JonClements(l)'
1000 loops, best of 3: 617 usec per loop
$ python -mtimeit -s 'import test; l=[False]*100000' 'test.moooeeeep(l)'
100 loops, best of 3: 1.85 msec per loop

나는 통계적 유의성을 확인하지 않았지만 흥미롭게도 이번에는 FJ가 제안한 접근 방식, 특히 Jon Clements가 제안한 접근 방식이 다시 분명히 우수 해 보입니다.


답변

수입이 필요하지 않은 것 :

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

또는 더 읽기 쉬운 버전 :

def single_true(iterable):
    iterator = iter(iterable)

    # consume from "i" until first true or it's exhausted
    has_true = any(iterator)

    # carry on consuming until another true value / exhausted
    has_another_true = any(iterator)

    # True if exactly one true found
    return has_true and not has_another_true

이:

  • i진정한 가치가 있는지 확인 합니다.
  • 다른 실제 값이 없는지 확인하기 위해 반복 가능한 지점에서 계속 찾습니다.


답변

단순히 True값을 찾고 있는지 아니면 True논리적으로 평가되는 다른 값 ( 11또는 등 "hello")을 찾고 있는지에 따라 다릅니다 . 전자의 경우 :

def only1(l):
    return l.count(True) == 1

후자의 경우 :

def only1(l):
    return sum(bool(e) for e in l) == 1

새 목록을 만들지 않고도 단일 반복으로 계산과 변환을 모두 수행 할 수 있기 때문입니다.


답변

단락 동작을 유지하는 한 줄 답변 :

from itertools import ifilter, islice

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

상대적으로 초기에 두 개 이상의 실제 값을 갖는 매우 큰 이터 러블의 경우 여기에있는 다른 대안보다 훨씬 빠릅니다.

ifilter(None, itr)(단 truthy 요소를 얻을 것이라는 반복자를 제공 x하는 경우 truthy입니다 bool(x)반환 True). islice(itr, 2)의 처음 두 요소 만 산출하는 iterable을 제공합니다 itr. 이것을리스트로 변환하고 길이가 1인지 확인함으로써 우리는 두 개를 찾은 후 추가 요소를 확인할 필요없이 정확히 하나의 진실 요소가 존재하는지 확인할 수 있습니다.

다음은 몇 가지 타이밍 비교입니다.

  • 설정 코드 :

    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()매우 작은 목록의 경우 접근 방식이 더 빠르지 만 입력 목록이 커지면 단락이 불가능할 때도 내 버전이 더 빠릅니다. 큰 입력에서 단락이 가능하면 성능 차이가 분명합니다.


답변

나는 네크로맨서 배지를 받고 싶었 기 때문에 Jon Clements의 탁월한 답변을 일반화하여 단락 논리의 이점을 보존하고 모든 사람에게 빠른 술어 검사를 수행했습니다.

따라서 여기에 있습니다 :

N (참) = n

def n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n)) and not any(i)

N (참) <= n :

def up_to_n_trues(iterable, n=1):
    i = iter(iterable)
    all(any(i) for j in range(n))
    return not any(i)

N (참)> = n :

def at_least_n_trues(iterable, n=1):
    i = iter(iterable)
    return all(any(i) for j in range(n))

m <= N (참) <= n

def m_to_n_trues(iterable, m=1, n=1):
    i = iter(iterable)
    assert m <= n
    return at_least_n_trues(i, m) and up_to_n_trues(i, n - m)


답변

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


답변

넌 할 수있어:

x = [bool(i) for i in x]
return x.count(True) == 1

또는

x = map(bool, x)
return x.count(True) == 1

@JoranBeasley의 방법을 기반으로 구축 :

sum(map(bool, x)) == 1