Jaka jest różnica między curry a nakładaniem częściowym?

438

Dość często widzę w Internecie różne skargi, że przykłady curry innych ludzi nie są curry, ale są jedynie częściowym zastosowaniem.

Nie znalazłem odpowiedniego wyjaśnienia, czym jest częściowe zastosowanie lub czym różni się od curry. Wydaje się, że istnieje ogólne zamieszanie, z równoważnymi przykładami opisywanymi jako curry w niektórych miejscach, a częściowe zastosowanie w innych.

Czy ktoś mógłby podać mi definicję obu terminów i szczegóły ich różnic?

SpoonMeiser
źródło

Odpowiedzi:

256

Curry przekształca pojedynczą funkcję z n argumentów w n funkcji z jednym argumentem każdy. Biorąc pod uwagę następującą funkcję:

function f(x,y,z) { z(x(y));}

Po curry staje się:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

Aby uzyskać pełne zastosowanie f (x, y, z), musisz to zrobić:

f(x)(y)(z);

Wiele funkcjonalnych języków pozwala pisać f x y z. Jeśli wywołujesz f x ylub f (x) (y), to otrzymujesz częściowo zastosowaną funkcję - zwracana wartość to zamknięcie lambda(z){z(x(y))}z przekazanymi wartościami x i y do f(x,y).

Jednym ze sposobów użycia aplikacji częściowej jest zdefiniowanie funkcji jako aplikacji częściowej funkcji uogólnionych, takich jak fold :

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10
Mark Cidade
źródło
40
Mówisz, że częściowa aplikacja ma miejsce, gdy wywołujesz funkcję i używasz niektórych, ale nie wszystkich wynikowych funkcji?
SpoonMeiser,
9
mniej więcej tak. Jeśli podasz tylko podzbiór argumentów, otrzymasz funkcję, która akceptuje resztę argumentów
Mark Cidade,
1
Czy zmiana funkcji f (a, b, c, d) na g (a, b) byłaby liczona jako zastosowanie częściowe? A może tylko w przypadku funkcji curry? Przepraszam, że przeszkadzam, ale szukam tutaj wyraźnej odpowiedzi.
SpoonMeiser,
2
@Mark: Wydaje mi się, że jest to tylko jedna z tych koncepcji, która we mnie uwypukla pedant - ale odwołanie się do autorytatywnych źródeł niewiele robi, by je zaspokoić, ponieważ wszystkie wydają się wskazywać na siebie nawzajem. Wikipedia nie jest tym, co uważam za wiarygodne źródło, ale rozumiem, że trudno znaleźć wiele innych. Wystarczy powiedzieć, że myślę, że oboje wiemy, o czym mówimy, i jego moc, niezależnie od tego, czy możemy się zgodzić (lub nie) w sprawie szczegółów języka ojczystego! :) Dzięki Mark!
Jason Bunting,
5
@JasonBunting, Jeśli chodzi o twój pierwszy komentarz, to, o czym mówiłeś, jest nieznośne . Curry przyjmuje funkcję multi-arg jako dane wejściowe i zwraca łańcuch funkcji 1-arg jako dane wyjściowe. De-curry przyjmuje łańcuch funkcji 1-arg jako dane wejściowe i zwraca funkcję wielu-arg jako dane wyjściowe. Jak opracowano na stackoverflow.com/a/23438430/632951
Pacerier
165

Najłatwiejszym sposobem sprawdzenia, jak się różnią, jest rozważenie prawdziwego przykładu . Załóżmy, że mamy funkcję, Addktóra przyjmuje 2 liczby jako dane wejściowe i zwraca liczbę jako dane wyjściowe, np . Add(7, 5)Zwraca 12. W tym przypadku:

  • Częściowe zastosowanie funkcji Addz wartością 7da nam nową funkcję jako wynik. Sama funkcja przyjmuje 1 liczbę jako dane wejściowe i zwraca liczbę. Takie jak:

    Partial(Add, 7); // returns a function f2 as output
    
                     // f2 takes 1 number as input and returns a number as output
    

    Więc możemy to zrobić:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Wybranie tej funkcji Addda nam nową funkcję jako wynik. Sama funkcja przyjmuje 1 liczbę jako dane wejściowe i wyjściowe jeszcze jedną nową funkcję. Ta trzecia funkcja przyjmuje następnie 1 liczbę jako dane wejściowe i zwraca liczbę jako dane wyjściowe. Takie jak:

    Curry(Add); // returns a function f2 as output
    
                // f2 takes 1 number as input and returns a function f3 as output
                // i.e. f2(number) = f3
    
                // f3 takes 1 number as input and returns a number as output
                // i.e. f3(number) = number
    

    Więc możemy to zrobić:

    f2 = Curry(Add);
    f3 = f2(7);
    f3(5); // returns 12
    

Innymi słowy, „curry” i „częściowe zastosowanie” to dwie zupełnie różne funkcje. Curry wymaga dokładnie 1 danych wejściowych, podczas gdy częściowa aplikacja wymaga 2 (lub więcej) danych wejściowych.

Mimo że oba zwracają funkcję jako wynik, zwrócone funkcje mają zupełnie inne formy, jak pokazano powyżej.

Pacerier
źródło
24
Częściowa aplikacja przekształca funkcję z n-aryna (x - n)-ary, curry z n-aryna n * 1-ary. Częściowo zastosowana funkcja ma ograniczony zakres (zastosowania), co oznacza, że Add7jest mniej wyrazista niż Add. Z drugiej strony funkcja curry jest równie wyrazista jak funkcja oryginalna.
Bob
4
Wierzę, że bardziej charakterystyczną cechą jest to, że kiedy curry f (x, y, z) => R, otrzymujemy f (x), który zwraca g (y) => h (z) => R, z których każdy pochłania pojedynczy argument; ale gdy częściowo zastosujemy f (x, y, z) jako f (x), otrzymamy g (y, z) => R, czyli z dwoma argumentami. Gdyby nie ta cecha, moglibyśmy powiedzieć, że curry jest jak częściowe zastosowanie do 0 argumentów, co pozostawia wszystkie argumenty niezwiązane; jednak w rzeczywistości f () częściowo zastosowane do 0 argumentów jest funkcją zużywającą 3 argumenty jednocześnie, w przeciwieństwie do curry f ().
Maksim Gumerov
2
Po raz kolejny poprawna odpowiedź nie jest pierwszą ani najczęściej głosowaną: proste wyjaśnienie podpisu curry vs. częściowe na końcu tej odpowiedzi jest naprawdę najłatwiejszym sposobem rozwiązania pytania.
fnl
2
Co oznacza komentarz f2(7)(5) is just a syntactic shortcut? (Wiem bardzo mało.) Nie f2zawiera / „wie o” 7?
Zach Mierzejewski
@Pacerier, czy gdzieś jest curryimplementacja (nie myśl, że to jest functools)
alancalvitti
51

Uwaga: zostało to zaczerpnięte z F # Basics doskonałego artykułu wprowadzającego dla programistów .NET, którzy zaczynają programować funkcjonalnie.

Curry oznacza rozbicie funkcji zawierającej wiele argumentów na szereg funkcji, z których każda pobiera jeden argument i ostatecznie daje taki sam wynik jak funkcja oryginalna. Curry jest prawdopodobnie najtrudniejszym tematem dla programistów początkujących w programowaniu funkcjonalnym, szczególnie dlatego, że często mylone jest z częściową aplikacją. W tym przykładzie możesz zobaczyć oba w pracy:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5

Od razu powinieneś zobaczyć zachowanie, które różni się od większości imperatywnych języków. Druga instrukcja tworzy nową funkcję o nazwie double, przekazując jeden argument do funkcji, która przyjmuje dwa. Wynikiem jest funkcja, która akceptuje jeden argument int i daje takie same wyniki, jak w przypadku wywołania pomnożenia przez x równe 2 i y równe temu argumentowi. Pod względem zachowania jest taki sam jak ten kod:

let double2 z = multiply 2 z

Często ludzie błędnie twierdzą, że mnożenie jest curry, tworząc podwójne. Ale to tylko trochę prawda. Funkcja mnożenia jest curry, ale dzieje się tak, gdy jest zdefiniowana, ponieważ funkcje w F # są domyślnie curry. Kiedy tworzona jest funkcja podwójna, dokładniej jest powiedzieć, że funkcja mnożenia jest częściowo zastosowana.

Funkcja mnożenia jest tak naprawdę serią dwóch funkcji. Pierwsza funkcja przyjmuje jeden argument int i zwraca inną funkcję, skutecznie wiążąc x z określoną wartością. Ta funkcja przyjmuje również argument int, który można traktować jako wartość powiązania z y. Po wywołaniu tej drugiej funkcji, xiy są ze sobą związane, więc wynikiem jest iloczyn xiy zdefiniowany w ciele podwójnym.

Aby utworzyć podwójnie, ocenia się pierwszą funkcję w łańcuchu funkcji mnożenia, aby częściowo zastosować mnożenie. Wynikowa funkcja ma podwójną nazwę. Kiedy wartość podwójna jest oceniana, używa swojego argumentu wraz z częściowo zastosowaną wartością do utworzenia wyniku.

dodgy_coder
źródło
33

Interesujące pytanie. Po krótkich poszukiwaniach „Aplikacja częściowo funkcjonująca nie curry” podała najlepsze znalezione wyjaśnienie. Nie mogę powiedzieć, że praktyczna różnica jest dla mnie szczególnie oczywista, ale nie jestem ekspertem od FP…

Inną przydatną stroną (której przyznam, że jeszcze nie przeczytałem w pełni) jest „Currying and Partial Application with Java Closures” .

Wygląda na to, że to bardzo myląca para terminów.

Jon Skeet
źródło
5
Pierwszy link mówi o różnicach. Oto kolejny, który uznałem za przydatny: bit.ly/CurryingVersusPartialApplication
Jason Bunting
5
Curry ma związek z krotkami (przekształcenie funkcji, która przyjmuje argument krotki w taki, który przyjmuje n osobnych argumentów i odwrotnie). Częściowe zastosowanie to możliwość zastosowania funkcji do niektórych argumentów, co daje nową funkcję dla pozostałych argumentów. Łatwo jest zapamiętać, jeśli po prostu curry == ma związek z krotkami.
Don Stewart,
9
@Jon opublikowane przez Ciebie linki mają charakter informacyjny, ale lepiej będzie rozwinąć swoją odpowiedź i dodać tutaj więcej informacji.
Zaheer Ahmed
11
Nie mogę uwierzyć, że masz 20 głosów pozytywnych za kilka linków i przyznanie, że tak naprawdę nie znasz różnicy między curry a częściowym zastosowaniem. Dobra gra, proszę pana.
AlienWebguy,
16

Odpowiedziałem na to w innym wątku https://stackoverflow.com/a/12846865/1685865 . Krótko mówiąc, aplikacja funkcji częściowej polega na naprawianiu niektórych argumentów danej funkcji wielowymiarowej, aby uzyskać inną funkcję z mniejszą liczbą argumentów, natomiast Currying polega na przekształceniu funkcji N argumentów w funkcję jednoargumentową, która zwraca funkcję jednoargumentową ... [Przykład Curry pokazano na końcu tego postu.]

Curry ma przede wszystkim znaczenie teoretyczne: obliczenia można wyrażać przy użyciu tylko jednoargumentowych funkcji (tj. Każda funkcja jest jednoargumentowa). W praktyce i jako produkt uboczny, jest to technika, która może uczynić wiele przydatnych (ale nie wszystkich) częściowych aplikacji funkcjonalnych trywialnymi, jeśli język ma funkcje curry. Ponownie, nie jest to jedyny sposób na wdrożenie częściowych aplikacji. Możesz więc spotkać się ze scenariuszami, w których częściowe stosowanie odbywa się w inny sposób, ale ludzie mylą to jako curry.

(Przykład curry)

W praktyce nie pisze się tylko

lambda x: lambda y: lambda z: x + y + z

lub równoważny javascript

function (x) { return function (y){ return function (z){ return x + y + z }}}

zamiast

lambda x, y, z: x + y + z

ze względu na Curry.

Ji Han
źródło
1
Czy powiedziałbyś wtedy, że curry jest szczególnym przypadkiem częściowego zastosowania?
SpoonMeiser,
1
@SpoonMeiser, Nie, curry nie jest szczególnym przypadkiem częściowego zastosowania: Częściowe zastosowanie funkcji 2-wejściowej nie jest tym samym, co curry funkcji. Zobacz stackoverflow.com/a/23438430/632951 .
Pacerier
10

Curry jest funkcją jednego argumentu, który przyjmuje funkcję fi zwraca nową funkcję h. Zauważ, że hpobiera argument Xi zwraca funkcję, która jest odwzorowana Yna Z:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

Częściowa aplikacja jest funkcją dwóch (lub więcej) argumentów, która pobiera funkcję fi jeden lub więcej dodatkowych argumentów fi zwraca nową funkcję g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

Zamieszanie powstaje, ponieważ przy funkcji dwóch argumentów zachowana jest następująca równość:

partial(f, a) = curry(f)(a)

Obie strony uzyskają tę samą funkcję jednego argumentu.

Równość nie jest prawdziwa dla funkcji wyższego rzędu, ponieważ w tym przypadku curry zwróci funkcję jednego argumentu, podczas gdy częściowa aplikacja zwróci funkcję wielu argumentów.

Różnica polega także na zachowaniu, podczas gdy curry przekształca rekurencyjnie całą oryginalną funkcję (jeden raz dla każdego argumentu), częściowe zastosowanie jest tylko krokiem zastępczym.

Źródło: Wikipedia Currying .

Roland
źródło
8

Różnicę między curry a częściową aplikacją można najlepiej zilustrować za pomocą następującego przykładu JavaScript:

function f(x, y, z) {
    return x + y + z;
}

var partial = f.bind(null, 1);

6 === partial(2, 3);

Częściowe zastosowanie skutkuje funkcją mniejszego arsenału; w powyższym przykładzie fma arity 3, a partialma arity 2. Co ważniejsze, częściowo zastosowana funkcja zwróci wynik natychmiast po wywołaniu , a nie kolejna funkcja w łańcuchu curry. Więc jeśli widzisz coś takiego partial(2)(3), w rzeczywistości nie jest to częściowe zastosowanie.

Dalsza lektura:

gsklee
źródło
„częściowo zastosowana funkcja zwróci wynik natychmiast po wywołaniu” - to nieprawda, prawda? kiedy częściowo zastosuję funkcję, to wyrażenie zwraca funkcję, a nie „wynik”. Ok, prawdopodobnie miałeś na myśli, że ta ostatnia funkcja, gdy zostanie wywołana z pozostałymi argumentami, zwraca wynik, w przeciwieństwie do kopania o jeden krok w dół w curry. Ale nikt tak naprawdę nie mówi, że musisz podać wszystkie pozostałe argumenty: możesz częściowo zastosować wynik częściowego zastosowania, a to znowu będzie funkcją, a nie „wynikiem”
Maksim Gumerov
6

Prosta odpowiedź

Curry: pozwala wywoływać funkcję, dzieląc ją na wiele wywołań, zapewniając jeden argument na wywołanie.

Częściowe: pozwala wywołać funkcję, dzieląc ją na wiele wywołań, zapewniając wiele argumentów na wywołanie.


Proste wskazówki

Oba pozwalają na wywołanie funkcji dostarczającej mniej argumentów (lub, lepiej, dostarczając je łącznie). W rzeczywistości oba z nich wiążą (przy każdym wywołaniu) określoną wartość z określonymi argumentami funkcji.

Rzeczywistą różnicę można zauważyć, gdy funkcja ma więcej niż 2 argumenty.


Proste e (c) (próbka)

(w JavaScript)

function process(context, success_callback, error_callback, subject) {...}

dlaczego zawsze przekazuje argumenty, takie jak kontekst i wywołania zwrotne, jeśli zawsze będą takie same? Po prostu przypisz kilka wartości dla funkcji

processSubject = _.partial(process, my_context, my_success, my_error)

i nazwij to temat1 i foobar z

processSubject('subject1');
processSubject('foobar');

Wygodne, prawda? 😉

Z curry trzeba będzie przekazywać jeden argument na raz

curriedProcess = _.curry(process);
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');
result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

Zrzeczenie się

Pominąłem wszystkie wyjaśnienia akademickie / matematyczne. Bo ja tego nie wiem. Może to pomogło 🙃

Kamafeather
źródło
4

Miałem to pytanie podczas nauki i od tego czasu zadawano mi je wiele razy. Najprostszym sposobem na opisanie różnicy jest to, że oba są takie same :) Pozwólcie, że wyjaśnię ... są oczywiście różnice.

Zarówno częściowe zastosowanie, jak i curry wymagają podania argumentów funkcji, być może nie wszystkich naraz. Dość kanonicznym przykładem jest dodanie dwóch liczb. W pseudokodzie (właściwie JS bez słów kluczowych) podstawowa funkcja może być następująca:

add = (x, y) => x + y

Gdybym chciał funkcji „addOne”, mógłbym ją częściowo zastosować lub curry:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

Teraz korzystanie z nich jest jasne:

addOneC(2) #=> 3
addOneP(2) #=> 3

Jaka jest różnica? Cóż, jest subtelny, ale częściowa aplikacja wymaga podania pewnych argumentów, a zwrócona funkcja wykona główną funkcję przy następnym wywołaniu, podczas gdy currying będzie czekał, aż będzie miał wszystkie niezbędne argumenty:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

Krótko mówiąc, użyj częściowej aplikacji do uzupełnienia niektórych wartości, wiedząc, że przy następnym wywołaniu metody zostanie ona wykonana, pozostawiając niezdefiniowane wszystkie niedostarczone argumenty; używaj curry, jeśli chcesz stale zwracać częściowo zastosowaną funkcję tyle razy, ile jest to konieczne do wypełnienia podpisu funkcji. Ostatni wymyślony przykład:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

Mam nadzieję że to pomoże!

AKTUALIZACJA: Niektóre implementacje języków lub bibliotek lib pozwolą ci przekazać arity (całkowitą liczbę argumentów w końcowej ocenie) do częściowej implementacji aplikacji, co może złączyć moje dwa opisy w mylący bałagan ... ale w tym momencie obie techniki są w dużej mierze wymienne.

Sunny-Mittal
źródło
3

Dla mnie aplikacja częściowa musi utworzyć nową funkcję, w której użyte argumenty są całkowicie zintegrowane z funkcją wynikową.

Większość języków funkcjonalnych implementuje curry, zwracając zamknięcie: nie oceniaj pod lambda, gdy zostanie częściowo zastosowane. Tak więc, aby częściowe zastosowanie było interesujące, musimy wprowadzić różnicę między curry a częściowym zastosowaniem i rozważyć częściowe zastosowanie jako curry plus ocena pod lambda.

Taoufik Dachraoui
źródło
3

Mogę się bardzo mylić, ponieważ nie mam silnego doświadczenia w matematyce teoretycznej lub programowaniu funkcjonalnym, ale z mojej krótkiej wyprawy do FP wydaje się, że curry ma tendencję do przekształcania funkcji N argumentów w N funkcji jednego argumentu, podczas gdy częściowe zastosowanie [w praktyce] działa lepiej z funkcjami wariadycznymi z nieokreśloną liczbą argumentów. Wiem, że niektóre przykłady z poprzednich odpowiedzi są sprzeczne z tym wyjaśnieniem, ale pomogło mi to najbardziej w rozróżnieniu pojęć. Rozważ ten przykład (napisany w CoffeeScript za zwięzłość, przepraszam, jeśli będzie on mylący, ale w razie potrzeby poproś o wyjaśnienie):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

Jest to oczywiście wymyślony przykład, ale zauważ, że częściowe zastosowanie funkcji, która akceptuje dowolną liczbę argumentów, pozwala nam wykonać funkcję, ale z pewnymi wstępnymi danymi. Curryowanie funkcji jest podobne, ale pozwala nam wykonać funkcję parametru N w kawałkach, dopóki wszystkie N parametrów nie zostaną uwzględnione.

Ponownie, to moje zdanie z rzeczy, które przeczytałem. Jeśli ktoś się nie zgadza, byłbym wdzięczny za komentarz, a nie za natychmiastowe zdanie. Ponadto, jeśli CoffeeScript jest trudny do odczytania, odwiedź coffeescript.org, kliknij „wypróbuj coffeescript” i wklej mój kod, aby zobaczyć skompilowaną wersję, która (mam nadzieję) może mieć większy sens. Dzięki!

Sunny-Mittal
źródło
2

Zakładam, że większość osób, które zadają to pytanie, zna już podstawowe pojęcia, więc nie trzeba o tym rozmawiać. To nakładanie się jest mylące.

Być może będziesz w stanie w pełni korzystać z tych pojęć, ale rozumiesz je razem jako pseudoatomowe amorficzne rozmycie pojęciowe. Brakuje tylko wiedzy, gdzie jest granica między nimi.

Zamiast definiować, czym jest każdy, łatwiej jest podkreślić tylko ich różnice - granicę.

Curry ma miejsce, gdy zdefiniujesz funkcję.

Częściowe zastosowanie następuje po wywołaniu funkcji.

Aplikacja ma funkcję matematyczną do wywoływania funkcji.

Częściowa aplikacja wymaga wywołania funkcji curry i uzyskania funkcji jako typu zwracanego.

Brennan Cheung
źródło
1

Są tutaj inne świetne odpowiedzi, ale uważam, że ten przykład (według mojego zrozumienia) w Javie może być korzystny dla niektórych osób:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

Curry daje więc funkcję jednoparametrową do tworzenia funkcji, przy czym częściowa aplikacja tworzy funkcję otoki, która koduje jeden lub więcej argumentów.

Jeśli chcesz skopiować i wkleić, poniższe ustawienia są bardziej głośne, ale łatwiejsze w obsłudze, ponieważ typy są łagodniejsze:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}
Sanki
źródło
Poniższe informacje dały mi kluczowy wgląd: „Więc curry daje ci funkcję jednego argumentu do tworzenia funkcji, gdzie częściowa aplikacja tworzy funkcję otoki, która koduje jeden lub więcej argumentów na sztywno”.
Roland,
0

Pisząc to, pomyliłem curry i nieświadomość. Są odwrotnymi przekształceniami funkcji. Tak naprawdę nie ma znaczenia, jak to nazywacie, o ile otrzymacie to, co reprezentuje transformacja i jej odwrotność.

Niepocieszanie nie jest zdefiniowane bardzo jasno (a raczej istnieją „sprzeczne” definicje, które oddają ducha idei). Zasadniczo oznacza to przekształcenie funkcji, która przyjmuje wiele argumentów w funkcję, która przyjmuje pojedynczy argument. Na przykład,

(+) :: Int -> Int -> Int

Jak zatem zmienić to w funkcję, która wymaga jednego argumentu? Oczywiście oszukujesz!

plus :: (Int, Int) -> Int

Zauważ, że plus przyjmuje teraz pojedynczy argument (który składa się z dwóch rzeczy). Wspaniały!

Jaki jest tego sens? Cóż, jeśli masz funkcję, która pobiera dwa argumenty i masz parę argumentów, miło jest wiedzieć, że możesz zastosować tę funkcję do argumentów i nadal uzyskać to, czego oczekujesz. W rzeczywistości instalacja hydrauliczna już istnieje, więc nie musisz robić rzeczy takich jak jawne dopasowywanie wzorców. Wszystko co musisz zrobić to:

(uncurry (+)) (1,2)

Czym jest zastosowanie funkcji częściowej? Jest to inny sposób na przekształcenie funkcji z dwóch argumentów w funkcję z jednym argumentem. Działa to jednak inaczej. Ponownie weźmy (+) jako przykład. Jak możemy przekształcić go w funkcję, która jako argument przyjmuje pojedynczy Int? Oszukujemy!

((+) 0) :: Int -> Int

To funkcja, która dodaje zero do dowolnego Int.

((+) 1) :: Int -> Int

dodaje 1 do dowolnego Int. Itd. W każdym z tych przypadków (+) jest „częściowo stosowany”.

nomen
źródło