【问题标题】:How does reduce function work?减少功能如何工作?
【发布时间】:2012-02-24 21:39:28
【问题描述】:

据我了解,reduce 函数需要一个列表l 和一个函数f。然后,它在列表的前两个元素上调用函数f,然后用下一个列表元素和前一个结果重复调用函数f

所以,我定义了以下函数:

以下函数计算阶乘。

def fact(n):
    if n == 0 or n == 1:
        return 1
    return fact(n-1) * n


def reduce_func(x,y):
    return fact(x) * fact(y)

lst = [1, 3, 1]
print reduce(reduce_func, lst)

现在,这不应该给我((1! * 3!) * 1!) = 6吗?但是,它却给出了720。为什么720?它似乎也采用了6 的阶乘。但是,我需要了解原因。

有人可以解释为什么会发生这种情况以及解决方法吗?

我基本上想计算列表中所有条目的阶乘乘积。 备份计划是运行一个循环并计算它。但是,我更喜欢使用 reduce。

【问题讨论】:

  • 谢谢大家。我发现了我错过的愚蠢的事情。我已经在答案中发布了正确的方法。
  • 要更深入地了解 reduce,请参阅下面显示的纯 python 等效项。

标签: python reduce


【解决方案1】:

其他答案很棒。我将简单地添加一个我觉得很好理解的图解示例reduce()

>>> reduce(lambda x,y: x+y, [47,11,42,13])
113

将按如下方式计算:

(Source) (mirror)

【讨论】:

  • 非常感谢。这真的很有帮助。我有一个关于 reduce 函数的快速问题:它只需要两个参数吗?
  • 是的。它需要 2 个参数。
【解决方案2】:

理解 reduce() 的最简单方法是查看其纯 Python 等效代码:

def myreduce(func, iterable, start=None):
    it = iter(iterable)
    if start is None:
        try:
            start = next(it)
        except StopIteration:
            raise TypeError('reduce() of empty sequence with no initial value')
    accum_value = start
    for x in iterable:
        accum_value = func(accum_value, x)
    return accum_value

您可以看到只有 reduce_func() 将阶乘应用于最右边的参数才有意义:

def fact(n):
    if n == 0 or n == 1:
        return 1
    return fact(n-1) * n

def reduce_func(x,y):
    return x * fact(y)

lst = [1, 3, 1]
print reduce(reduce_func, lst)

通过这个小修改,代码会产生 6 如您所料:-)

【讨论】:

  • 你刚刚让reduce裸体!但是当start=Nonemyreduce((lambda x,y:x+y),[1,2,3,4]) 返回11 但它应该有10;我把sum 当作func
  • 我认为更正应该看起来像for x in iterable[1:]:
  • for 循环应该遍历it,而不是iterablefor x in it:
【解决方案3】:

您的函数在两个参数上调用fact()。您正在计算((1! * 3!)! * 1!)。解决方法是仅在第二个参数上调用它,并将初始值 1 传递给 reduce()

【讨论】:

    【解决方案4】:

    来自Python reduce documentation

    reduce(function, sequence) 返回一个单个值,该值是通过对序列的前两项、结果和下一项调用 (binary) 函数构造的,以此类推。

    所以,一步一步。它计算前两个元素reduce_func(1, 3) = 1! * 3! = 6reduce_func。然后,它计算结果的reduce_func 和下一项:reduce_func(6, 1) = 6! * 1! = 720

    您错过了,当第一个 reduce_func 调用的结果作为输入传递给第二个时,它在乘法之前被分解。

    【讨论】:

      【解决方案5】:

      好的,明白了:

      我需要先将数字映射到它们的阶乘,然后使用乘法运算符调用 reduce。

      所以,这会起作用:

      lst_fact = map(fact, lst)
      reduce(operator.mul, lst_fact)
      

      【讨论】:

      • 嗯,这有点工作。您的阶乘函数仍然已经计算了其输入的阶乘,因此您的 reduce 不仅仅是这样做。
      • 是的,这是一种方法,并且可能比将阶乘计算放在 reduce 函数中更“干净”,正如其他一些答案所建议的那样——但任何一个都会做你想做的事。
      【解决方案6】:

      你也可以使用reduce来实现阶乘。

      def factorial(n):
        return(reduce(lambda x,y:x*y,range(n+1)[1:]))
      

      【讨论】:

        【解决方案7】:

        除了一些琐碎的例子,我发现reduce 实际上非常有用:

        想象一个可迭代的有序int 值,通常包含一些连续值,我们希望将其“总结”为表示范围的元组列表。 (还要注意,这个可迭代对象可能是一个非常长的序列的生成器——另一个使用 reduce 的原因,而不是对内存中集合的某些操作)。

        from functools import reduce
        
        def rle(a, b):
            if a and a[-1][1] == b:
                return a[:-1] + [(a[-1][0], b + 1)]
            return a + [(b, b + 1)]
        
        reduce(rle, [0, 1, 2, 5, 8, 9], [])
        # [(0, 3), (5, 6), (8, 10)]
        

        注意对reduce 使用正确的initial 值(此处为[])。

        也处理了特殊情况:

        reduce(rle, [], [])
        # []
        
        reduce(rle, [0], [])
        # [(0, 1)]
        

        【讨论】:

          【解决方案8】:

          首先,您的reduce_func 没有折叠结构;它与您对折叠的描述不符(这是正确的)。

          折叠的结构是:def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)

          现在,您的 fact 函数不对两个元素进行操作 - 它只是计算阶乘。

          因此,总而言之,您没有使用折叠,并且根据阶乘的定义,您不需要。

          如果您确实想使用阶乘,请查看 y-combinator:http://mvanier.livejournal.com/2897.html

          如果您想了解折叠,请查看我对这个问题的回答,该回答演示了它用于计算累积分数:creating cumulative percentage from a dictionary of data

          【讨论】:

            【解决方案9】:

            reduce通过参数#2中迭代器提供的值依次执行参数#1中的函数

            print '-------------- Example: Reduce(x + y) --------------'
            
            def add(x,y): return x+y
            x = 5
            y = 10
            
            import functools
            tot = functools.reduce(add, range(5, 10))
            print 'reduce('+str(x)+','+str(y)+')=' ,tot
            
            def myreduce(a,b):
                tot = 0
                for i in range(a,b):
                    tot = tot+i
                    print i,tot
                print 'myreduce('+str(a)+','+str(b)+')=' ,tot
            
            myreduce(x,y)
            
            print '-------------- Example: Reduce(x * y) --------------'
            
            def add(x,y): return x*y
            x = 5
            y = 10
            
            import functools
            tot = functools.reduce(add, range(5, 10))
            print 'reduce('+str(x)+','+str(y)+')=' ,tot
            
            def myreduce(a,b):
                tot = 1
                for i in range(a,b):
                    tot = tot * i
                    print i,tot
                print 'myreduce('+str(a)+','+str(b)+')=' ,tot
            
            myreduce(x,y)
            

            【讨论】:

              猜你喜欢
              • 2020-11-01
              • 1970-01-01
              • 2018-10-29
              • 1970-01-01
              • 2020-03-25
              • 1970-01-01
              • 2021-03-13
              • 2022-12-18
              • 2016-05-02
              相关资源
              最近更新 更多