Jaki jest najbardziej idiomatyczny sposób osiągnięcia czegoś takiego w Haskellu:
foldl (+) 0 [1,2,3,4,5]
--> 15
Lub jego odpowiednik w Rubim:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
Oczywiście Python udostępnia reduce
funkcję, która jest implementacją fold, dokładnie tak, jak powyżej, jednak powiedziano mi, że „pythonowym” sposobem programowania jest unikanie lambda
terminów i funkcji wyższego rzędu, preferowanie wyrażeń listowych tam, gdzie to możliwe. W związku z tym, czy istnieje preferowany sposób zwinięcia listy lub struktury podobnej do listy w Pythonie, która nie jest reduce
funkcją, czy też jest reduce
idiomatycznym sposobem osiągnięcia tego?
sum
nie jest wystarczająco dobry?sum
, możesz podać kilka różnych typów przykładów.sum()
rzeczywistości zapewnia ograniczoną funkcjonalność w tym.sum([[a], [b, c, d], [e, f]], [])
zwraca[a, b, c, d, e, f]
na przykład.+
na listach jest liniowa operacja czasowa zarówno w czasie, jak i w pamięci, dzięki czemu całe wywołanie jest kwadratowe. Używanielist(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])
jest ogólnie liniowe - i jeśli potrzebujesz powtórzyć to tylko raz, możesz porzucić wywołanie,list
aby było stałe pod względem pamięci.Odpowiedzi:
Sposób sumowania tablicy w Pythonie polega na użyciu
sum
. Do innych celów można czasem użyć kombinacjireduce
(zfunctools
modułu) ioperator
modułu, np .:Należy pamiętać, że
reduce
jest to właściwie afoldl
, w terminologii Haskella. Nie ma specjalnej składni do wykonywania zawinięć, nie ma wbudowanejfoldr
, a używaniereduce
z operatorami niezespolonymi jest uważane za zły styl.Używanie funkcji wyższego rzędu jest dość pytoniczne; dobrze wykorzystuje zasadę Pythona, że wszystko jest obiektem, łącznie z funkcjami i klasami. Masz rację, że wyrażenia lambda są mile widziane przez niektórych Pythonistów, ale głównie dlatego, że nie są zbyt czytelne, gdy stają się złożone.
źródło
reduce()
jest w zasadzie ograniczona do operatorów asocjacyjnych, a we wszystkich innych przypadkach lepiej jest wyraźnie napisać pętlę akumulacji”. Tak więc jego użycie jest ograniczone, ale najwyraźniej nawet GvR musiał przyznać, że jest na tyle użyteczny, aby utrzymać go w standardowej bibliotece.Haskell
foldl (+) 0 [1,2,3,4,5]
Pyton
reduce(lambda a,b: a+b, [1,2,3,4,5], 0)
Oczywiście jest to trywialny przykład ilustrujący sprawę. W Pythonie po prostu byś to zrobił,
sum([1,2,3,4,5])
a nawet puryści Haskell na ogół wolelibysum [1,2,3,4,5]
.W przypadku nietrywialnych scenariuszy, w których nie ma oczywistej funkcji wygodnej, idiomatycznym podejściem w języku Python jest jawne zapisanie pętli for i użycie mutowalnego przypisania zmiennej zamiast używania
reduce
lubfold
.To wcale nie jest styl funkcjonalny, ale jest to sposób „pythonowy”. Python nie jest przeznaczony dla funkcjonalnych purystów. Zobacz, jak Python faworyzuje wyjątki do sterowania przepływem, aby zobaczyć, jak niefunkcjonalny jest idiomatyczny Python.
źródło
W Pythonie 3
reduce
usunięto: Uwagi do wydania . Niemniej jednak możesz skorzystać z modułu functoolsZ drugiej strony w dokumentacji preferowany jest
for
-loop zamiastreduce
, stąd:źródło
reduce
nie został usunięty ze standardowej biblioteki Python 3.reduce
przeniesiony dofunctools
modułu, jak pokazujesz.Rozpoczynając
Python 3.8
i wprowadzając wyrażenia przypisania (PEP 572) (:=
operator), które dają możliwość nazwania wyniku wyrażenia, możemy użyć wyrażenia listowego, aby powielić, jakie inne języki nazywają operacjami fold / foldleft / redukuj:Mając listę, funkcję redukującą i akumulator:
możemy złożyć
items
zf
w celu uzyskania wytworzonyaccumulation
:lub w postaci skondensowanej:
Zauważ, że w rzeczywistości jest to również operacja „scanleft”, ponieważ wynik zrozumienia listy reprezentuje stan akumulacji na każdym kroku:
źródło
Możesz także odkryć koło na nowo:
źródło
f
swoim przypadku rekurencyjnym zamieniasz argumenty na około.reduce
już oferuje (zauważ, że sygnatura funkcjiredred toreduce(function, sequence[, initial]) -> value
- zawiera również funkcjonalność polegającą na podaniu wartości początkowej dla akumulator).Niezupełnie odpowiedź na pytanie, ale jednolinijkowe dla foldl i foldr:
źródło
reduce(lambda y, x: x**y, reversed(a))
. Ma teraz bardziej naturalne użycie, działa z iteratorami i zużywa mniej pamięci.Właściwa odpowiedź na ten (zredukowany) problem brzmi: po prostu użyj pętli!
Będzie to szybsze niż redukcja, a rzeczy takie jak PyPy mogą optymalizować takie pętle.
Przy okazji, przypadek sumy należy rozwiązać za pomocą
sum
funkcjiźródło
reduce
jest powszechnym sposobem optymalizacji programu w Pythonie.product
przeciwko jednej w twoim stylu i jest szybsza (choć marginalnie).operator.add
) jako argument do redukcji: To dodatkowe wywołanie to wywołanie w C (które jest znacznie tańsze niż wywołanie w Pythonie) i oszczędza wysyłanie i interpretowanie kilku instrukcji kodu bajtowego, co może z łatwością spowodować dziesiątki wywołania funkcji.Uważam, że niektórzy respondenci tego pytania nie dostrzegli szerszego znaczenia
fold
funkcji jako narzędzia abstrakcyjnego. Tak,sum
można zrobić to samo dla listy liczb całkowitych, ale jest to trywialny przypadek.fold
jest bardziej ogólny. Jest to przydatne, gdy masz sekwencję struktur danych o różnym kształcie i chcesz jasno wyrazić agregację. Zamiast więc tworzyćfor
pętlę ze zmienną zagregowaną i ręcznie przeliczać ją za każdym razem,fold
funkcja (lub wersja Pythona, którareduce
wydaje się odpowiadać) pozwala programiście wyrazić zamiar agregacji znacznie jaśniej, po prostu zapewniając dwie rzeczy:źródło
fold
co jest trudne do wykonania w Pythonie, a następnie "fold
" to w Pythonie :-)Mogę się spóźnić na imprezę, ale możemy stworzyć własny,
foldr
używając prostego rachunku lambda i funkcji curry. Oto moja implementacja foldr w Pythonie.Mimo że realizacja jest rekurencyjna (może być powolne), wydrukuje wartości
15
i120
odpowiednioźródło