Каков эквивалент Python этих функций более высокого порядка Haskell?

В главе о частичных функциях из книги « Learn You a Haskell For Great Good» содержится следующий код:

multThree :: (Num a) => a -> a -> a -> a multThree xyz = x * y * z ghci> let multTwoWithNine = multThree 9 ghci> multTwoWithNine 2 3 54 ghci> let multWithEighteen = multTwoWithNine 2 ghci> multWithEighteen 10 180 

В настоящее время я играю с библиотекой functools в Python и умею реплицировать поведение этих функций, используя это.

 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) = fxy : zipWith' f xs ys 

Тем не менее, я не уверен, как это сделать, или если partial() здесь даже полезен.

Встроенная функция map Python ведет себя как Haskell's zipWith :

 >>> def add(x,y): return x + y ... >>> map(add,[1,2,3],[10,20,30]) [11, 22, 33] 
 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] 

Кроме того, проверьте модуль встроенного itertools на Python: http://docs.python.org/2/library/itertools.html

Этот код 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 предел рекурсии около (или точно?) 1000, поэтому эта функция вызовет исключение, если выходной список содержит более 1000 элементов в длину ,

Вторая и третья проблемы могут быть решены с использованием генераторов.

Это хороший кандидат на использование встроенной функции zip и понимания списка:

 >>> zip_with = lambda fn, la, lb: [fn(a, b) for (a, b) in zip(la, lb)] >>> add2 = lambda x,y: x+y >>> zip_with(add2, range(10), range(1,11)) [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]