部分应用列表理解

时间:2014-07-12 06:29:56

标签: python haskell functional-programming list-comprehension

我开始学习Haskell,事实证明,在那里,你可以使列表理解成为部分应用的函数。换句话说,它返回一个接受列表的函数,然后在列表上运行列表推导。

示例:

boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]

boomBangs can then be called with the actual "xs" argument.

我的问题:在Python中有没有办法做到这一点?我已经四处寻找并且无法找到方法,但我可能会遗漏一些东西。

能够做到这一点非常有价值。

修改

我的意思似乎有些混乱。

我希望能够定义列表理解,而不必将列表用于,直到稍后。

这个想法是列表理解有效地变成了一个接受一个参数的函数,即要处理的列表。

E.g。我能做到:

my_new_func = [x*2 for x in l] # l is **not defined**

然后在代码中的某个地方:

my_new_func(range(10)) # Returns the answer

这是一种非常好的功能性编程方式。

第二次修改:

这是一种做我想做的事情,但我想知道是否有更好的方法:

boomBangs = lambda lst: [actual list comprehension]

boomBangs(range(10))

3 个答案:

答案 0 :(得分:5)

关于那个特殊的部分不是列表理解,而是函数定义。恰好该函数返回一个列表。因此,只需将其定义为Python中的普通函数即可。

boomBangs = lambda xs: ["BOOM!" if x < 10 else "BANG!" for x in xs if odd(x)]

def boomBang(xs):
  return ["BOOM!" if x < 10 else "BANG!" for x in xs if odd(x)]

如果你需要懒惰,请改用genex。

boomBangs = lambda xs: ("BOOM!" if x < 10 else "BANG!" for x in xs if odd(x))

def boomBang(xs):
  return ("BOOM!" if x < 10 else "BANG!" for x in xs if odd(x))

答案 1 :(得分:1)

你可以使用python的列表理解:

xs = ["BOOM!" if x < 10 else "BANG!" for x in range(20) if x % 2 == 1]

将此功能合并到一个功能中:

def boombang(xs):
    return ["BOOM!" if x < 10 else "BANG!" for x in xs if x % 2 == 1]

您也可以使用lambda

>>> boombang = lambda xs: ["BOOM!" if x < 10 else "BANG!" for x in xs if x % 2 == 1]
>>> boombang(range(-10, 20))
['BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BANG!', 'BANG!', 'BANG!', 'BANG!', 'BANG!']
>>> 

列表推导工作向后,结束的定义和开头的语句。在列表理解的开头,我们说"BOOM!" if x < 10 else "BANG!",转换为:

if x < 10:
    xs.append("BOOM!")
else:
    xs.append("BANG!")

在第二部分中,我们将x定义为0到20(包括0和20)列表中的每个项目。之后,我们确保只有x是奇数时才能完成此循环,使用python&#39; s modulo。


>>> xs = ["BOOM!" if x < 10 else "BANG!" for x in range(20) if x % 2 == 1]
>>> xs
['BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BANG!', 'BANG!', 'BANG!', 'BANG!', 'BANG!']
>>> 

答案 2 :(得分:0)

您可以使用内置函数mapfilterfunctools.partial以您想要的样式实现此目的,但结果可能不是非常清晰或方便比较像其他海报一样制作Python功能。但是,部分应用程序和currying确实是可以在Python中使用的强大技术。

en.wikipedia.org/wiki/Partial_application

en.wikipedia.org/wiki/Currying

这可以使用toolz Python包轻松演示,该包包含许多其他函数式编程语言中常见的功能实用程序。 toolz还有一个方便的curried命名空间,我将用它来重现您的示例:

 
>>> # define our primitive functions for clarity
>>> boombang = lambda x: "BOOM!" if x < 10 else "BANG!"
>>> is_odd = lambda x: x % 2 == 1

>>> # given a list, using built-in functions we can do
>>> map(boombang, filter(is_odd, range(20)))
['BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BANG!', 'BANG!', 'BANG!', 'BANG!', 'BANG!']

>>> # if we want to provide the list later, we can do
>>> from toolz.curried import map, filter, compose
>>> boomBangs = compose(map(boombang), filter(is_odd))

>>> # ... Now we have our input list, so let's use it!
>>> list(boomBangs(range(20))
['BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BOOM!', 'BANG!', 'BANG!', 'BANG!', 'BANG!', 'BANG!']

请注意我们在最后一个示例中仅向mapfilter传递了一个参数。这是在哭泣!在提供最终参数时调用函数,我们确实获得了相同的结果。

让我们分别使用mapfilter来澄清正在发生的事情:

 
>>> from toolz.curried import map, filter
>>> map_boombang = map(boombang)
>>> list(map_boombang([9, 10, 11]))
['BOOM!', 'BANG!', 'BANG!']

>>> filter_odd = filter(is_odd)
>>> list(filter_odd(range(10)))
[1, 3, 5, 7, 9]