【问题标题】:Pythonic way for recursive reduction in pythonPython中递归减少的Pythonic方法
【发布时间】:2018-04-12 23:49:29
【问题描述】:

在python中减少列表时,我考虑过创建多个列表减少并编写了以下sn-p。

def multiply(a, b): return a * b

def recursive_reduce(reduce_func, *args):
     ret_val = reduce(reduce_func, *args)
     if type(ret_val) == list:
         ret_val = recursive_reduce(reduce_func, ret_val)
     return ret_val

a = [1, 1, 3]
b = [4, 5, 6]

recursive_reduce(multiply, a, b)

这行得通。但是我想知道根据返回值的类型定义迭代逻辑是否是pythonic。

我们还有其他方法可以更优雅地完成递归归约吗?

【问题讨论】:

  • 这是python2吗?函数第一行中的reduce 是什么? functools.reduce?
  • 是的,这是 python2 代码。无需任何导入即可使用 reduce 函数。 docs.python.org/2/library/functions.html#reduce
  • 绝对不是pythonic。这甚至不明智。 reduce 函数返回单个值。仅当reduce_func 以某种方式将两个列表合二为一时才使用 if 语句。但是您使用三个参数(一个函数和两个列表)从 main 调用 recursive_reduce。递归调用只有两个参数,一个函数和一个列表。那么同一个函数应该如何处理两种情况呢?
  • 为什么需要多次调用归约函数?为什么它不总是减少被调用的可迭代对象的维数?从我的 POV 来看,您的归约函数似乎存在问题,您应该修复而不是从递归函数中调用它。
  • @PaulCornelius,在上面的例子中,if 语句的计算结果为 True。关于这不是pythonic方式,可能是这就是我问这个问题并想了解更多信息的原因。

标签: python reduce


【解决方案1】:

我认为您要做的是做一个递归版本的reduce。

def rreduce(f, init, default=None):                     
    if default is None:
        default = init[0]
        init = init[1:]
    if len(init) == 0:
         return default
    return rreduce(f, init[1:], f(default, init[0]))


>>> rreduce(lambda a, b: a*b, range(1,10))
362880
>>> rreduce(lambda a, b: a+b, ['t', 'a', 'c', 'o'])
'taco'

虽然递归很棒,但这不是 Python 中 reduce 类型函数的首选方式,因为它很慢而且你会遇到 STACK OVERFLOW (HAA)

>>> rreduce(lambda a, b: a + [b], list(range(1, 10000)), [])
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-41-7dc07c5d9246> in <module>()
----> 1 rreduce(lambda a, b: a + [b], list(range(1, 10000)), [])

<ipython-input-33-37206eb8e39f> in rreduce(f, init, default)
      5     if len(init) == 0:
      6         return default
----> 7     return rreduce(f, init[1:], f(default, init[0]))

... last 1 frames repeated, from the frame below ...

<ipython-input-33-37206eb8e39f> in rreduce(f, init, default)
      5     if len(init) == 0:
      6         return default
----> 7     return rreduce(f, init[1:], f(default, init[0]))

RecursionError: maximum recursion depth exceeded in comparison

回答您的实际问题...

def lreduce(f, init, default=None):
        if default is None:
            return reduce(lambda x, a: x + [reduce(f, a)], init, [])
        else:
            return reduce(lambda x, a: x + [reduce(f, a, default)], init, [])

reduce 列表列表。

>>> lreduce(lambda a, b: a + b, [range(10), range(10), range(10)])
[45, 45, 45]

if/else 之所以必要是因为reduce 作为builtin 不接受关键字参数:

In [56]: reduce(function=lambda a, b: a + b, sequence=range(10), initial=0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-56-9fa3ed177831> in <module>()
----> 1 reduce(function=lambda a, b: a + b, sequence=range(10), initial=0)

TypeError: reduce() takes no keyword arguments

然后如果你想更进一步......

def lreduceall(f, init, default=None):
    if default is None:
        return reduce(f, reduce(lambda x, a: x + [reduce(f, a)], init, []))
    else:
        return reduce(f, reduce(lambda x, a: x + [reduce(f, a, default)], init, []), default)

终于:

>>> lreduceall(lambda a, b: a + b, [range(10), range(10), range(10)])
135

【讨论】:

  • 我要回答一个关于堆栈溢出时堆栈溢出的问题。这是我一生中最伟大的一天。
  • 谢谢。我现在从你的想法中得到了清晰的信息。我会尝试这种方法。
  • 太棒了!不要忘记将其标记为已接受的答案,以便您将来可以帮助他人!
猜你喜欢
  • 2011-06-07
  • 1970-01-01
  • 2013-06-13
  • 2019-01-15
  • 2015-08-21
  • 2013-04-12
  • 2013-10-10
  • 1970-01-01
  • 2021-06-11
相关资源
最近更新 更多