목록에 하나의 진실한 값만 있는지 어떻게 확인할 수 있습니까?
파이썬에서는 단 하나의 진실한 값 (즉, 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)
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
진정한 가치가 있는지 확인 합니다.- 다른 실제 값이 없는지 확인하기 위해 반복 가능한 지점에서 계속 찾습니다.
단순히 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
하나만있는 경우 s True
의 길이는 True
1이어야합니다.
def only_1(l): return 1 == len(filter(None, l))
이것은 작동하는 것처럼 보이며 list
s 뿐만 아니라 반복 가능한 모든 것을 처리 할 수 있어야합니다 . 효율성을 극대화하기 위해 가능할 때마다 단락됩니다. 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
(모든 값이 부울이라고 가정합니다.)
합산하는 것이 더 빠를 것입니다.
sum(list) == 1
목록의 데이터 유형에 따라 몇 가지 문제가 발생할 수 있습니다.
@ JonClements` 솔루션 은 최대 N 개의 True 값으로 확장되었습니다 .
# Extend any() to n true values
def _NTrue(i, n=1):
for x in xrange(n):
if any(i): # False for empty
continue
else:
return False
return True
def NTrue(iterable, n=1):
i = iter(iterable)
return any(i) and not _NTrue(i, n)
편집 : 더 나은 버전
def test(iterable, n=1):
i = iter(iterable)
return sum(any(i) for x in xrange(n+1)) <= n
EDIT2 : 포함 적어도 사실을 해요 그리고 대부분의 N 사실에
def test(iterable, n=1, m=1):
i = iter(iterable)
return m <= sum(any(i) for x in xrange(n+1)) <= n
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 값이 하나뿐입니다.
완성도를 높이고 for 루프 반복에 대한 Python의 제어 흐름의 고급 사용을 보여주기 위해 허용되는 답변에서 추가 계정을 피할 수 있으므로 약간 더 빠릅니다. :
def one_bool_true(iterable):
it = iter(iterable)
for i in it:
if i:
break
else: #no break, didn't find a true element
return False
for i in it: # continue consuming iterator where left off
if i:
return False
return True # didn't find a second true.
The above's simple control flow makes use of Python's sophisticated feature of loops: the else
. The semantics are that if you finish iterating over the iterator that you are consuming without break
-ing out of it, you then enter the else
block.
Here's the accepted answer, which uses a bit more accounting.
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
to time these:
import timeit
>>> min(timeit.repeat(lambda: one_bool_true([0]*100 + [1, 1])))
13.992251592921093
>>> min(timeit.repeat(lambda: one_bool_true([1, 1] + [0]*100)))
2.208037032979064
>>> min(timeit.repeat(lambda: only1([0]*100 + [1, 1])))
14.213872335107908
>>> min(timeit.repeat(lambda: only1([1, 1] + [0]*100)))
2.2482982632641324
>>> 2.2482/2.2080
1.0182065217391305
>>> 14.2138/13.9922
1.0158373951201385
So we see that the accepted answer takes a bit longer (slightly more than one and a half of a percent).
Naturally, using the built-in any
, written in C, is much faster (see Jon Clement's answer for the implementation - this is the short form):
>>> min(timeit.repeat(lambda: single_true([0]*100 + [1, 1])))
2.7257133318785236
>>> min(timeit.repeat(lambda: single_true([1, 1] + [0]*100)))
2.012824866380015
Is this what you're looking for?
sum(l) == 1
import collections
def only_n(l, testval=True, n=1):
counts = collections.Counter(l)
return counts[testval] == n
Linear time. Uses the built-in Counter class, which is what you should be using to check counts.
Re-reading your question, it looks like you actually want to check that there is only one truthy value, rather than one True
value. Try this:
import collections
def only_n(l, testval=True, coerce=bool, n=1):
counts = collections.Counter((coerce(x) for x in l))
return counts[testval] == n
While you can get better best case performance, nothing has better worst-case performance. This is also short and easy to read.
Here's a version optimised for best-case performance:
import collections
import itertools
def only_n(l, testval=True, coerce=bool, n=1):
counts = collections.Counter()
def iterate_and_count():
for x in itertools.imap(coerce,l):
yield x
if x == testval and counts[testval] > n:
break
counts.update(iterate_and_count())
return counts[testval] == n
The worst case performance has a high k
(as in O(kn+c)
), but it is completely general.
Here's an ideone to experiment with performance: http://ideone.com/ZRrv2m
Here's something that ought to work for anything truthy, though it has no short-circuit. I found it while looking for a clean way to forbid mutually-exclusive arguments:
if sum(1 for item in somelist if item) != 1:
raise ValueError("or whatever...")
What about:
len([v for v in l if type(v) == bool and v])
If you only want to count boolean True values.
'Nice programing' 카테고리의 다른 글
쌍을 이루는 순환 파이썬 'for'루프 (0) | 2020.10.17 |
---|---|
자바 스크립트에서 배열의 중복 값을 계산하는 방법 (0) | 2020.10.17 |
std :: multimap의 차이점은 무엇입니까 (0) | 2020.10.16 |
Linux에서 파일을 사용하는 프로세스를 어떻게 알 수 있습니까? (0) | 2020.10.16 |
Angular2- 'router-outlet'은 알려진 요소가 아닙니다. (0) | 2020.10.16 |