Widziałem odniesienia do funkcji curry w kilku artykułach i blogach, ale nie mogę znaleźć dobrego wyjaśnienia (lub przynajmniej takiego, które ma sens!)
652
Widziałem odniesienia do funkcji curry w kilku artykułach i blogach, ale nie mogę znaleźć dobrego wyjaśnienia (lub przynajmniej takiego, które ma sens!)
curry
iuncurry
funkcjami Haskella. Ważne jest tutaj to, że te izomorfizmy są wcześniej ustalone, a zatem „wbudowane” w język.add x y = x+y
(curry) różni się odadd (x, y)=x+y
(niezasłużonego)Odpowiedzi:
Curry polega na rozbiciu funkcji, która bierze wiele argumentów na szereg funkcji, z których każda przyjmuje tylko jeden argument. Oto przykład w JavaScript:
Jest to funkcja, która pobiera dwa argumenty, aib, i zwraca ich sumę. Będziemy teraz curry tę funkcję:
Jest to funkcja, która pobiera jeden argument, a, i zwraca funkcję, która przyjmuje inny argument, b, i ta funkcja zwraca ich sumę.
Pierwsza instrukcja zwraca 7, podobnie jak instrukcja add (3, 4). Druga instrukcja definiuje nową funkcję o nazwie add3, która doda 3 do swojego argumentu. To właśnie niektórzy nazywają zamknięciem. Trzecia instrukcja używa operacji add3, aby dodać 3 do 4, ponownie wytwarzając w rezultacie 7.
źródło
[1, 2, 3, 4, 5]
, które chcesz pomnożyć przez dowolną liczbę. W Haskell mogę pisać,map (* 5) [1, 2, 3, 4, 5]
aby pomnożyć całą listę5
, a tym samym wygenerować listę[5, 10, 15, 20, 25]
.map
musi być funkcja, która pobiera tylko 1 argument - element z listy. Mnożenie - jako koncepcja matematyczna - jest operacją binarną; wymaga 2 argumentów. Jednak w Haskell*
jest funkcja curry, podobna do drugiej wersjiadd
tej odpowiedzi. Wynikiem(* 5)
jest funkcja, która pobiera pojedynczy argument i mnoży go przez 5, co pozwala nam używać go z mapą.W algebrze funkcji radzenie sobie z funkcjami, które pobierają wiele argumentów (lub równoważny jeden argument, którym jest N-krotka) jest nieco nieeleganckie - ale, jak udowodnił Moses Schönfinkel (i, niezależnie, Haskell Curry), nie jest potrzebne: wszystko potrzebne są funkcje, które biorą jeden argument.
Jak radzisz sobie z czymś, co naturalnie wyrazisz, powiedzmy
f(x,y)
,? Cóż, traktujesz to jako ekwiwalentf(x)(y)
-f(x)
, nazwij tog
, jest funkcją i zastosujesz tę funkcję doy
. Innymi słowy, masz tylko funkcje, które pobierają jeden argument - ale niektóre z nich zwracają inne funkcje (które RÓWNIEŻ biorą jeden argument ;-).Jak zwykle, wikipedia ma ładny opis podsumowujący, zawierający wiele przydatnych wskazówek (prawdopodobnie w tym dotyczących twoich ulubionych języków ;-), a także nieco bardziej rygorystyczne podejście matematyczne.
źródło
div :: Integral a => a -> a -> a
- zwrócić uwagę na te wiele strzałek? „Mapowanie do funkcji mapowanie do a” to jedno czytanie ;-). Ty mógł używać (single) argumentu krotny dladiv
& C, ale to byłoby naprawdę anty-idiomatyczne w Haskell.Oto konkretny przykład:
Załóżmy, że masz funkcję, która oblicza siłę grawitacji działającą na obiekt. Jeśli nie znasz tej formuły, możesz ją znaleźć tutaj . Ta funkcja przyjmuje trzy niezbędne parametry jako argumenty.
Teraz, będąc na ziemi, chcesz tylko obliczyć siły dla obiektów na tej planecie. W języku funkcjonalnym możesz przekazać masę ziemi do funkcji, a następnie częściowo ją ocenić. To, co otrzymasz, to kolejna funkcja, która bierze tylko dwa argumenty i oblicza siłę grawitacji obiektów na Ziemi. Nazywa się to curry.
źródło
Curry to transformacja, którą można zastosować do funkcji, aby pozwolić im wziąć o jeden argument mniej niż poprzednio.
Na przykład w F # można zdefiniować funkcję w ten sposób:
Tutaj funkcja f bierze parametry x, y i z i sumuje je razem, więc:
Zwraca 6.
Z naszej definicji możemy zatem zdefiniować funkcję curry dla f: -
Gdzie „fun x -> fx” jest funkcją lambda równoważną x => f (x) w C #. Ta funkcja wprowadza funkcję, którą chcesz curry i zwraca funkcję, która przyjmuje pojedynczy argument i zwraca określoną funkcję z pierwszym argumentem ustawionym na argument wejściowy.
Korzystając z naszego poprzedniego przykładu, możemy uzyskać curry f:
Następnie możemy wykonać następujące czynności:
Co daje nam funkcję f1, która jest równoważna f1 yz = 1 + y + z. Oznacza to, że możemy wykonać następujące czynności:
Który zwraca 6.
Proces ten jest często mylony z „aplikacją funkcji częściowej”, którą można zdefiniować w następujący sposób:
Chociaż możemy rozszerzyć go na więcej niż jeden parametr, tj .:
Częściowa aplikacja pobierze funkcję i parametr (y) i zwróci funkcję, która wymaga jednego lub więcej parametrów, a jak pokazują poprzednie dwa przykłady, jest zaimplementowana bezpośrednio w standardowej definicji funkcji F #, abyśmy mogli osiągnąć poprzedni wynik w ten sposób:
Co zwróci wynik 6.
Podsumowując:
Różnica między curry a aplikacją funkcji częściowej polega na tym, że:
Currying przyjmuje funkcję i udostępnia nową funkcję, która akceptuje pojedynczy argument i zwraca określoną funkcję z pierwszym argumentem ustawionym na ten argument. To pozwala nam reprezentować funkcje z wieloma parametrami jako serię funkcji pojedynczego argumentu . Przykład:-
Aplikacja funkcji częściowej jest bardziej bezpośrednia - pobiera funkcję i jeden lub więcej argumentów i zwraca funkcję z pierwszymi n argumentami ustawionymi na n podanych argumentów. Przykład:-
źródło
Może to być sposób używania funkcji do tworzenia innych funkcji.
W javascript:
Pozwoliłby nam to tak nazwać:
Kiedy to działa,
10
jest przekazywane jakox
;co oznacza, że zwrócono nam tę funkcję:
Więc kiedy zadzwonisz
naprawdę dzwonisz:
Więc jeśli to zrobisz:
jest taki sam jak:
Dlatego
addTen()
zawsze dodajemy dziesięć do wszystkiego, co przekazujemy. Możemy wykonywać podobne funkcje w ten sam sposób:Teraz oczywistym pytaniem jest, dlaczego, u licha, chciałbyś to zrobić? Zmienia to, co było chętną operacją,
x + y
w tę, którą można leniwie przejść, co oznacza, że możemy zrobić co najmniej dwie rzeczy: 1. buforować kosztowne operacje 2. osiągnąć abstrakcje w paradygmacie funkcjonalnym.Wyobraź sobie, że nasza funkcja curry wyglądała tak:
Możemy wywołać tę funkcję jeden raz, a następnie przekazać wyniki, aby użyć ich w wielu miejscach, co oznacza, że wykonujemy drogie obliczeniowo tylko raz:
Możemy uzyskać abstrakcje w podobny sposób.
źródło
Funkcja curry jest funkcją kilku przerobionych argumentów, tak że akceptuje pierwszy argument i zwraca funkcję, która akceptuje drugi argument i tak dalej. Pozwala to funkcjom kilku argumentów częściowo zastosować niektóre z ich początkowych argumentów.
źródło
map
funkcjif
na liście list,xss
możesz to zrobićmap (map f) xss
.Oto zabawkowy przykład w Pythonie:
(Wystarczy użyć konkatenacji za pomocą +, aby uniknąć rozproszenia uwagi dla programistów niebędących Pythonami).
Edycja do dodania:
Zobacz http://docs.python.org/library/functools.html?highlight=partial#functools.partial , który pokazuje także częściowe rozróżnienie obiektu na funkcję w sposób, w jaki Python implementuje to.
źródło
Curry tłumaczy funkcję z wywoływalnej
f(a, b, c)
na wywoływalną jakof(a)(b)(c)
.W przeciwnym razie curry ma miejsce wtedy, gdy rozkładasz funkcję, która bierze wiele argumentów w szereg funkcji, które biorą udział w argumentach.
Dosłownie curry to transformacja funkcji: z jednego sposobu wywoływania w inny. W JavaScript zwykle tworzymy opakowanie, aby zachować oryginalną funkcję.
Curry nie wywołuje funkcji. Po prostu to przekształca.
Zróbmy funkcję curry, która wykonuje curry dla funkcji dwuargumentowych. Innymi słowy,
curry(f)
dla dwóch argumentówf(a, b)
tłumaczy to naf(a)(b)
Jak widać, implementacja jest serią opakowań.
curry(func)
jest opakowaniefunction(a)
.sum(1)
argument jest zapisywany w środowisku leksykalnym i zwracane jest nowe opakowaniefunction(b)
.sum(1)(2)
końcu wywołujefunction(b)
podając 2 i przekazuje wywołanie do oryginalnej sumy wielu argumentów.źródło
Jeśli rozumiesz,
partial
że jesteś w połowie drogi. Chodzi opartial
to, aby wstępnie zastosować argumenty do funkcji i zwrócić nową funkcję, która chce tylko pozostałych argumentów. Po wywołaniu tej nowej funkcji zawiera ona wstępnie załadowane argumenty wraz z wszelkimi dostarczonymi argumentami.W Clojure
+
jest funkcja, ale aby wyjaśnić wszystko wyraźnie:Możesz być świadomy, że
inc
funkcja po prostu dodaje 1 do dowolnej liczby, którą przekazuje.Zbudujmy go sami, używając
partial
:Tutaj zwracamy inną funkcję, która ma 1 załadowany do pierwszego argumentu
add
. Ponieważadd
przyjmuje dwa argumenty, nowainc
funkcja chce tylkob
argumentu, a nie 2 argumentów, jak poprzednio, ponieważ 1 został już częściowo zastosowany.partial
Jest to zatem narzędzie, za pomocą którego można tworzyć nowe funkcje z domyślnymi wartościami domyślnymi. Dlatego w funkcjonalnym języku funkcje często porządkują argumenty od ogólnych do szczegółowych. Ułatwia to ponowne użycie takich funkcji do zbudowania innych funkcji.Teraz wyobraź sobie, że język był wystarczająco inteligentny, aby zrozumieć introspekcyjnie,
add
wymagając dwóch argumentów. Kiedy przekazaliśmy mu jeden argument, zamiast się wahać, co jeśli funkcja częściowo zastosowała argument, przekazaliśmy go w naszym imieniu, rozumiejąc, że prawdopodobnie zamierzamy podać drugi argument później? Możemy wtedy zdefiniowaćinc
bez wyraźnego użyciapartial
.Tak zachowują się niektóre języki. Jest to wyjątkowo przydatne, gdy chce się łączyć funkcje w większe transformacje. Doprowadziłoby to do przetworników.
źródło
Znalazłem ten artykuł i artykuł, do którego się odwołuje, użyteczne, aby lepiej zrozumieć curry: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Jak wspomniano inni, jest to tylko sposób na posiadanie funkcji jednego parametru.
Jest to przydatne, ponieważ nie musisz zakładać, ile parametrów zostanie przekazanych, więc nie potrzebujesz funkcji 2 parametrów, 3 parametrów i 4 parametrów.
źródło
Jak wszystkie inne odpowiedzi curry pomaga tworzyć częściowo zastosowane funkcje. Javascript nie zapewnia natywnej obsługi automatycznego curry. Przykłady podane powyżej mogą nie pomóc w praktycznym kodowaniu. Istnieje doskonały przykład w livecript (który zasadniczo kompiluje się do js) http://livescript.net/
W powyższym przykładzie, gdy podałeś mniej argumentów, skrypt życia generuje dla ciebie nową funkcję curry (podwójnie)
źródło
Curry może uprościć kod. Jest to jeden z głównych powodów, aby z tego korzystać. Curry to proces przekształcania funkcji, która akceptuje n argumentów w n funkcji, które akceptują tylko jeden argument.
Zasadą jest przekazywanie argumentów przekazanej funkcji za pomocą właściwości closure (closure), aby przechowywać je w innej funkcji i traktować jako wartość zwracaną, a funkcje te tworzą łańcuch, a końcowe argumenty są przekazywane do uzupełnienia operacja.
Zaletą tego jest to, że może uprościć przetwarzanie parametrów, radząc sobie z jednym parametrem na raz, co może również poprawić elastyczność i czytelność programu. Dzięki temu program jest łatwiejszy w zarządzaniu. Również podzielenie kodu na mniejsze części sprawi, że będzie on łatwy do ponownego użycia.
Na przykład:
Mogę też zrobić ...
Jest to bardzo świetne do porządkowania złożonego kodu i obsługi niezsynchronizowanych metod itp.
źródło
Funkcja curry jest stosowana do wielu list argumentów zamiast tylko jednej.
Oto zwykła funkcja bez curry, która dodaje dwa parametry Int, xiy:
Oto podobna funkcja, która jest curry. Zamiast jednej listy dwóch parametrów Int zastosujesz tę funkcję do dwóch list jednego parametru Int każdy:
To, co się tutaj dzieje, polega na tym, że kiedy wywołujesz
curriedSum
, faktycznie otrzymujesz dwie tradycyjne wywołania funkcji z powrotem do tyłu. Pierwsze wywołanie funkcji przyjmuje pojedynczy parametr Int o nazwiex
i zwraca wartość funkcji dla drugiej funkcji. Ta druga funkcja przyjmuje parametr Inty
.Oto funkcja o nazwie
first
, która w duchu robi to, cocurriedSum
zrobiłaby pierwsza tradycyjna funkcja :Zastosowanie 1 do pierwszej funkcji - innymi słowy, wywołanie pierwszej funkcji i przekazanie jej 1 - daje drugą funkcję:
Zastosowanie 2 do drugiej funkcji daje wynik:
źródło
Przykładem curry może być posiadanie funkcji, które znasz tylko jeden z parametrów:
Na przykład:
Tutaj, ponieważ nie znasz drugiego parametru dla wywołania zwrotnego podczas wysyłania go
performAsyncRequest(_:)
, musisz utworzyć kolejną lambdę / zamknięcie, aby wysłać ten parametr do funkcji.źródło
func callback
powrót się? Nazywa się to @,callback(str)
więclet callback = callback(str)
oddzwanianie jest tylko wartościąfunc callback
func callback(_:data:)
akceptuje dwa parametry, tutaj podaję tylko jeden,String
więc czeka na następny (NSData
), dlatego terazlet callback
jest kolejna funkcja czekająca na przesłanie danychOto przykład ogólnej i najkrótszej wersji funkcji curry z nr n. params.
źródło
Tutaj możesz znaleźć proste wyjaśnienie implementacji curry w C #. W komentarzach starałem się pokazać, jak curry może być przydatne:
źródło
Curry to jedna z wyższych funkcji Java Script.
Curry jest funkcją wielu argumentów, która jest przepisywana w taki sposób, że pobiera pierwszy argument i zwraca funkcję, która z kolei używa pozostałych argumentów i zwraca wartość.
Zmieszany?
Zobaczmy przykład
Jest to podobne do następującej funkcji curry,
Co oznacza ten kod?
Teraz przeczytaj ponownie definicję,
Curry jest funkcją wielu argumentów, która jest przepisywana tak, że pobiera pierwszy argument i zwraca funkcję, która z kolei wykorzystuje pozostałe argumenty i zwraca wartość.
Wciąż zmieszany? Pozwól mi wyjaśnić głęboko!
Po wywołaniu tej funkcji
Zwróci ci taką funkcję,
Nazywa się to funkcjami wyższego rzędu. Oznacza to, że wywoływanie jednej funkcji po kolei zwraca inną funkcję jest dokładną definicją funkcji wyższego rzędu. Jest to największa zaleta legendy Java Script. Wróć więc do curry
Ten wiersz przekaże drugi argument do funkcji curryAdd.
co z kolei powoduje,
Mam nadzieję, że rozumiesz użycie curry tutaj. Wracając do zalet
Dlaczego curry?
Wykorzystuje możliwość ponownego użycia kodu. Mniej kodu, mniej błędów. Możesz zapytać, jak to jest mniej kodu?
Mogę to udowodnić za pomocą skryptu ECMA 6 nowych funkcji strzałek funkcji.
Tak! ECMA 6, zapewnij nam wspaniałą funkcję zwaną funkcjami strzałek,
Za pomocą funkcji strzałki możemy napisać powyższą funkcję w następujący sposób,
Fajnie prawda?
Więc mniej kodu i mniej błędów !!
Za pomocą funkcji wyższego rzędu można łatwo opracować kod wolny od błędów.
Wyzywam cię!
Mam nadzieję, że zrozumiałeś, co jest curry. Skomentuj tutaj, jeśli potrzebujesz wyjaśnień.
Dzięki. Miłego dnia!
źródło
Istnieje przykład „Currying in ReasonML”.
źródło