If I do:
result = reduce(operator.and_, [False] * 1000)
Will it stop after the first result? (since False & anything == False)
Similarly:
result = reduce(operator.or_, [True] * 1000)
If I do:
result = reduce(operator.and_, [False] * 1000)
Will it stop after the first result? (since False & anything == False)
Similarly:
result = reduce(operator.or_, [True] * 1000)
It may well be possible (see fate of reduce) that an alternative reduce implementation will do a good job.
This idea has perfectly worked for me to make things more transparent in the design.
def ipairs(seq):
prev = None
for item in seq:
if prev is not None:
yield (prev, item)
prev = item
def iapply(seq, func):
for a, b in ipairs(seq):
yield func(a, b)
def satisfy(seq, cond):
return all(iapply(seq, cond))
def is_uniform(seq):
return satisfy(seq, lambda a, b: a == b)
As you see reduce is broken into iapply <- ipairs.
Please note it is not equivalent to
def ireduce(seq, func):
prev = None
for item in seq:
if prev is None:
prev = item
else:
prev = func(prev, item)
return prev
I had a related use case where I wanted a behavior different from any and all but equivalent to a loop over or and and. The solution is to use filter rather than reduce.
any returns booleans while or returns the last object evaluated as True.
>>> any(['', 'a'])
True
>>> any(['', ''])
False
>>> any([0, 1])
True
>>> any([0, 0])
False
or returns the last object evaluated as True.
>>> '' or 'a'
'a'
>>> '' or ''
''
>>> 0 or 1
1
>>> 0 or 0
0
reduce(operator.or_, xs) will not short circuit but next(filter(bool, xs)) in python3 or next(itertools.ifilter(bool, xs)) in python2 will short circuit. It is not because filter short circuits, but because iterator returned is lazy and will only evaluate when needed. By using next we are only asking for first element that satisfies the filter criteria.
>>> def maybenext(iter, onstopiter=False):
... try: return next(iter)
... except StopIteration: return onstopiter
...
>>>
>>> maybenext(filter(bool, ['', 'a']))
'a'
>>> maybenext(filter(bool, ['', '']))
False
>>> maybenext(filter(bool, [0, 1]))
1
>>> maybenext(filter(bool, [0, 0]))
False
The result is not as fast as any but close enough
>>> %timeit maybenext(filter(bool, [1] * 1000))
2.48 µs ± 91.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit any([1] * 1000)
2.26 µs ± 90.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit reduce(operator.or_, [1] * 1000)
47.3 µs ± 1.75 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Here's a possible lazy reduce implementation:
from itertools import accumulate
def lazy_reduce(pred, reducer, it, initial=None):
accum = accumulate(it, reducer, initial=initial)
last = None
for item in accum:
last = item
if pred(item):
return item
return last
This should work like reduce but will short on pred.
from operator import add
from itertools import count
res = short_reduce(lambda x: x > 20, add, count(1,8)) # 27