How To Write Foldr (right Fold) Generator In Python?
Solution 1:
So a simple generator in python (without appropriate error checking):
deffoldr(op, lst):
l, x = reversed(list(lst)), Nonefor i in l:
ifnot x:
x = i
continue
x = op(x, i)
yield x
e.g.:
>>>from operator import mul>>>for i in foldr(mul, [1,2,3,4]):...print i
24
24
12
Almost identical to the 'roughly equivalent' implementation of reduce in the documentation:
deffoldr(function, iterable, initializer=None):
it = reversed(list(iterable))
if initializer isNone:
try:
initializer = next(it)
except StopIteration:
raise TypeError('foldr() of empty sequence with no initial value')
accum_value = initializer
for x in it:
accum_value = function(accum_value, x)
yield accum_value
[Edit] So purely as an exercise of the mind and with very little practical value, it is possible to defer as long as there is some cooperation between the function that you a folding over... e.g.:
classDefer(object):
def__init__(self, func, *args):
self.func = func
self.args = args
def__bool__(self):
return self.func(*self.args)
def__int__(self):
return self.func(*self.args)
deffoldr(function, iterable, initializer):
it = iter(iterable)
try:
return function(next(it), Defer(foldr, function, it, initializer))
except StopIteration:
return initializer
Then as long as the function converts to the right type you can defer the calculation, however this will not work with native operators, so not sure how useful this really is:
>>>print(foldr(lambda a, b: int(a)*int(b), [1,2,3,4], 1))
24
Defining a forever generator:
from itertools import repeat
defforever():
yieldFalseyieldTruefor i in repeat(False):
yield i
Folding or
across an infinite list, returns when it finds a True
>>> print(foldr(lambda a, b: bool(a) orbool(b), forever(), False))
True
Solution 2:
You will have to catch appropriate exceptions but should be an idea of how to do it iteratively:
deffoldr(a, b, l):
ifisinstance(l, Iterator):
it = reversed(list(l))
else:
it = reversed(l)
try:
nxt = next(it)
except StopIteration:
return
c = a(nxt, b)
stop = object()
while nxt isnot stop:
yield c
nxt = next(it, stop)
c = a(nxt, c) if nxt isnot stop else c
from operator import truediv
for c in (foldr(truediv, 1, [1, 2, 3, 4, 5, 6, 7, 8])):
print(c)
Solution 3:
If you are going to define a function using generators, why not use the following?
deffoldr(op, lst):
return reduce(op, reversed(lst))
Solution 4:
I think something like this is what you want:
deffoldr(fn, seq, init):
it = iter(seq)
try:
x = next(it)
except StopIteration:
try:
for elem in init:
yield elem
except TypeError:
yield init
else:
try:
for elem in fn(x, foldr(fn, it, init)):
yield elem
except TypeError:
yield fn(x, foldr(fn, it, init))
It's not exactly production-ready since it will hit the Python stack limit pretty quickly and it will be surprising in the presence of side-effecting functions due to the double call to fn
, but it should be enough to give you an idea.
Post a Comment for "How To Write Foldr (right Fold) Generator In Python?"