Apa perbedaan antara aplikasi currying dan partial?

438

Saya cukup sering melihat di Internet berbagai keluhan bahwa contoh kari orang lain tidak kari, tetapi sebenarnya hanya sebagian aplikasi.

Saya belum menemukan penjelasan yang layak tentang apa aplikasi parsial itu, atau bagaimana itu berbeda dari currying. Tampaknya ada kebingungan umum, dengan contoh-contoh yang setara digambarkan sebagai kari di beberapa tempat, dan aplikasi parsial di tempat lain.

Bisakah seseorang memberi saya definisi dari kedua istilah, dan detail tentang bagaimana mereka berbeda?

SpoonMeiser
sumber

Jawaban:

256

Currying mengkonversi fungsi tunggal n argumen ke n fungsi dengan satu argumen masing-masing. Diberikan fungsi berikut:

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

Saat kari, menjadi:

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

Untuk mendapatkan aplikasi lengkap dari f (x, y, z), Anda perlu melakukan ini:

f(x)(y)(z);

Banyak bahasa fungsional memungkinkan Anda menulis f x y z. Jika Anda hanya memanggil f x yatau f (x) (y) maka Anda mendapatkan fungsi yang diterapkan sebagian — nilai kembali adalah penutupan lambda(z){z(x(y))}dengan nilai x dan y yang diteruskan f(x,y).

Salah satu cara untuk menggunakan aplikasi parsial adalah dengan mendefinisikan fungsi sebagai aplikasi parsial dari fungsi umum, seperti lipatan :

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
sumber
40
Anda mengatakan bahwa sebagian aplikasi adalah ketika Anda menjelajah suatu fungsi, dan menggunakan beberapa, tetapi tidak semua fungsi yang dihasilkan?
SpoonMeiser
9
kurang lebih, ya. Jika Anda hanya menyediakan sebagian dari argumen, Anda akan mendapatkan kembali fungsi yang menerima sisa argumen
Mark Cidade
1
Apakah mengubah fungsi f (a, b, c, d) menjadi g (a, b) dianggap sebagai aplikasi parsial? Atau hanya ketika diterapkan pada fungsi kari? Maaf menjadi sakit, tapi saya memancing jawaban eksplisit di sini.
SpoonMeiser
2
@ Mark: Saya kira ini hanyalah salah satu dari konsep-konsep yang menonjolkan pedant dalam diri saya - tetapi daya tarik terhadap sumber-sumber yang otoritatif tidak banyak memuaskan, karena semuanya tampaknya saling menunjuk satu sama lain. Wikipedia bukanlah apa yang saya anggap sebagai sumber resmi, tetapi saya mengerti bahwa sulit untuk menemukan banyak hal lain. Cukuplah untuk mengatakan bahwa saya pikir kita berdua tahu apa yang kita bicarakan dan kekuatannya, terlepas dari apakah kita dapat setuju (atau tidak setuju) pada rincian dari bahasa sehari-hari! :) Terima kasih, Mark!
Jason Bunting
5
@JasonBunting, Mengenai komentar pertama Anda, apa yang sedang Anda bicarakan adalah decurrying . Currying mengambil fungsi multi-arg sebagai input dan mengembalikan rantai fungsi 1-arg sebagai output. De-currying mengambil rantai fungsi 1-arg sebagai input dan mengembalikan fungsi multi-arg sebagai output. Seperti yang diuraikan di stackoverflow.com/a/23438430/632951
Pacerier
165

Cara termudah untuk melihat perbedaannya adalah dengan mempertimbangkan contoh nyata . Mari kita asumsikan bahwa kita memiliki fungsi Addyang mengambil 2 angka sebagai input dan mengembalikan angka sebagai output, misalnya Add(7, 5)mengembalikan 12. Pada kasus ini:

  • Menerapkan sebagian fungsi Adddengan suatu nilai 7akan memberi kita fungsi baru sebagai output. Fungsi itu sendiri membutuhkan 1 angka sebagai input dan menghasilkan angka. Dengan demikian:

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

    Jadi kita bisa melakukan ini:

    f2 = Partial(Add, 7);
    f2(5); // returns 12;
           // f2(7)(5) is just a syntactic shortcut
    
  • Currying fungsi Addakan memberi kita fungsi baru sebagai output. Fungsi itu sendiri memakan waktu 1 angka sebagai input dan output belum fungsi baru yang lain. Fungsi ketiga itu kemudian mengambil 1 angka sebagai input dan mengembalikan angka sebagai output. Dengan demikian:

    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
    

    Jadi kita bisa melakukan ini:

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

Dengan kata lain, "currying" dan "aplikasi parsial" adalah dua fungsi yang sama sekali berbeda. Currying mengambil tepat 1 input, sedangkan aplikasi parsial membutuhkan 2 (atau lebih) input.

Meskipun keduanya mengembalikan fungsi sebagai output, fungsi yang dikembalikan adalah bentuk yang sama sekali berbeda seperti yang ditunjukkan di atas.

Pacerier
sumber
25
Aplikasi parsial mengubah fungsi dari n-arymenjadi (x - n)-ary, menjelajah dari n-aryke n * 1-ary. Fungsi yang diterapkan sebagian memiliki cakupan yang lebih kecil (dari aplikasi), yaitu Add7kurang ekspresif daripada Add. Fungsi kari di sisi lain sama ekspresifnya dengan fungsi aslinya.
bob
4
Saya percaya sifat yang lebih khas adalah ketika kita mencari f (x, y, z) => R, kita mendapatkan f (x) yang mengembalikan g (y) => h (z) => R, masing-masing menggunakan argumen tunggal; tetapi ketika kita menerapkan sebagian f (x, y, z) sebagai f (x) kita mendapatkan g (y, z) => R, yaitu dengan dua argumen. Jika bukan karena sifat itu, kita bisa mengatakan bahwa currying seperti aplikasi parsial untuk 0 argumen, sehingga meninggalkan semua argumen tidak terikat; Namun pada kenyataannya f () yang diterapkan secara parsial pada 0 argumen adalah fungsi yang mengkonsumsi 3 argumen sekaligus, tidak seperti curried f ().
Maksim Gumerov
2
Sekali lagi jawaban yang benar bukanlah yang pertama atau yang paling banyak dipilih: Penjelasan sederhana tentang tanda tangan kari vs. sebagian pada akhir jawaban ini benar-benar cara termudah untuk menyelesaikan pertanyaan.
fnl
2
Apa komentarnya f2(7)(5) is just a syntactic shortcut? (Saya tahu sedikit.) Tidak f2mengandung / "tahu tentang" 7?
Zach Mierzejewski
@ Peracerier, apakah ada curryimplementasi di suatu tempat (jangan pikir itu dalam functools)
alancalvitti
51

Catatan: ini diambil dari F # Basics , artikel pengantar yang sangat baik untuk pengembang .NET masuk ke pemrograman fungsional.

Currying berarti memecah suatu fungsi dengan banyak argumen menjadi serangkaian fungsi yang masing-masing mengambil satu argumen dan pada akhirnya menghasilkan hasil yang sama dengan fungsi aslinya. Currying mungkin adalah topik yang paling menantang bagi pengembang yang baru mengenal pemrograman fungsional, terutama karena sering bingung dengan aplikasi parsial. Anda dapat melihat keduanya bekerja dalam contoh ini:

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

Segera, Anda akan melihat perilaku yang berbeda dari bahasa yang paling penting. Pernyataan kedua menciptakan fungsi baru yang disebut ganda dengan meneruskan satu argumen ke fungsi yang membutuhkan dua. Hasilnya adalah fungsi yang menerima satu argumen int dan menghasilkan output yang sama seolah-olah Anda telah memanggil multiply dengan x sama dengan 2 dan y sama dengan argumen itu. Dalam hal perilaku, sama dengan kode ini:

let double2 z = multiply 2 z

Seringkali, orang keliru mengatakan bahwa multiply adalah kari untuk membentuk dobel. Tetapi ini hanya benar. Fungsi multiply dikeringkan, tetapi itu terjadi ketika itu didefinisikan karena fungsi dalam F # dikeringkan secara default. Ketika fungsi ganda dibuat, itu lebih akurat untuk mengatakan bahwa fungsi multiply diterapkan sebagian.

Fungsi multiply sebenarnya adalah serangkaian dari dua fungsi. Fungsi pertama mengambil satu argumen int dan mengembalikan fungsi lainnya, secara efektif mengikat x ke nilai tertentu. Fungsi ini juga menerima argumen int yang dapat Anda anggap sebagai nilai untuk diikat ke y. Setelah memanggil fungsi kedua ini, x dan y keduanya terikat, sehingga hasilnya adalah produk dari x dan y sebagaimana didefinisikan dalam tubuh ganda.

Untuk membuat dobel, fungsi pertama dalam rantai fungsi gandakan dievaluasi untuk menerapkan sebagian gandakan. Fungsi yang dihasilkan diberi nama ganda. Ketika dobel dievaluasi, ia menggunakan argumennya bersama dengan nilai yang diterapkan sebagian untuk menciptakan hasilnya.

dodgy_coder
sumber
33

Pertanyaan menarik. Setelah sedikit mencari, "Aplikasi Fungsi Sebagian tidak kari" memberikan penjelasan terbaik yang saya temukan. Saya tidak bisa mengatakan bahwa perbedaan praktisnya sangat jelas bagi saya, tetapi kemudian saya bukan ahli FP ...

Halaman lain yang tampak bermanfaat (yang saya akui belum sepenuhnya saya baca) adalah "Currying and Partial Application with Java Closures" .

Memang terlihat seperti ini adalah pasangan istilah yang membingungkan, ingatlah.

Jon Skeet
sumber
5
Tautan pertama tepat tentang perbedaan. Berikut ini satu lagi yang saya temukan berguna: bit.ly/CurryingVersusPartialApplication
Jason Bunting
5
Currying berkaitan dengan tuple (mengubah fungsi yang mengambil argumen tuple menjadi yang mengambil n argumen terpisah, dan sebaliknya). Aplikasi parsial adalah kemampuan untuk menerapkan fungsi ke beberapa argumen, menghasilkan fungsi baru untuk argumen yang tersisa. Sangat mudah untuk diingat jika Anda hanya berpikir tentang menjilat == ada hubungannya dengan tuple.
Don Stewart
9
@Jon tautan yang Anda posting informatif, tetapi akan lebih baik untuk memperluas jawaban Anda dan menambahkan beberapa info lebih lanjut di sini.
Zaheer Ahmed
2
@Jon
user247702
11
Tidak percaya Anda mendapat 20 upvotes untuk beberapa tautan dan tiket masuk yang tidak Anda ketahui perbedaannya antara aplikasi kari dan sebagian. Bermain bagus, tuan.
AlienWebguy
16

Saya telah menjawab ini di utas lain https://stackoverflow.com/a/12846865/1685865 . Singkatnya, aplikasi fungsi parsial adalah tentang memperbaiki beberapa argumen dari fungsi multivariabel yang diberikan untuk menghasilkan fungsi lain dengan argumen yang lebih sedikit, sementara Currying adalah tentang mengubah fungsi argumen N menjadi fungsi unary yang mengembalikan fungsi unary ... [Contoh dari Kari ditampilkan di akhir posting ini.]

Currying sebagian besar adalah kepentingan teoretis: seseorang dapat mengekspresikan komputasi hanya menggunakan fungsi unary (yaitu setiap fungsi unary). Dalam praktik dan sebagai produk sampingan, ini adalah teknik yang dapat membuat banyak aplikasi fungsional yang berguna (tetapi tidak semua) sepele, jika bahasa tersebut memiliki fungsi yang digulung. Sekali lagi, ini bukan satu-satunya cara untuk mengimplementasikan aplikasi parsial. Jadi Anda bisa menemukan skenario di mana aplikasi sebagian dilakukan dengan cara lain, tetapi orang-orang salah mengartikannya sebagai Currying.

(Contoh Kari)

Dalam praktiknya orang tidak akan hanya menulis

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

atau javascript yang setara

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

dari pada

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

demi Currying.

Ji Han
sumber
1
Apakah Anda akan mengatakan bahwa kari adalah kasus khusus aplikasi parsial?
SpoonMeiser
1
@SpoonMeiser, Tidak, currying bukan kasus spesifik aplikasi parsial: Aplikasi parsial dari fungsi 2-input tidak sama dengan currying function. Lihat stackoverflow.com/a/23438430/632951 .
Pacerier
10

Currying adalah fungsi dari satu argumen yang mengambil fungsi fdan mengembalikan fungsi baru h. Catatan yang hmengambil argumen dari Xdan mengembalikan fungsi yang memetakan Yke Z:

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

Aplikasi parsial adalah fungsi dari dua (atau lebih) argumen yang mengambil fungsi fdan satu atau lebih argumen tambahan ke fdan mengembalikan fungsi baru g:

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

Kebingungan muncul karena dengan fungsi dua argumen kesetaraan berikut berlaku:

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

Kedua belah pihak akan menghasilkan fungsi satu argumen yang sama.

Kesetaraan tidak benar untuk fungsi arity yang lebih tinggi karena dalam hal ini currying akan mengembalikan fungsi satu argumen, sedangkan aplikasi parsial akan mengembalikan fungsi multi-argumen.

Perbedaannya juga dalam perilaku, sedangkan kari mengubah seluruh fungsi asli secara rekursif (satu kali untuk setiap argumen), aplikasi parsial hanyalah penggantian satu langkah.

Sumber: Currying Wikipedia .

Roland
sumber
8

Perbedaan antara aplikasi kari dan sebagian dapat digambarkan dengan baik melalui contoh JavaScript berikut ini:

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

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

6 === partial(2, 3);

Aplikasi parsial menghasilkan fungsi arity yang lebih kecil; dalam contoh di atas, fmemiliki arity 3 sementara partialhanya arity 2. Lebih penting lagi, fungsi yang diterapkan sebagian akan mengembalikan hasilnya segera setelah dipanggil , bukan fungsi lain di rantai currying. Jadi jika Anda melihat sesuatu seperti partial(2)(3), itu bukan aplikasi parsial dalam kenyataannya.

Bacaan lebih lanjut:

gsklee
sumber
"fungsi yang diterapkan sebagian akan mengembalikan hasilnya segera setelah dipanggil" - itu tidak benar, kan? ketika saya menerapkan sebagian fungsi, ekspresi itu mengembalikan fungsi, bukan "hasil". Ok, Anda mungkin bermaksud bahwa fungsi yang terakhir ini, ketika dipanggil dengan argumen yang tersisa, mengembalikan hasilnya, tidak seperti menggali satu langkah ke dalam currying. Tapi tidak ada yang benar-benar mengatakan Anda harus menentukan semua argumen yang tersisa: Anda dapat sebagian menerapkan hasil aplikasi parsial, dan itu sekali lagi akan menjadi fungsi, bukan "hasil"
Maksim Gumerov
6

Jawaban sederhana

Curry: memungkinkan Anda memanggil suatu fungsi, membaginya dalam beberapa panggilan, memberikan satu argumen per-panggilan.

Partial: memungkinkan Anda memanggil suatu fungsi, membaginya dalam beberapa panggilan, memberikan beberapa argumen per-panggilan.


Petunjuk sederhana

Keduanya memungkinkan Anda untuk memanggil fungsi yang menyediakan lebih sedikit argumen (atau, lebih baik, menyediakannya secara kumulatif). Sebenarnya keduanya mengikat (pada setiap panggilan) nilai tertentu untuk argumen fungsi tertentu.

Perbedaan nyata dapat dilihat ketika fungsi memiliki lebih dari 2 argumen.


Sederhana e (c) (sampel)

(dalam Javascript)

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

mengapa selalu melewati argumen, seperti konteks dan panggilan balik, jika mereka akan selalu sama? Ikat saja beberapa nilai untuk fungsi tersebut

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

dan menyebutnya di subject1 dan foobar dengan

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

Nyaman bukan? 😉

Dengan kari Anda harus melewati satu argumen per waktu

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');

Penolakan

Saya melewatkan semua penjelasan akademik / matematika. Karena aku tidak tahu itu. Mungkin itu membantu 🙃

Kamafather
sumber
4

Saya punya banyak pertanyaan saat belajar dan sejak itu telah ditanyakan berkali-kali. Cara paling sederhana yang bisa saya jelaskan perbedaannya adalah bahwa keduanya sama-sama :) Mari saya jelaskan ... jelas ada perbedaan.

Baik aplikasi parsial maupun currying melibatkan penyediaan argumen ke suatu fungsi, mungkin tidak sekaligus. Contoh yang cukup kanonik adalah menambahkan dua angka. Dalam pseudocode (sebenarnya JS tanpa kata kunci), fungsi dasar mungkin sebagai berikut:

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

Jika saya ingin fungsi "addOne", saya bisa menerapkannya sebagian atau menjilatnya:

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

Sekarang menggunakannya jelas:

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

Jadi apa bedanya? Yah, itu halus, tetapi aplikasi parsial melibatkan penyediaan beberapa argumen dan fungsi yang dikembalikan kemudian akan menjalankan fungsi utama pada doa berikutnya sedangkan currying akan terus menunggu sampai memiliki semua argumen yang diperlukan:

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

Singkatnya, gunakan aplikasi parsial untuk mengambil beberapa nilai, mengetahui bahwa saat Anda memanggil metode berikutnya, itu akan mengeksekusi, meninggalkan semua argumen yang tidak ditentukan tidak ditentukan; gunakan currying ketika Anda ingin terus mengembalikan fungsi yang diterapkan sebagian sebanyak yang diperlukan untuk memenuhi tanda tangan fungsi. Satu contoh akhir yang dibuat:

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

Semoga ini membantu!

UPDATE: Beberapa bahasa atau implementasi lib akan memungkinkan Anda untuk lulus arity (jumlah total argumen dalam evaluasi akhir) ke implementasi aplikasi parsial yang dapat mengubah dua deskripsi saya menjadi kekacauan yang membingungkan ... tetapi pada saat itu, kedua teknik tersebut sebagian besar dipertukarkan.

cerah-mittal
sumber
3

Bagi saya sebagian aplikasi harus membuat fungsi baru di mana argumen yang digunakan sepenuhnya terintegrasi ke dalam fungsi yang dihasilkan.

Sebagian besar bahasa fungsional menerapkan currying dengan mengembalikan penutupan: jangan mengevaluasi di bawah lambda ketika diterapkan sebagian. Jadi, untuk aplikasi parsial menjadi menarik, kita perlu membuat perbedaan antara currying dan aplikasi parsial dan menganggap aplikasi parsial sebagai currying plus evaluasi di bawah lambda.

Taoufik Dachraoui
sumber
3

Saya bisa saja sangat salah di sini, karena saya tidak memiliki latar belakang yang kuat dalam matematika teoretis atau pemrograman fungsional, tetapi dari perampokan singkat saya ke FP, tampaknya kari cenderung mengubah fungsi argumen N menjadi fungsi N dari satu argumen, sedangkan aplikasi parsial [dalam praktek] bekerja lebih baik dengan fungsi variad dengan jumlah argumen yang tidak ditentukan. Saya tahu beberapa contoh dalam jawaban sebelumnya menentang penjelasan ini, tetapi itu telah membantu saya untuk paling memisahkan konsep. Pertimbangkan contoh ini (ditulis dalam CoffeeScript untuk ringkasnya, permintaan maaf saya jika membingungkan lebih lanjut, tapi tolong minta klarifikasi, jika perlu):

# 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

Ini jelas merupakan contoh yang dibuat-buat, tetapi perhatikan bahwa sebagian menerapkan fungsi yang menerima sejumlah argumen memungkinkan kita untuk mengeksekusi fungsi tetapi dengan beberapa data awal. Currying suatu fungsi serupa tetapi memungkinkan kita untuk mengeksekusi fungsi N-parameter dalam potongan sampai, tetapi hanya sampai, semua parameter N diperhitungkan.

Sekali lagi, ini adalah pendapat saya dari hal-hal yang saya baca. Jika ada yang tidak setuju, saya akan sangat menghargai komentar mengapa daripada langsung downvote. Juga, jika CoffeeScript sulit dibaca, silakan kunjungi coffeescript.org, klik "coba coffeescript" dan tempelkan kode saya untuk melihat versi yang dikompilasi, yang (semoga) lebih masuk akal. Terima kasih!

cerah-mittal
sumber
2

Saya akan menganggap kebanyakan orang yang mengajukan pertanyaan ini sudah akrab dengan konsep dasar sehingga mereka tidak perlu membicarakan hal itu. Tumpang tindihlah yang merupakan bagian yang membingungkan.

Anda mungkin dapat sepenuhnya menggunakan konsep-konsep ini, tetapi Anda memahaminya bersama-sama sebagai pengaburan konseptual amorf pseudo-atomik ini. Yang hilang adalah mengetahui di mana batas di antara mereka.

Daripada mendefinisikan apa masing-masing, lebih mudah untuk menyoroti perbedaan mereka saja — batasnya.

Currying adalah saat Anda mendefinisikan fungsi.

Aplikasi Parsial adalah ketika Anda memanggil fungsi.

Aplikasi adalah matematika-berbicara untuk memanggil suatu fungsi.

Aplikasi parsial membutuhkan memanggil fungsi curried dan mendapatkan fungsi sebagai tipe kembali.

Brennan Cheung
sumber
1

Ada jawaban bagus lainnya di sini tapi saya percaya contoh ini (sesuai pemahaman saya) di Jawa mungkin bermanfaat bagi sebagian orang:

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 );
}

Jadi currying memberi Anda fungsi satu argumen untuk membuat fungsi, di mana sebagian aplikasi membuat fungsi wrapper yang mengkode satu atau lebih argumen.

Jika Anda ingin menyalin & menempel, yang berikut ini lebih ribut tetapi lebih ramah untuk digunakan karena jenisnya lebih lunak:

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 );
}
Kereta luncur
sumber
Berikut ini memberi saya wawasan kunci: "Jadi currying memberi Anda fungsi satu argumen untuk membuat fungsi, di mana sebagian aplikasi membuat fungsi wrapper yang mengkode satu atau lebih argumen."
Roland
0

Dalam menulis ini, saya bingung kari dan tidak. Mereka adalah transformasi terbalik pada fungsi. Tidak masalah apa yang Anda panggil, selama Anda mendapatkan apa yang diwakili oleh transformasi dan kebalikannya.

Ketidaksesuaian tidak didefinisikan dengan sangat jelas (atau lebih tepatnya, ada definisi "bertentangan" yang semuanya menangkap semangat gagasan). Pada dasarnya, ini berarti mengubah fungsi yang mengambil banyak argumen menjadi fungsi yang membutuhkan satu argumen. Sebagai contoh,

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

Sekarang, bagaimana Anda mengubahnya menjadi fungsi yang membutuhkan satu argumen? Kamu curang, tentu saja!

plus :: (Int, Int) -> Int

Perhatikan bahwa plus sekarang mengambil argumen tunggal (yang terdiri dari dua hal). Super!

Apa gunanya ini? Nah, jika Anda memiliki fungsi yang membutuhkan dua argumen, dan Anda memiliki sepasang argumen, senang mengetahui bahwa Anda dapat menerapkan fungsi ke argumen, dan masih mendapatkan apa yang Anda harapkan. Dan, sebenarnya, pipa ledeng untuk melakukannya sudah ada, sehingga Anda tidak perlu melakukan hal-hal seperti pencocokan pola eksplisit. Yang harus Anda lakukan adalah:

(uncurry (+)) (1,2)

Jadi apa aplikasi fungsi parsial? Ini adalah cara yang berbeda untuk mengubah fungsi dalam dua argumen menjadi fungsi dengan satu argumen. Ini bekerja secara berbeda. Sekali lagi, mari kita ambil (+) sebagai contoh. Bagaimana kita mengubahnya menjadi fungsi yang menggunakan Int tunggal sebagai argumen? Kami curang!

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

Itulah fungsi yang menambahkan nol ke Int.

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

menambahkan 1 ke Int. Dll. Dalam setiap kasus ini, (+) "diterapkan sebagian".

nomen
sumber