2012-04-28 3 views
88

Что является наиболее идиоматическим способом добиться чего-то вроде следующего, в Haskell:Что такое «pythonic», эквивалентный функции «fold» от функционального программирования?

foldl (+) 0 [1,2,3,4,5] 
--> 15 

или его эквивалент в Ruby:

[1,2,3,4,5].inject(0) {|m,x| m + x} 
#> 15 

Очевидно, что Python предоставляет функцию reduce, которая является реализацией однако, мне сказали, что «питонический» способ программирования состоит в том, чтобы избежать использования терминами и функциями более высокого порядка, предпочитая, если это возможно, понимание списков. Следовательно, существует ли предпочтительный способ сгибания списка или структуры списка в Python, которая не является функцией reduce, или reduce - идиоматический способ достижения этого?

+2

'sum' не достаточно хорошо? – JBernardo

+3

не уверен, что это хороший пример для вашего вопроса. Его можно легко достичь с помощью 'sum', вы можете предложить несколько разных типов примеров. – jamylak

+10

Эй, Дж. Бернардо. Суммирование списка чисел означало как довольно вырожденный пример, меня больше интересует общая идея накопления элементов списка с использованием некоторой двоичной операции и начальное значение, а не суммирование целых чисел. – mistertim

ответ

93

Pythonic способ суммирования массива sum. Для других целей иногда можно использовать некоторую комбинацию из reduce и модуля operator, например.

def product(xs): 
    return reduce(operator.mul, xs, 1) 

Имейте в виду, что reduce на самом деле foldl, с точки зрения Haskell. Для выполнения сгибов нет специального синтаксиса, нет встроенного foldr, и фактически использование reduce с неассоциативными операторами считается плохим.

Использование функций более высокого порядка довольно pythonic; он хорошо использует принцип Python, что все является объектом, включая функции и классы. Вы правы, что лямбды недовольны некоторыми питонистами, но в основном потому, что они, как правило, не очень читаемы, когда они становятся сложными.

+0

Высший порядок функции не всегда могут быть pythonic.Иначе зачем «уменьшать» удаляться из встроенного пространства имен Py3K? – JBernardo

+3

@JBernardo: вы говорите, что что-то не в модуле builtins не является питоническим? –

+3

Нет, это было бы глупо говорить. Но дайте мне одну причину, почему вы думаете, что [GvR будет так сильно ненавидеть функцию сокращения] (http://www.artima.com/weblogs/viewpost.jsp?thread=98196) в момент удаления ее из встроенных? – JBernardo

3

Фактический ответ на эту проблему (сокращение): просто используйте цикл!

initial_value = 0 
for x in the_list: 
    initial_value += x #or any function. 

Это будет быстрее, чем сокращение, и такие вещи, как PyPy, могут оптимизировать такие петли.

Кстати, в случае сумма должна быть решена с sum функции

+4

Это не будет рассматриваться как pythonic для примера, такого как этот. – jamylak

+7

Петли Python, как известно, медленны.Использование (или злоупотребление) 'reduce' является распространенным способом оптимизации программы Python. –

+2

@jamylak Прочитайте снова последнюю строчку, которую я написал .... – JBernardo

3

Вы можете изобретать велосипед, а также:

def fold(f, l, a): 
    """ 
    f: the function to apply 
    l: the list to fold 
    a: the accumulator, who is also the 'zero' on the first call 
    """ 
    return a if(len(l) == 0) else fold(f, l[1:], f(a, l[0])) 

print "Sum:", fold(lambda x, y : x+y, [1,2,3,4,5], 0) 

print "Any:", fold(lambda x, y : x or y, [False, True, False], False) 

print "All:", fold(lambda x, y : x and y, [False, True, False], True) 

# Prove that result can be of a different type of the list's elements 
print "Count(x==True):", 
print fold(lambda x, y : x+1 if(y) else x, [False, True, True], 0) 
+0

Вы заменяете аргументы на 'f' в вашем рекурсивном случае. – KayEss

+6

Поскольку у Python отсутствует рекурсия хвоста, это будет ломаться по более длинным спискам и расточительно. Кроме того, это не действительно функция «fold», а просто левая складка, т. Е. Foldl, то есть * точно * то, что уже предлагает 'сокращение' (обратите внимание, что сигнатура функции сокращения -' сокращение (функция, последовательность [, initial]) -> value' - он также включает в себя функциональность предоставления начального значения для аккумулятора). – cemper93

12

Haskell

foldl (+) 0 [1,2,3,4,5]

Python

reduce(lambda a,b: a+b, [1,2,3,4,5], 0)

Очевидно, что это тривиальный пример, иллюстрирующий точку. В Python вы просто сделали бы sum([1,2,3,4,5]), и даже пуристы Haskell обычно предпочитали бы sum [1,2,3,4,5].

Для нетривиальных сценариев, когда нет очевидной удобной функции, идиоматический питонический подход заключается в явной выписывании цикла for и использовании изменчивого назначения переменных вместо использования reduce или fold.

Это вовсе не функциональный стиль, но это «питонический» способ. Python не предназначен для функциональных пуристов. Посмотрите, как Python поддерживает исключения для управления потоком, чтобы увидеть, как нефункциональный идиоматический питон.

+1

складки полезны более функциональным «пуристам». Это абстракции общего назначения. Рекурсивные проблемы широко распространены при вычислении. Folds предлагают способ удаления шаблона и способ сделать рекурсивные решения безопасными на языках, которые не поддерживают рекурсию. Это очень практичная вещь. Несчастные предрассудки GvR в этой области. – itsbruce

3

Не на самом деле ответить на этот вопрос, но однострочечники для foldl и foldr:

a = [8,3,4] 

## Foldl 
reduce(lambda x,y: x**y, a) 
#68719476736 

## Foldr 
reduce(lambda x,y: y**x, a[::-1]) 
#14134776518227074636666380005943348126619871175004951664972849610340958208L 
6

В Python 3, reduce был удален: Release notes. Тем не менее вы можете использовать functools module

import operator, functools 
def product(xs): 
    return functools.reduce(operator.mul, xs, 1) 

С другой стороны, документация выражает предпочтение в отношении for -loop вместо reduce, следовательно:

def product(xs): 
    result = 1 
    for i in xs: 
     result *= i 
    return result 
+1

'reduce' не был удален из стандартной библиотеки Python 3. 'reduce' переместился в модуль' functools', как вы показываете. – clay

+0

@clay, я просто взял фразу из заметок о выпуске Guido, но вы можете быть правы :) – Kyr