【问题标题】:one-liner reduce in Python3Python3中的单行减少
【发布时间】:2017-10-19 15:11:21
【问题描述】:

在 Python3 中,我正在寻找一种方法来计算 一行 一个对两个两个元素调用的 lambda 函数。假设我要计算整数列表的LCM,这可以在one line in Python2 中完成:

print reduce(lambda a,b: a * b // gcd(a, b), mylist)

是否可以在一行 Python3 中做同样的事情(隐含,没有functools.reduce)?

在 Python3 中,我知道 filtermapreduce 消失了。我觉得我不再需要 filtermap 了,因为它们可以用 Python3 以更短、更清晰的方式编写,但我想我也可以找到一个很好的替代 reduce,除非我没有发现任何。我have 看过many 文章that 建议to 使用functools.reduce 或“write out the accumulation loop explicitly”,但我想在不导入functools 的情况下这样做。

如果它更容易,我应该提一下我使用了associativecommutative 的函数。例如,使用列表[1,2,3,4] 上的函数f,如果它计算结果会很好:

  • f(1,f(2,f(3,4)))
  • f(f(1,2),f(3,4))
  • f(f(3,f(1,4)),2)
  • 或任何其他订单

【问题讨论】:

  • 为什么要一行行呢?鉴于此,您为什么不想使用functools.reduce?这两个要求似乎都是武断的,没有实际用途
  • 添加到 Chris 的评论中,reduce 已从全局命名空间移动到 functools,这正是 因为 它主要用于简洁(有人可能会说,晦涩难懂)的-衬里。
  • 你不使用functools.reduce的理由对我来说没有多大意义......
  • filtermap 仍然在 Python 3 中,但最好使用推导式。如前所述,reducefunctools 中。在此处查看 BDFL 的帖子artima.com/weblogs/viewpost.jsp?thread=98196

标签: python python-3.x python-2.x reduce


【解决方案1】:

所以我确实想出了一些东西。虽然我不保证性能,但它是一个只使用 lambda 函数的单行 - 没有来自 functoolsitertools,甚至没有一个循环。

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)

这有点不可读,所以在这里展开:

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
    )

测试:

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

请查看this other post 以更深入地了解其工作原理。

模拟递归函数的原理,使用lambda函数,其第一个参数是一个函数,并且将是它自己。

这个递归函数嵌入在一个函数中,可以有效地触发递归调用:lambda u, a: u(u, a)

最后,所有东西都封装在一个函数中,它的参数是一个列表和一个二进制函数。


在您的代码中使用my_reduce

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

【讨论】:

【解决方案2】:

假设您有一个至少有一个项目长的序列,您可以像这样简单地递归定义reduce

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

长版会更易读:

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

但是,这是递归的,python 不擅长递归调用(这意味着速度慢且递归限制阻止处理超过 300 个项目的序列)。一个更快的实现是:

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

但是由于循环,它不能放在一行中。它可以使用副作用来解决:

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']

或:

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

相当于:

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)"

这应该更快,但它并不完全是 pythonic,因为单行实现中的列表理解只是因为副作用而被使用。

【讨论】:

    猜你喜欢
    • 2018-06-04
    • 2018-12-21
    • 2021-07-10
    • 2022-01-21
    • 2011-05-07
    • 1970-01-01
    • 2012-11-17
    • 2016-06-28
    • 2018-07-30
    相关资源
    最近更新 更多