What is the 'pythonic' equivalent to the 'fold' function from functional programming?

后端 未结 9 2012
被撕碎了的回忆
被撕碎了的回忆 2020-12-01 00:03

What is the most idiomatic way to achieve something like the following, in Haskell:

foldl (+) 0 [1,2,3,4,5]
--> 15

Or its equivalent in

相关标签:
9条回答
  • 2020-12-01 00:07

    Not really answer to the question, but one-liners for foldl and foldr:

    a = [8,3,4]
    
    ## Foldl
    reduce(lambda x,y: x**y, a)
    #68719476736
    
    ## Foldr
    reduce(lambda x,y: y**x, a[::-1])
    #14134776518227074636666380005943348126619871175004951664972849610340958208L
    
    0 讨论(0)
  • 2020-12-01 00:12

    You can reinvent the wheel as well:

    def fold(f, l, a):
        """
        f: the function to apply
        l: the list to fold
        a: the accumulator, who is also the 'zero' on the first call
        """ 
        return a if(len(l) == 0) else fold(f, l[1:], f(a, l[0]))
    
    print "Sum:", fold(lambda x, y : x+y, [1,2,3,4,5], 0)
    
    print "Any:", fold(lambda x, y : x or y, [False, True, False], False)
    
    print "All:", fold(lambda x, y : x and y, [False, True, False], True)
    
    # Prove that result can be of a different type of the list's elements
    print "Count(x==True):", 
    print fold(lambda x, y : x+1 if(y) else x, [False, True, True], 0)
    
    0 讨论(0)
  • 2020-12-01 00:13

    I believe some of the respondents of this question have missed the broader implication of the fold function as an abstract tool. Yes, sum can do the same thing for a list of integers, but this is a trivial case. fold is more generic. It is useful when you have a sequence of data structures of varying shape and want to cleanly express an aggregation. So instead of having to build up a for loop with an aggregate variable and manually recompute it each time, a fold function (or the Python version, which reduce appears to correspond to) allows the programmer to express the intent of the aggregation much more plainly by simply providing two things:

    • A default starting or "seed" value for the aggregation.
    • A function that takes the current value of the aggregation (starting with the "seed") and the next element in the list, and returns the next aggregation value.
    0 讨论(0)
  • 2020-12-01 00:15

    Haskell

    foldl (+) 0 [1,2,3,4,5]

    Python

    reduce(lambda a,b: a+b, [1,2,3,4,5], 0)

    Obviously, that is a trivial example to illustrate a point. In Python you would just do sum([1,2,3,4,5]) and even Haskell purists would generally prefer sum [1,2,3,4,5].

    For non-trivial scenarios when there is no obvious convenience function, the idiomatic pythonic approach is to explicitly write out the for loop and use mutable variable assignment instead of using reduce or a fold.

    That is not at all the functional style, but that is the "pythonic" way. Python is not designed for functional purists. See how Python favors exceptions for flow control to see how non-functional idiomatic python is.

    0 讨论(0)
  • 2020-12-01 00:21

    In Python 3, the reduce has been removed: Release notes. Nevertheless you can use the functools module

    import operator, functools
    def product(xs):
        return functools.reduce(operator.mul, xs, 1)
    

    On the other hand, the documentation expresses preference towards for-loop instead of reduce, hence:

    def product(xs):
        result = 1
        for i in xs:
            result *= i
        return result
    
    0 讨论(0)
  • 2020-12-01 00:22

    I may be quite late to the party, but we can create custom foldr using simple lambda calculus and curried function. Here is my implementation of foldr in python.

    def foldr(func):
        def accumulator(acc):
            def listFunc(l):
                if l:
                    x = l[0]
                    xs = l[1:]
                    return func(x)(foldr(func)(acc)(xs))
                else:
                    return acc
            return listFunc
        return accumulator  
    
    
    def curried_add(x):
        def inner(y):
            return x + y
        return inner
    
    def curried_mult(x):
        def inner(y):
            return x * y
        return inner
    
    print foldr(curried_add)(0)(range(1, 6))
    print foldr(curried_mult)(1)(range(1, 6))
    

    Even though the implementation is recursive (might be slow), it will print the values 15 and 120 respectively

    0 讨论(0)
提交回复
热议问题