这些Haskell高阶函数的Python等价是什么?
这段内容来自一本书《Learn You a Haskell For Great Good》,里面有一章讲的是部分函数,代码如下:
multThree :: (Num a) => a -> a -> a -> a
multThree x y z = x * y * z
ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
ghci> let multWithEighteen = multTwoWithNine 2
ghci> multWithEighteen 10
180
我现在正在玩Python的functools库,成功地用它复制了那些函数的行为。
from functools import partial
def multThree(x,y,z):
return x * y * z
>>> multTwoWithNine = partial(multThree,9)
>>> multTwoWithNine(2,3)
>>> multWithEighteen = partial(multTwoWithNine,2)
>>> multWithEighteen(10)
180
接下来我想看看能不能复制书中那一章里一些更有趣的高阶函数,比如:
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
不过,我不太确定该怎么做,或者说partial()
在这里是否有用。
4 个回答
0
这段Python代码的功能和你提到的zipWith'
函数有点像:
def zip_with(f, l1, l2):
if len(l1) == 0 or len(l2) == 0:
return []
else:
return [f(l1[0], l2[0])] + zip_with(f, l1[1:], l2[1:])
不过,这个函数和Haskell的函数比起来有几个不足之处。首先,它看起来不够优雅,因为Python没有模式匹配的语法;我们需要用len
、[0]
和[1:]
来代替。其次,Python的这个函数没有使用惰性求值,也就是说zip_with
会一直遍历整个列表,即使它可以提前停止。第三,这个函数会针对结果列表中的每个元素调用自己一次,而Python对递归调用的限制大约是(或者确切地说是)1,000次,所以如果输出列表超过1,000个元素,这个函数就会报错。
第二和第三个问题可以用生成器来解决。
2
def add(a, b):
return a + b
x = [1, 2, 3, 4]
y = [5, 6, 7, 8]
>> map(add, x, y)
[6, 8, 10, 12]
另外,建议你看看Python自带的 itertools
模块:http://docs.python.org/2/library/itertools.html
5
Python里面有一个内置的 map
函数,它的工作方式和Haskell里的 zipWith
函数很像:
>>> def add(x,y): return x + y
...
>>> map(add,[1,2,3],[10,20,30])
[11, 22, 33]