one-liner reduce in Python3

南楼画角 提交于 2019-11-30 16:45:39
Right leg

So I actually did come up with something. I do not guarantee the performance though, but it is a one-liner using exclusively lambda functions - nothing from functools or itertools, not even a single loop.

my_reduce = lambda l, f: (lambda u, a: u(u, a))((lambda v, m: None if len(m) == 0 else (m[0] if len(m) == 1 else v(v, [f(m[0], m[1])] + m[2:]))), l)

This is somewhat unreadable, so here it is expanded:

my_reduce = lambda l, f: (
    lambda u, a: u(u, a)) (
        (lambda v, m: None if len(m) == 0
                           else (m[0] if len(m) == 1
                                      else v(v, [f(m[0], m[1])] + m[2:])
                                )
        ),
        l
    )

Test:

>>> f = lambda a,b: a+b
>>> my_reduce([1, 2, 3, 4], f)
10
>>> my_reduce(['a', 'b', 'c', 'd'], f)
'abcd'

Please check this other post for a deeper explanation of how this works.

The principle if to emulate a recursive function, by using a lambda function whose first parameter is a function, and will be itself.

This recursive function is embedded inside of a function that effectively triggers the recursive calling: lambda u, a: u(u, a).

Finally, everything is wrapped in a function whose parameters are a list and a binary function.


Using my_reduce with your code:

my_reduce(mylist, lambda a,b: a * b // gcd(a, b))

Assuming you have a sequence that is at least one item long you can simply define reduce recursivly like this:

def reduce(func, seq): return seq[0] if len(seq) == 1 else func(reduce(func, seq[:-1]), seq[-1])

The long version would be slightly more readable:

def reduce(func, seq):
    if len(seq) == 1:
        return seq[0]
    else:
        return func(reduce(func, seq[:-1]), seq[-1])

However that's recursive and python isn't very good at recursive calls (meaning slow and the recursion limit prevents prosessing sequences longer than 300 items). A much faster implementation would be:

def reduce(func, seq):
    tmp = seq[0]
    for item in seq[1:]:
        tmp = func(tmp, item)
    return tmp

But because of the loop it can't be put in one-line. It could be solved using side-effects:

def reduce(func, seq): d = {}; [d.__setitem__('last', func(d['last'], i)) if 'last' in d else d.__setitem__('last', i) for i in seq]; return d['last']

or:

def reduce(func, seq): d = {'last': seq[0]}; [d.__setitem__('last', func(d['last'], i)) for i in seq[1:]]; return d['last']

Which is the equivalent of:

def reduce(func, seq): 
    d = {} 
    for item in seq:
        if 'last' in d:
            d['last'] = func(d['last'], item)
        else:
            d['last'] = item
    return d['last']  # or "d.get('last', 0)"

That should be faster but it's not exactly pythonic because the list-comprehension in the one-line implementation is just used because of the side-effects.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!