在Python3中,我正在寻找一种方法来计算一行一个lambda
函数,该函数在两个元素上调用。假设我想计算整数列表的LCM,这可以在one line in Python2中完成:
print reduce(lambda a,b: a * b // gcd(a, b), mylist)
是否可以在一行Python3中执行相同操作(暗示,没有functools.reduce
)?
在Python3中,我知道filter
,map
和reduce
已经消失。我觉得我不再需要filter
和map
,因为它们可以用更简洁,更清晰的方式用Python3编写,但我认为我可以找到reduce
的一个很好的替代品,除了我没有找到任何。我have看过many篇文章that建议to使用functools.reduce
或“write out the accumulation loop explicitly”,但我想在不导入functools的情况下这样做在一行。
如果它更容易,我应该提到我使用associative和commutative的函数。例如,在列表f
上使用函数[1,2,3,4]
,如果计算结果将是好的:
f(1,f(2,f(3,4)))
f(f(1,2),f(3,4))
f(f(3,f(1,4)),2)
答案 0 :(得分:2)
所以我确实想出了一些东西。我不保证性能,但它是一个单独使用lambda
函数的单行 - 不是来自functools
或itertools
,甚至不是单个循环。
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))
答案 1 :(得分:1)
假设您的序列至少有一个项目,您可以像这样简单地定义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,因为单线实现中的列表理解只是因为副作用而被使用。