Co to jest „curry”?


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!)


12
[Pozostawione jako komentarz, ponieważ będzie to bezużyteczne dla nie-matematyków.] Zgodnie z definicją kartezjańskiej kategorii zamkniętej istnieje stała rodzina adiustacji (naturalnie sparametryzowana przez A) między X -> X x A i X -> X ^ A. Izomorfizmy hom (X x A, Y) <-> hom (X, Y ^ A) są funkcjami curryi uncurryfunkcjami Haskella. Ważne jest tutaj to, że te izomorfizmy są wcześniej ustalone, a zatem „wbudowane” w język.
Alexandre C.,

3
Jest tu fajny samouczek na temat curry w haskell learnyouahaskell.com/higher-order-functions#curried-functions krótkie komentarze mówią, że add x y = x+y(curry) różni się od add (x, y)=x+y(niezasłużonego)
Jaider

Odpowiedzi:


872

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:

function add (a, b) {
  return a + b;
}

add(3, 4); // returns 7

Jest to funkcja, która pobiera dwa argumenty, aib, i zwraca ich sumę. Będziemy teraz curry tę funkcję:

function add (a) {
  return function (b) {
    return a + b;
  }
}

Jest to funkcja, która pobiera jeden argument, a, i zwraca funkcję, która przyjmuje inny argument, b, i ta funkcja zwraca ich sumę.

add(3)(4);

var add3 = add(3);

add3(4);

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.


235
W sensie praktycznym, jak mogę wykorzystać tę koncepcję?
Strawberry

43
@ Strawberry, powiedzmy na przykład, że masz listę liczb [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].
nyson

62
Rozumiem, co robi funkcja mapy, ale nie jestem pewien, czy rozumiem punkt, który próbujesz dla mnie zilustrować. Czy mówisz, że funkcja mapy reprezentuje koncepcję curry?
Strawberry

78
@Strawberry Pierwszym argumentem mapmusi 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 wersji addtej odpowiedzi. Wynikiem (* 5)jest funkcja, która pobiera pojedynczy argument i mnoży go przez 5, co pozwala nam używać go z mapą.
Doval

26
@Strawberry Zaletą funkcjonalnych języków, takich jak Standard ML lub Haskell, jest to, że można uzyskać curry „za darmo”. Możesz zdefiniować funkcję wielu argumentów, tak jak w każdym innym języku, i automatycznie otrzymasz jej curry wersję, bez konieczności wrzucania kilku lambdas. Możesz więc tworzyć nowe funkcje, które pobierają mniej argumentów z dowolnej istniejącej funkcji bez większego zamieszania i kłopotów, a to ułatwia przekazywanie ich do innych funkcji.
Doval

125

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 ekwiwalent f(x)(y)- f(x), nazwij to g, jest funkcją i zastosujesz tę funkcję do y. 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.


1
Przypuszczam, że podobny komentarz do mojego powyżej - nie widziałem, aby języki funkcjonalne ograniczały funkcje do pobierania pojedynczego argumentu. Czy się mylę?
Eric M

1
@hoohoo: Języki funkcjonalne zasadniczo nie ograniczają funkcji do jednego argumentu. Jednak na niższym, bardziej matematycznym poziomie, o wiele łatwiej jest poradzić sobie z funkcjami, które wymagają tylko jednego argumentu. (Na przykład w rachunku lambda funkcje biorą tylko jeden argument na raz.)
Sam DeFabbia-Kane

1
OK. Kolejne pytania. Czy poniższe stwierdzenie jest prawdziwym stwierdzeniem? Rachunek lambda może być stosowany jako model programowania funkcjonalnego, ale programowanie funkcjonalne niekoniecznie jest stosowane rachunek lambda.
Eric M

7
Jak zauważają strony wikipedii, większość języków FP „upiększa” lub „rozszerza” rachunek lambda (np. Z pewnymi stałymi i typami danych), a nie tylko „stosuje”, ale nie jest tak blisko. BTW, co sprawia wrażenie, że np. Haskell NIE „ogranicza funkcji do przyjmowania pojedynczego argumentu”? Z pewnością tak, chociaż nie ma to znaczenia dzięki curry; np. 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 dla div& C, ale to byłoby naprawdę anty-idiomatyczne w Haskell.
Alex Martelli,

@Alex - wrt Haskell & arg count, nie spędziłem dużo czasu na Haskell, a to wszystko było kilka tygodni temu. Łatwo więc było popełnić błąd.
Eric M

100

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.


2
Jako ciekawostkę biblioteka Prototype dla JavaScript oferuje funkcję „curry”, która wykonuje dokładnie to, co tu wyjaśniłeś: prototypejs.org/api/function/curry
shuckster


7
Dla mnie to brzmi jak częściowe zastosowanie. Rozumiem, że jeśli zastosujesz curry, możesz tworzyć funkcje za pomocą jednego argumentu i komponować je w celu utworzenia bardziej skomplikowanych funkcji. Czy coś brakuje?
neontapir

9
@neontapir jest poprawny. To, co opisała Shea, nie curry. Jest to częściowe zastosowanie. Jeśli funkcja z trzema argumentami jest curry i wywołujesz ją jako f (1), to, co otrzymujesz, nie jest funkcją z dwoma argumentami. Otrzymujesz funkcję jednoparametrową, która zwraca inną funkcję jednoparametrową. Do funkcji curry można zawsze przekazać tylko jeden argument. Funkcja curry w PrototypeJS również nie curry. To częściowe zastosowanie.
MindJuice

nie (do częściowej oceny) i nie (do curry). jest to znane jako częściowe zastosowanie. curry jest potrzebne, aby to umożliwić.
Czy Ness

47

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:

let f x y z = x + y + z

Tutaj funkcja f bierze parametry x, y i z i sumuje je razem, więc:

f 1 2 3

Zwraca 6.

Z naszej definicji możemy zatem zdefiniować funkcję curry dla f: -

let curry f = fun x -> f x

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:

let curryf = curry f

Następnie możemy wykonać następujące czynności:

let f1 = curryf 1

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:

f1 2 3

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:

let papply f x = f x

Chociaż możemy rozszerzyć go na więcej niż jeden parametr, tj .:

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

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:

let f1 = f 1
f1 2 3

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:-

let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6

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:-

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6

Więc metody w C # musiałyby być curry, zanim będą mogły być częściowo zastosowane?
cdmckay

„To pozwala nam przedstawiać funkcje z wieloma parametrami jako serię funkcji pojedynczego argumentu” - idealnie, co dla mnie wszystko to ładnie wyjaśniło. Dzięki
Analiza rozmyta

44

Może to być sposób używania funkcji do tworzenia innych funkcji.

W javascript:

let add = function(x){
  return function(y){ 
   return x + y
  };
};

Pozwoliłby nam to tak nazwać:

let addTen = add(10);

Kiedy to działa, 10jest przekazywane jako x;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

co oznacza, że ​​zwrócono nam tę funkcję:

function(y) { return 10 + y };

Więc kiedy zadzwonisz

 addTen();

naprawdę dzwonisz:

 function(y) { return 10 + y };

Więc jeśli to zrobisz:

 addTen(4)

jest taki sam jak:

function(4) { return 10 + 4} // 14

Dlatego addTen()zawsze dodajemy dziesięć do wszystkiego, co przekazujemy. Możemy wykonywać podobne funkcje w ten sam sposób:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

Teraz oczywistym pytaniem jest, dlaczego, u licha, chciałbyś to zrobić? Zmienia to, co było chętną operacją, x + yw 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:

let doTheHardStuff = function(x) {
  let z = doSomethingComputationallyExpensive(x)
  return function (y){
    z + y
  }
}

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:

let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)

Możemy uzyskać abstrakcje w podobny sposób.


5
Najlepsze wyjaśnienie krok po kroku z natury sekwencyjnego procesu, jaki widziałem tutaj, i być może najlepsza, najbardziej wyjaśniająca odpowiedź z całej partii.

4
@jonsilver Powiedziałbym coś przeciwnego, niezbyt dobre wytłumaczenie. Zgadzam się, że jest dobry w wyjaśnianiu podanego przykładu, ale ludzie mają tendencję do domyślnego myślenia: „tak, zupełnie jasne, ale mógłbym zrobić to samo w inny sposób, więc co dobrego curry?” Innymi słowy, żałuję, że nie ma wystarczającego kontekstu lub wyjaśnienia, aby wyjaśnić nie tylko działanie curry, ale także dlaczego nie jest to bezużyteczna i trywialna obserwacja w porównaniu do innych sposobów dodawania dziesięciu.
whitneyland

29

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.


5
„Pozwala to funkcjom kilku argumentów częściowo zastosować niektóre z ich początkowych argumentów.” - dlaczego to jest korzystne?
acarlon

5
@acarlon Funkcje są często wywoływane wielokrotnie, z co najmniej jednym argumentem takim samym. Na przykład, jeśli chcesz mapfunkcji fna liście list, xssmożesz to zrobić map (map f) xss.
Jon Harrop,

1
Dziękuję, to ma sens. Zrobiłem trochę więcej czytania i wszystko się ułożyło.
acarlon

4
Sądzę, że ta odpowiedź dobrze i zwięźle ją poprawnie przedstawia. „Curry” to proces przejmowania funkcji wielu argumentów i przekształcania jej w szereg funkcji, z których każda pobiera pojedynczy argument i zwraca funkcję pojedynczego argumentu, aw przypadku funkcji końcowej zwraca rzeczywisty wynik . Można to zrobić automatycznie dla danego języka lub wywołać funkcję curry () w innych językach, aby wygenerować wersję curry. Zauważ, że wywołanie funkcji curry z parametrem nie jest curry. Curry już się wydarzyło.
MindJuice

7

Oto zabawkowy przykład w Pythonie:

>>> from functools import partial as curry

>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
        print who, 'said regarding', subject + ':'
        print '"' + quote + '"'


>>> display_quote("hoohoo", "functional languages",
           "I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."

>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")

>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."

(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.


Nie rozumiem tego - robisz to: >>> am_quote = curry (display_quote, "Alex Martelli"), ale potem robisz to dalej: >>> am_quote ("curry", "Jak zwykle, wikipedia ma ładne podsumowanie. .. ") Więc masz funkcję z dwoma argumentami. Wydaje się, że curry powinno dać ci trzy różne zabawy, które komponowałbyś?
Eric M

Używam częściowego do curry tylko jednego parametru, tworząc funkcję z dwoma argumentami. Jeśli chcesz, możesz dalej curry am_quote, aby stworzyć taki, który cytował Alexa tylko na określony temat. Tło matematyki może koncentrować się na tworzeniu funkcji z tylko jednym parametrem - ale uważam, że ustalenie dowolnej liczby takich parametrów jest powszechnie (jeśli nieprecyzyjnie z punktu widzenia matematyki) nazywane curry.
Anon

(btw - „>>>” jest pytaniem w interaktywnym interprecie Pythona, nie stanowi części kodu.)
Anon

OK, dziękuję za wyjaśnienie na temat argumentów. Wiem o wierszu interpretera Pythona, próbowałem zacytować wiersze, ale to nie zadziałało ;-)
Eric M

Po twoim komentarzu szukałem i znalazłem inne odniesienia, w tym tutaj na SO, do różnicy między „curry” a. „częściowe zastosowanie” w odpowiedzi na wiele przypadków nieprecyzyjnego użycia, które znam. Patrz na przykład: stackoverflow.com/questions/218025/…
Anon

5

Curry tłumaczy funkcję z wywoływalnej f(a, b, c)na wywoływalną jako f(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ów f(a, b)tłumaczy to naf(a)(b)

function curry(f) { // curry(f) does the currying transform
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let carriedSum = curry(sum);

alert( carriedSum(1)(2) ); // 3

Jak widać, implementacja jest serią opakowań.

  • Rezultatem curry(func)jest opakowanie function(a).
  • Po wywołaniu jak sum(1)argument jest zapisywany w środowisku leksykalnym i zwracane jest nowe opakowanie function(b).
  • Następnie w sum(1)(2)końcu wywołuje function(b)podając 2 i przekazuje wywołanie do oryginalnej sumy wielu argumentów.

4

Jeśli rozumiesz, partialże jesteś w połowie drogi. Chodzi o partialto, 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:

(defn add [a b] (+ a b))

Możesz być świadomy, że incfunkcja po prostu dodaje 1 do dowolnej liczby, którą przekazuje.

(inc 7) # => 8

Zbudujmy go sami, używając partial:

(def inc (partial add 1))

Tutaj zwracamy inną funkcję, która ma 1 załadowany do pierwszego argumentu add. Ponieważ addprzyjmuje dwa argumenty, nowa incfunkcja chce tylko bargumentu, a nie 2 argumentów, jak poprzednio, ponieważ 1 został już częściowo zastosowany. partialJest 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, addwymagają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ć incbez wyraźnego użycia partial.

(def inc (add 1)) #partial is implied

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.



3

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/

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
double = times 2
double 5         #=> 10

W powyższym przykładzie, gdy podałeś mniej argumentów, skrypt życia generuje dla ciebie nową funkcję curry (podwójnie)


3

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:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

Mogę też zrobić ...

var minus7 = curryMinus(7);
minus7(3);
minus7(5);

Jest to bardzo świetne do porządkowania złożonego kodu i obsługi niezsynchronizowanych metod itp.


2

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:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

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:

scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3

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 nazwie xi zwraca wartość funkcji dla drugiej funkcji. Ta druga funkcja przyjmuje parametr Int y.

Oto funkcja o nazwie first, która w duchu robi to, co curriedSumzrobiłaby pierwsza tradycyjna funkcja :

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Zastosowanie 1 do pierwszej funkcji - innymi słowy, wywołanie pierwszej funkcji i przekazanie jej 1 - daje drugą funkcję:

scala> val second = first(1)
second: (Int) => Int = <function1>

Zastosowanie 2 do drugiej funkcji daje wynik:

scala> second(2)
res6: Int = 3

2

Przykładem curry może być posiadanie funkcji, które znasz tylko jeden z parametrów:

Na przykład:

func aFunction(str: String) {
    let callback = callback(str) // signature now is `NSData -> ()`
    performAsyncRequest(callback)
}

func callback(str: String, data: NSData) {
    // Callback code
}

func performAsyncRequest(callback: NSData -> ()) {
    // Async code that will call callback with NSData as parameter
}

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.


jest func callbackpowrót się? Nazywa się to @, callback(str)więc let callback = callback(str)oddzwanianie jest tylko wartością func callback
zwracaną

nie, func callback(_:data:)akceptuje dwa parametry, tutaj podaję tylko jeden, Stringwięc czeka na następny ( NSData), dlatego teraz let callbackjest kolejna funkcja czekająca na przesłanie danych
S2dent

2

Oto przykład ogólnej i najkrótszej wersji funkcji curry z nr n. params.

const add = a => b => b ? add(a + b) : a; 

const add = a => b => b ? add(a + b) : a; 
console.log(add(1)(2)(3)(4)());


1

Tutaj możesz znaleźć proste wyjaśnienie implementacji curry w C #. W komentarzach starałem się pokazać, jak curry może być przydatne:

public static class FuncExtensions {
    public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        return x1 => x2 => func(x1, x2);
    }
}

//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);

//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times 
//with different input parameters.

int result = func(1);

1

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

function add(a,b)
    {
        return a+b;
    }
add(5,6);

Jest to podobne do następującej funkcji curry,

function add(a)
    {
        return function(b){
            return a+b;
        }
    }
var curryAdd = add(5);
curryAdd(6);

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

var curryAdd = add(5);

Zwróci ci taką funkcję,

curryAdd=function(y){return 5+y;}

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.

curryAdd(6);

co z kolei powoduje,

curryAdd=function(6){return 5+6;}
// Which results in 11

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,

function add(a)
    {
        return function(b){
            return a+b;
        }
    }

Za pomocą funkcji strzałki możemy napisać powyższą funkcję w następujący sposób,

x=>y=>x+y

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!


0

Istnieje przykład „Currying in ReasonML”.

let run = () => {
    Js.log("Curryed function: ");
    let sum = (x, y) => x + y;
    Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
    let per2 = sum(2);
    Printf.printf("per2(3) : %d\n", per2(3));
  };
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.