Apa itu 'Currying'?


653

Saya telah melihat referensi untuk fungsi kari di beberapa artikel dan blog tetapi saya tidak dapat menemukan penjelasan yang baik (atau setidaknya satu yang masuk akal!)


12
[Ditinggalkan sebagai komentar, karena tidak akan berguna bagi non-matematikawan.] Sesuai definisi kategori tertutup kartesius, ada keluarga tambahan adjunctions (secara alami ditentukan oleh A) antara X -> X x A dan X -> X ^ A. Hom isomorfisma (X x A, Y) <-> hom (X, Y ^ A) adalah currydan uncurryfungsi Haskell. Yang penting di sini adalah isomorfisma ini sudah diperbaiki sebelumnya, dan oleh karena itu "built-in" ke dalam bahasa.
Alexandre C.

3
Ada tutorial yang bagus di sini untuk kari di haskell learnyouahaskell.com/higher-order-functions#curried-functions komentar singkat adalah bahwa add x y = x+y(kari) berbeda dengan add (x, y)=x+y(tidak)
Jaider

Jawaban:


872

Currying adalah ketika Anda memecah fungsi yang mengambil banyak argumen menjadi serangkaian fungsi yang masing-masing hanya mengambil satu argumen. Berikut ini contoh dalam JavaScript:

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

add(3, 4); // returns 7

Ini adalah fungsi yang mengambil dua argumen, a dan b, dan mengembalikan jumlah mereka. Kami sekarang akan menjilat fungsi ini:

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

Ini adalah fungsi yang mengambil satu argumen, a, dan mengembalikan fungsi yang mengambil argumen lain, b, dan fungsi yang mengembalikan jumlah mereka.

add(3)(4);

var add3 = add(3);

add3(4);

Pernyataan pertama mengembalikan 7, seperti pernyataan add (3, 4). Pernyataan kedua mendefinisikan fungsi baru yang disebut add3 yang akan menambah 3 argumennya. Inilah yang oleh sebagian orang disebut sebagai penutupan. Pernyataan ketiga menggunakan operasi add3 untuk menambah 3 hingga 4, lagi-lagi menghasilkan 7 sebagai hasilnya.


236
Dalam arti praktis, bagaimana saya bisa memanfaatkan konsep ini?
Strawberry

43
@Strawberry, katakan misalnya bahwa Anda memiliki daftar angka [1, 2, 3, 4, 5]yang ingin Anda kalikan dengan angka acak. Di Haskell, saya bisa menulis map (* 5) [1, 2, 3, 4, 5]untuk mengalikan seluruh daftar dengan 5, dan dengan demikian menghasilkan daftar [5, 10, 15, 20, 25].
nyson

62
Saya mengerti apa fungsi peta, tetapi saya tidak yakin apakah saya mengerti poin yang Anda coba gambarkan untuk saya. Apakah Anda mengatakan fungsi peta mewakili konsep currying?
Strawberry

78
@Strawberry Argumen pertama yang mapharus menjadi fungsi yang hanya membutuhkan 1 argumen - elemen dari daftar. Perkalian - sebagai konsep matematika - adalah operasi biner; dibutuhkan 2 argumen. Namun, dalam Haskell *adalah fungsi kari, mirip dengan versi kedua adddalam jawaban ini. Hasilnya (* 5)adalah fungsi yang mengambil argumen tunggal dan mengalikannya dengan 5, dan yang memungkinkan kita untuk menggunakannya dengan peta.
Doval

26
@Strawberry Hal yang menyenangkan tentang bahasa fungsional seperti Standard ML atau Haskell adalah Anda bisa mendapatkan kari "gratis". Anda dapat mendefinisikan fungsi multi-argumen seperti yang Anda lakukan dalam bahasa lain, dan Anda secara otomatis mendapatkan versi kari, tanpa harus melemparkan sekelompok lambda sendiri. Jadi, Anda dapat menghasilkan fungsi baru yang mengurangi argumen dari fungsi apa pun yang ada tanpa banyak kerepotan atau repot, dan membuatnya mudah untuk meneruskannya ke fungsi lain.
Doval

125

Dalam aljabar fungsi, berurusan dengan fungsi yang mengambil banyak argumen (atau satu argumen yang setara dengan N-tuple) agak tidak tepat - tetapi, seperti yang dibuktikan oleh Moses Schönfinkel (dan, secara independen, Haskell Curry), itu tidak diperlukan: Anda semua butuhkan adalah fungsi yang mengambil satu argumen.

Jadi, bagaimana Anda menangani sesuatu yang secara alami Anda ungkapkan sebagai, katakanlah f(x,y),? Nah, Anda menganggap itu setara dengan f(x)(y)- f(x), sebut saja g, adalah fungsi, dan Anda menerapkan fungsi itu y. Dengan kata lain, Anda hanya memiliki fungsi yang mengambil satu argumen - tetapi beberapa fungsi tersebut mengembalikan fungsi lainnya (yang JUGA mengambil satu argumen ;-).

Seperti biasa, wikipedia memiliki entri ringkasan yang bagus tentang hal ini, dengan banyak petunjuk bermanfaat (mungkin termasuk yang berhubungan dengan bahasa favorit Anda ;-) dan juga perlakuan matematika yang sedikit lebih ketat.


1
Saya kira komentar serupa dengan saya di atas - saya belum melihat bahwa bahasa fungsional membatasi fungsi untuk mengambil satu argumen. Apakah saya salah?
Eric M

1
@hoohoo: Bahasa fungsional umumnya tidak membatasi fungsi hanya pada satu argumen. Namun, pada level matematika yang lebih rendah dan lebih jauh lebih mudah untuk berurusan dengan fungsi yang hanya membutuhkan satu argumen. (Dalam kalkulus lambda, misalnya, fungsi hanya mengambil satu argumen pada satu waktu.)
Sam DeFabbia-Kane

1
BAIK. Pertanyaan lain kemudian. Apakah pernyataan berikut ini benar? Kalkulus Lambda dapat digunakan sebagai model pemrograman fungsional tetapi pemrograman fungsional belum tentu diterapkan kalkulus lambda.
Eric M

7
Seperti yang dicatat oleh halaman wikipedia, kebanyakan bahasa FP "memperindah" atau "menambah" lambda kalkulus (misalnya dengan beberapa konstanta dan tipe data) daripada hanya "menerapkan" itu, tetapi tidak terlalu dekat. BTW, apa yang memberi Anda kesan bahwa misalnya Haskell TIDAK TIDAK "membatasi fungsi untuk mengambil satu argumen"? Tentu saja, meskipun itu tidak relevan berkat currying; misalnya div :: Integral a => a -> a -> a- perhatikan beberapa panah itu? "Map a to function mapping a to a" adalah satu bacaan ;-). Anda bisa menggunakan argumen tuple (tunggal) untuk div& c, tapi itu akan benar-benar anti-idiomatis di Haskell.
Alex Martelli

@Alex - wrt Haskell & arg count, saya belum menghabiskan banyak waktu di Haskell, dan itu semua beberapa minggu yang lalu. Jadi itu kesalahan yang mudah dibuat.
Eric M

101

Berikut ini contoh nyata:

Misalkan Anda memiliki fungsi yang menghitung gaya gravitasi yang bekerja pada suatu objek. Jika Anda tidak tahu rumusnya, Anda dapat menemukannya di sini . Fungsi ini mengambil tiga parameter yang diperlukan sebagai argumen.

Sekarang, berada di bumi, Anda hanya ingin menghitung kekuatan untuk objek di planet ini. Dalam bahasa fungsional, Anda bisa meneruskan massa bumi ke fungsi dan kemudian mengevaluasinya sebagian. Apa yang akan Anda dapatkan kembali adalah fungsi lain yang hanya membutuhkan dua argumen dan menghitung gaya gravitasi benda di bumi. Ini disebut kari.


2
Sebagai rasa ingin tahu, perpustakaan Prototipe untuk JavaScript menawarkan fungsi "kari" yang cukup banyak melakukan apa yang telah Anda jelaskan di sini: prototypejs.org/api/function/curry
shuckster


7
Ini kedengarannya seperti aplikasi parsial bagi saya. Pemahaman saya adalah bahwa jika Anda menerapkan kari, Anda dapat membuat fungsi dengan argumen tunggal dan menyusunnya untuk membentuk fungsi yang lebih rumit. Apakah saya melewatkan sesuatu?
neontapir

9
@neontapir benar. Apa yang Shea gambarkan bukanlah kari. Ini adalah aplikasi parsial. Jika fungsi tiga argumen digulung dan Anda menyebutnya sebagai f (1), apa yang Anda dapatkan kembali bukanlah fungsi dua argumen. Anda mendapatkan kembali fungsi satu argumen yang mengembalikan fungsi satu argumen lainnya. Fungsi curried hanya bisa dilewati satu argumen. Fungsi kari di PrototypeJS juga tidak kari. Ini aplikasi parsial.
MindJuice

tidak (evaluasi parsial) dan tidak (untuk kari). ini dikenal sebagai aplikasi parsial. kari diperlukan untuk mengaktifkannya.
Will Ness

47

Currying adalah transformasi yang dapat diterapkan pada fungsi untuk memungkinkan mereka mengambil satu argumen yang kurang dari sebelumnya.

Misalnya, dalam F # Anda dapat mendefinisikan fungsi sebagai berikut: -

let f x y z = x + y + z

Di sini fungsi f mengambil parameter x, y dan z dan menjumlahkannya jadi: -

f 1 2 3

Pengembalian 6.

Dari definisi kami, kami dapat mendefinisikan fungsi kari untuk f: -

let curry f = fun x -> f x

Di mana 'fun x -> fx' adalah fungsi lambda yang setara dengan x => f (x) dalam C #. Fungsi ini menginput fungsi yang ingin Anda gulirkan dan mengembalikan fungsi yang mengambil argumen tunggal dan mengembalikan fungsi yang ditentukan dengan argumen pertama yang diatur ke argumen input.

Dengan menggunakan contoh kami sebelumnya, kami dapat memperoleh kari f dengan demikian: -

let curryf = curry f

Kami kemudian dapat melakukan hal berikut: -

let f1 = curryf 1

Yang memberi kita fungsi f1 yang setara dengan f1 yz = 1 + y + z. Ini berarti kita dapat melakukan hal berikut: -

f1 2 3

Yang mengembalikan 6.

Proses ini sering dikacaukan dengan 'aplikasi fungsi parsial' yang dapat didefinisikan sebagai berikut: -

let papply f x = f x

Meskipun kami dapat memperluasnya ke lebih dari satu parameter, yaitu: -

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

Aplikasi parsial akan mengambil fungsi dan parameter dan mengembalikan fungsi yang memerlukan satu atau lebih sedikit parameter, dan seperti yang ditunjukkan dua contoh sebelumnya diimplementasikan secara langsung dalam definisi fungsi standar F # sehingga kami dapat mencapai hasil sebelumnya dengan demikian: -

let f1 = f 1
f1 2 3

Yang akan mengembalikan hasil 6.

Kesimpulannya:-

Perbedaan antara aplikasi fungsi currying dan sebagian adalah: -

Currying mengambil fungsi dan menyediakan fungsi baru menerima argumen tunggal, dan mengembalikan fungsi yang ditentukan dengan argumen pertama yang diatur ke argumen itu. Ini memungkinkan kami untuk mewakili fungsi dengan beberapa parameter sebagai serangkaian fungsi argumen tunggal . Contoh:-

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

Aplikasi fungsi parsial lebih langsung - dibutuhkan fungsi dan satu atau lebih argumen dan mengembalikan fungsi dengan argumen n pertama diset ke argumen yang ditentukan. Contoh:-

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

Jadi metode dalam C # perlu disentuh sebelum bisa diterapkan sebagian?
cdmckay

"Ini memungkinkan kami untuk mewakili fungsi dengan beberapa parameter sebagai serangkaian fungsi argumen tunggal" - sempurna, yang membersihkan semuanya dengan baik untuk saya. Terima kasih
Analisis Fuzzy

44

Ini bisa menjadi cara untuk menggunakan fungsi untuk membuat fungsi lainnya.

Dalam javascript:

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

Akan memungkinkan kita untuk menyebutnya seperti ini:

let addTen = add(10);

Ketika ini berjalan 10dilewatkan sebagai x;

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

yang berarti kami mengembalikan fungsi ini:

function(y) { return 10 + y };

Jadi saat Anda menelepon

 addTen();

Anda benar-benar menelepon:

 function(y) { return 10 + y };

Jadi, jika Anda melakukan ini:

 addTen(4)

sama dengan:

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

Jadi kami addTen()selalu menambahkan sepuluh untuk apa pun yang kami lewati. Kami dapat membuat fungsi serupa dengan cara yang sama:

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

Sekarang pertanyaan tindak lanjut yang jelas adalah mengapa Anda ingin melakukan itu? Ternyata apa yang ingin operasi x + ymenjadi satu yang dapat dilewati dengan malas, artinya kita bisa melakukan setidaknya dua hal 1. cache operasi mahal 2. mencapai abstraksi dalam paradigma fungsional.

Bayangkan fungsi kari kita terlihat seperti ini:

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

Kita dapat memanggil fungsi ini satu kali, lalu membagikan hasilnya untuk digunakan di banyak tempat, artinya kita hanya melakukan hal-hal yang mahal secara komputasi sekali:

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

Kita bisa mendapatkan abstraksi dengan cara yang sama.


5
Penjelasan langkah demi langkah terbaik dari proses sekuensial inheren yang pernah saya lihat di sini, dan mungkin jawaban terbaik, paling jelas dari semuanya.

4
@ jonsilver saya akan mengatakan sebaliknya, bukan penjelasan yang bagus. Saya setuju itu bagus dalam menjelaskan contoh yang diajukan, tetapi orang-orang cenderung default untuk berpikir, "ya sangat jelas tapi saya bisa melakukan hal yang sama dengan cara lain sehingga apa yang baik adalah kari?" Dengan kata lain, saya berharap ini memiliki konteks atau penjelasan yang cukup untuk menerangi tidak hanya bagaimana cara kerja currying, tetapi juga mengapa itu bukan pengamatan yang tidak berguna dan sepele dibandingkan dengan cara lain untuk menambahkan sepuluh.
whitneyland

29

Fungsi curried adalah fungsi dari beberapa argumen yang ditulis ulang sehingga menerima argumen pertama dan mengembalikan fungsi yang menerima argumen kedua dan seterusnya. Ini memungkinkan fungsi beberapa argumen agar sebagian argumen awalnya diterapkan sebagian.


5
"Ini memungkinkan fungsi beberapa argumen untuk menerapkan sebagian argumen awal mereka." - mengapa itu bermanfaat?
acarlon

5
@acarlon Functions sering dipanggil berulang kali dengan satu atau lebih argumen yang sama. Misalnya, jika Anda ingin mapsuatu fungsi di fatas daftar daftar xssyang dapat Anda lakukan map (map f) xss.
Jon Harrop

1
Terima kasih, itu masuk akal. Saya membaca sedikit lebih banyak dan sudah jatuh pada tempatnya.
acarlon

4
Saya pikir jawaban ini tepat dengan cara ringkas yang bagus. "Currying" adalah proses mengambil fungsi beberapa argumen dan mengubahnya menjadi fungsi yang serius yang masing-masing mengambil argumen tunggal dan mengembalikan fungsi argumen tunggal, atau dalam kasus fungsi akhir, kembalikan hasil aktual . Ini dapat dilakukan untuk Anda secara otomatis berdasarkan bahasa, atau Anda dapat memanggil fungsi curry () dalam bahasa lain untuk menghasilkan versi curry. Perhatikan bahwa memanggil fungsi curried dengan parameter tidak currying. Kari sudah terjadi.
MindJuice

7

Berikut contoh mainan dalam Python:

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

(Hanya menggunakan rangkaian via + untuk menghindari gangguan bagi programmer non-Python.)

Editing untuk menambahkan:

Lihat http://docs.python.org/library/functools.html?highlight=partial#functools.partial , yang juga menunjukkan objek parsial vs perbedaan fungsi dalam cara Python mengimplementasikan ini.


Saya tidak mendapatkan ini - Anda melakukan ini: >>> am_quote = kari (display_quote, "Alex Martelli") tetapi kemudian Anda melakukan ini selanjutnya: >>> am_quote ("currying", "Seperti biasa, wikipedia memiliki ringkasan yang bagus. .. ") Jadi Anda memiliki fungsi dengan dua argumen. Tampaknya kari harus memberi Anda tiga fungsi berbeda yang akan Anda buat?
Eric M

Saya menggunakan parsial untuk menjelajah hanya satu parameter, menghasilkan fungsi dengan dua argumen. Jika Anda mau, Anda bisa menjelajah am_quote lebih lanjut untuk membuat satu yang hanya mengutip Alex pada subjek tertentu. Backgound matematika mungkin difokuskan pada berakhir dengan fungsi dengan hanya satu parameter - tapi saya percaya memperbaiki sejumlah parameter seperti ini biasanya (jika tidak tepat dari sudut pandang matematika) disebut currying.
Anon

(Btw - '>>>' adalah prompt dalam interpreter interaktif Python, bukan bagian dari kode.)
Anon

OK terima kasih atas klarifikasi tentang args. Aku tahu tentang Python interpreter prompt, saya mencoba untuk mengutip baris tetapi diidn't kerja ;-)
Eric M

Setelah komentar Anda, saya mencari dan menemukan referensi lain, termasuk di sini di SO, untuk perbedaan antara "currying" dan. "sebagian aplikasi" sebagai respons terhadap banyak contoh penggunaan yang tidak tepat yang saya kenal. Lihat misalnya: stackoverflow.com/questions/218025/…
Anon

5

Currying menerjemahkan fungsi dari callable f(a, b, c)menjadi callable as f(a)(b)(c).

Kalau tidak currying adalah ketika Anda memecah fungsi yang mengambil beberapa argumen menjadi serangkaian fungsi yang mengambil bagian dari argumen.

Secara harfiah, kari adalah transformasi fungsi: dari satu cara memanggil ke cara lain. Dalam JavaScript, kami biasanya membuat pembungkus untuk menjaga fungsi aslinya.

Currying tidak memanggil fungsi. Itu hanya mengubahnya.

Mari kita membuat fungsi kari yang melakukan kari untuk fungsi dua argumen. Dengan kata lain, curry(f)untuk dua argumen f(a, b)diterjemahkan menjadif(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

Seperti yang Anda lihat, implementasinya adalah serangkaian pembungkus.

  • Hasilnya curry(func)adalah pembungkusfunction(a) .
  • Ketika disebut seperti sum(1), argumen disimpan di Lingkungan Leksikal, dan pembungkus baru dikembalikanfunction(b) .
  • Kemudian sum(1)(2)akhirnya panggilan function(b)menyediakan 2, dan meneruskan panggilan ke jumlah multi-argumen asli.

4

Jika Anda mengerti partialAnda setengah jalan di sana. Idenya partialadalah untuk mengajukan argumen ke suatu fungsi dan mengembalikan fungsi baru yang hanya menginginkan argumen yang tersisa. Ketika fungsi baru ini dipanggil itu termasuk argumen yang dimuat bersama dengan argumen apa pun yang disediakan untuk itu.

Dalam Clojure +adalah fungsi tetapi untuk membuat semuanya jelas:

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

Anda mungkin sadar bahwa incfungsi itu hanya menambahkan 1 ke nomor apa pun yang dilewati.

(inc 7) # => 8

Mari kita bangun sendiri menggunakan partial:

(def inc (partial add 1))

Di sini kita mengembalikan fungsi lain yang memiliki 1 dimuat ke argumen pertama add. Seperti addmengambil dua argumen, incfungsi baru hanya menginginkan bargumen - bukan 2 argumen seperti sebelumnya karena 1 telah diterapkan sebagian . Dengan demikian partialadalah alat untuk membuat fungsi-fungsi baru dengan nilai-nilai standar yang sudah ada sebelumnya. Itu sebabnya dalam fungsi bahasa fungsi sering memerintahkan argumen dari umum ke spesifik. Ini membuatnya lebih mudah untuk menggunakan kembali fungsi tersebut untuk membangun fungsi lainnya.

Sekarang bayangkan jika bahasanya cukup pintar untuk memahami secara introspektif yang addmenginginkan dua argumen. Ketika kami melewati satu argumen, bukannya menolak, bagaimana jika fungsi tersebut menerapkan sebagian argumen yang kami berikan atas nama kami, memahami bahwa kami mungkin bermaksud memberikan argumen yang lain nanti? Kami kemudian dapat mendefinisikan inctanpa menggunakan secara eksplisit partial.

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

Ini adalah cara beberapa bahasa berperilaku. Ini sangat berguna ketika seseorang ingin menyusun fungsi menjadi transformasi yang lebih besar. Ini akan membawa seseorang ke transduser.



3

Karena semua jawaban lain, currying membantu menciptakan fungsi yang diterapkan sebagian. Javascript tidak menyediakan dukungan asli untuk currying otomatis. Jadi contoh yang diberikan di atas mungkin tidak membantu dalam pengkodean praktis. Ada beberapa contoh yang sangat bagus dalam livcript (Yang pada dasarnya mengkompilasi ke js) http://livescript.net/

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

Dalam contoh di atas ketika Anda telah memberikan sedikit tidak ada dari argumen, ScriptScript menghasilkan fungsi kari baru untuk Anda (ganda)


3

Kari dapat menyederhanakan kode Anda. Ini adalah salah satu alasan utama untuk menggunakan ini. Currying adalah proses mengubah fungsi yang menerima n argumen menjadi n fungsi yang hanya menerima satu argumen.

Prinsipnya adalah untuk meneruskan argumen dari fungsi yang lewat, menggunakan properti closure (penutupan), untuk menyimpannya di fungsi lain dan memperlakukannya sebagai nilai balik, dan fungsi-fungsi ini membentuk rantai, dan argumen terakhir dilewatkan untuk menyelesaikan operasi.

Manfaatnya adalah dapat menyederhanakan pemrosesan parameter dengan menangani satu parameter pada satu waktu, yang juga dapat meningkatkan fleksibilitas dan keterbacaan program. Ini juga membuat program lebih mudah dikelola. Juga membagi kode menjadi potongan-potongan kecil akan membuatnya ramah ulang.

Sebagai contoh:

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

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

Saya juga bisa melakukan ...

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

Ini sangat bagus untuk membuat kode kompleks rapi dan penanganan metode yang tidak disinkronkan dll.


2

Fungsi curry diterapkan ke beberapa daftar argumen, bukan hanya satu.

Berikut ini adalah fungsi reguler, non-curried, yang menambahkan dua parameter Int, x dan y:

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

Ini adalah fungsi yang mirip dengan kari. Alih-alih satu daftar dua parameter Int, Anda menerapkan fungsi ini ke dua daftar masing-masing parameter Int:

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

Apa yang terjadi di sini adalah bahwa ketika Anda memohon curriedSum, Anda benar-benar mendapatkan dua pemanggilan fungsi tradisional kembali ke belakang. Doa fungsi pertama mengambil parameter Int tunggal bernama x, dan mengembalikan nilai fungsi untuk fungsi kedua. Fungsi kedua ini mengambil parameter Int y .

Berikut adalah nama fungsi firstyang sesuai dengan apa yang curriedSumakan dilakukan doa fungsi tradisional pertama :

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

Menerapkan 1 pada fungsi pertama — dengan kata lain, menjalankan fungsi pertama dan meneruskannya 1 — menghasilkan fungsi kedua:

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

Menerapkan 2 ke fungsi kedua menghasilkan hasil:

scala> second(2)
res6: Int = 3

2

Contoh currying adalah ketika memiliki fungsi, Anda hanya tahu salah satu parameter saat ini:

Sebagai contoh:

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
}

Di sini, karena Anda tidak tahu parameter kedua untuk callback ketika mengirimkannya kepada performAsyncRequest(_:)Anda, Anda harus membuat lambda / closure lain untuk mengirim yang itu ke fungsi.


apakah func callbackmengembalikan itu sendiri? Ini disebut @ callback(str)begitu let callback = callback(str), panggilan balik hanya nilai pengembalianfunc callback
nikk wong

tidak, func callback(_:data:)menerima dua parameter, di sini saya hanya memberikan satu, itu String, jadi menunggu yang berikutnya ( NSData), inilah mengapa sekarang let callbackada fungsi lain yang menunggu data untuk diteruskan
S2dent

2

Berikut adalah contoh generik dan versi terpendek untuk fungsi currying dengan n no. dari 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

Di sini Anda dapat menemukan penjelasan sederhana tentang implementasi kari di C #. Dalam komentar, saya telah mencoba menunjukkan bagaimana kari dapat bermanfaat:

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

Currying adalah salah satu fungsi tingkat tinggi dari Java Script.

Currying adalah fungsi dari banyak argumen yang ditulis ulang sehingga mengambil argumen pertama dan mengembalikan fungsi yang pada gilirannya menggunakan argumen yang tersisa dan mengembalikan nilainya.

Bingung?

Mari kita lihat sebuah contoh,

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

Ini mirip dengan fungsi kari berikut,

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

Jadi apa artinya kode ini?

Sekarang baca definisi lagi,

Currying adalah fungsi dari banyak argumen yang ditulis ulang sehingga dibutuhkan argumen pertama dan mengembalikan fungsi yang pada gilirannya menggunakan argumen yang tersisa dan mengembalikan nilainya.

Masih bingung? Biarkan saya jelaskan secara mendalam!

Ketika Anda memanggil fungsi ini,

var curryAdd = add(5);

Ini akan mengembalikan Anda fungsi seperti ini,

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

Jadi, ini disebut fungsi tingkat tinggi. Artinya, Meminta satu fungsi secara bergantian mengembalikan fungsi lainnya adalah definisi yang tepat untuk fungsi tingkat tinggi. Ini adalah keuntungan terbesar untuk legenda, Java Script. Jadi kembalilah ke kari,

Baris ini akan meneruskan argumen kedua ke fungsi curryAdd.

curryAdd(6);

yang pada gilirannya menghasilkan,

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

Semoga Anda mengerti penggunaan kari di sini. Jadi, Datang ke keuntungan,

Mengapa kari?

Itu menggunakan penggunaan kembali kode. Lebih sedikit kode, Lebih Sedikit Kesalahan. Anda mungkin bertanya bagaimana ini kode kurang?

Saya bisa membuktikannya dengan ECMA script 6 fitur fitur panah baru.

Iya! ECMA 6, berikan kami fitur luar biasa yang disebut fungsi panah,

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

Dengan bantuan fungsi panah, kita dapat menulis fungsi di atas sebagai berikut,

x=>y=>x+y

Keren kan?

Jadi, Kurang Kode dan Sedikit bug !!

Dengan bantuan fungsi tingkat tinggi ini seseorang dapat dengan mudah mengembangkan kode bebas bug.

Aku menantang kamu!

Harapan, Anda mengerti apa yang kari. Silakan berkomentar di sini jika Anda membutuhkan klarifikasi.

Terima kasih, Semoga harimu menyenangkan!


0

Ada contoh "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));
  };
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.