Каков наиболее идиоматический способ добиться в Haskell чего-то вроде следующего:
foldl (+) 0 [1,2,3,4,5]
--> 15
Или его эквивалент в Ruby:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
Очевидно, Python предоставляет reduce
функцию, которая является реализацией fold, точно так же, как указано выше, однако мне сказали, что «питонический» способ программирования заключался в том, чтобы избегать lambda
терминов и функций более высокого порядка, предпочитая, где это возможно, понимание списков. Следовательно, существует ли предпочтительный способ сворачивания списка или структуры в виде списка в Python, который не является reduce
функцией, или это reduce
идиоматический способ достижения этого?
sum
недостаточно хорошо?sum
, возможно, вы захотите привести несколько различных типов примеров.sum()
фактически предоставляет ограниченную функциональность.sum([[a], [b, c, d], [e, f]], [])
возвращается[a, b, c, d, e, f]
например.+
со списками - это операция с линейным временем как во времени, так и в памяти, что делает весь вызов квадратичным. Использованиеlist(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])
в целом линейно - и если вам нужно выполнить итерацию только один раз, вы можете отказаться от вызова,list
чтобы сделать его постоянным с точки зрения памяти.Ответы:
Пифонический способ суммирования массива использует
sum
. Для других целей вы можете иногда использовать некоторую комбинациюreduce
(изfunctools
модуля) иoperator
модуля, например:Имейте в виду, что в терминах Haskell
reduce
это на самом делеfoldl
. Нет специального синтаксиса для выполнения сверток, нет встроенных функцийfoldr
, и на самом деле использованиеreduce
с неассоциативными операторами считается плохим стилем.Использование функций высшего порядка довольно питонично; он хорошо использует принцип Python, согласно которому все является объектом, включая функции и классы. Вы правы, что лямбды не одобряются некоторыми питонистами, но в основном потому, что они не очень удобочитаемы, когда становятся сложными.
источник
reduce()
значительной степени ограничена ассоциативными операторами, а во всех остальных случаях лучше явно прописать цикл накопления». Таким образом, его использование ограничено, но даже GvR, очевидно, должен был признать его достаточно полезным, чтобы оставить его в стандартной библиотеке.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 отдает предпочтение исключениям для управления потоком, чтобы увидеть, насколько нефункциональный идиоматический Python.
источник
В Python 3
reduce
был удален: Примечания к выпуску . Тем не менее вы можете использовать модуль functoolsС другой стороны, в документации предпочтение отдается
for
-loop вместоreduce
, следовательно:источник
reduce
не был удален из стандартной библиотеки Python 3.reduce
переехал вfunctools
модуль, как вы показываете.Начиная
Python 3.8
с введения выражений присваивания (PEP 572) (:=
оператор), которые дают возможность назвать результат выражения, мы можем использовать понимание списка, чтобы воспроизвести то, что в других языках называется операциями сворачивания / свертывания / сворачивания / сокращения:Учитывая список, уменьшающую функцию и аккумулятор:
мы можем сложить
items
сf
целью получения Результирующегоaccumulation
:или в сжатом виде:
Обратите внимание, что на самом деле это также операция «сканирования влево», поскольку результат понимания списка представляет состояние накопления на каждом шаге:
источник
Вы также можете изобретать велосипед:
источник
f
в своем рекурсивном случае.reduce
уже предлагает (обратите внимание, что сигнатура функции reducereduce(function, sequence[, initial]) -> value
- она также включает в себя функцию задания начального значения для аккумулятор).Не совсем ответ на вопрос, но однострочные для foldl и foldr:
источник
reduce(lambda y, x: x**y, reversed(a))
. Теперь он имеет более естественное использование, работает с итераторами и потребляет меньше памяти.Фактический ответ на эту проблему (сокращение): просто используйте цикл!
Это будет быстрее, чем сокращение, и такие вещи, как PyPy, могут оптимизировать такие циклы.
Кстати, случай суммы следует решать с помощью
sum
функцииисточник
reduce
- распространенный способ оптимизации программы Python.product
в вашем стиле, и она быстрее (хотя и незначительно).operator.add
) в качестве аргумента для уменьшения: этот дополнительный вызов - это вызов C (который намного дешевле, чем вызов Python), и он экономит отправку и интерпретацию пары инструкций байт-кода, которые могут легко вызвать десятки вызовы функций.Я считаю, что некоторые из респондентов, ответивших на этот вопрос, упустили из виду более широкий смысл
fold
функции как абстрактного инструмента. Да,sum
можно сделать то же самое со списком целых чисел, но это тривиальный случай.fold
является более общим. Это полезно, когда у вас есть последовательность структур данных различной формы и вы хотите четко выразить агрегирование. Поэтому вместо того, чтобы создаватьfor
цикл с агрегированной переменной и каждый раз вручную пересчитывать ее,fold
функция (или версия Python, которая, по-reduce
видимому, соответствует) позволяет программисту гораздо более четко выразить намерение агрегирования, просто предоставив две вещи:источник
fold
что трудно сделать чисто на Python, а затем "fold
" это на Python :-)Возможно, я довольно опаздываю на вечеринку, но мы можем создать собственный,
foldr
используя простое лямбда-исчисление и функцию карри. Вот моя реализация foldr на Python.Несмотря на то, что реализация рекурсивная (может быть медленной), она распечатает значения
15
и120
соответственноисточник