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!)
add x y = x+y
(kari) berbeda dengan add (x, y)=x+y
(tidak)
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!)
add x y = x+y
(kari) berbeda dengan add (x, y)=x+y
(tidak)
Jawaban:
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.
[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]
.
map
harus 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 add
dalam jawaban ini. Hasilnya (* 5)
adalah fungsi yang mengambil argumen tunggal dan mengalikannya dengan 5, dan yang memungkinkan kita untuk menggunakannya dengan peta.
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.
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.
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.
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
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 10
dilewatkan 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 + y
menjadi 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.
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.
map
suatu fungsi di f
atas daftar daftar xss
yang dapat Anda lakukan map (map f) xss
.
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.
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.
curry(func)
adalah pembungkusfunction(a)
.sum(1)
, argumen disimpan di Lingkungan Leksikal, dan pembungkus baru dikembalikanfunction(b)
.sum(1)(2)
akhirnya panggilan function(b)
menyediakan 2, dan meneruskan panggilan ke jumlah multi-argumen asli.Jika Anda mengerti partial
Anda setengah jalan di sana. Idenya partial
adalah 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 inc
fungsi 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 add
mengambil dua argumen, inc
fungsi baru hanya menginginkan b
argumen - bukan 2 argumen seperti sebelumnya karena 1 telah diterapkan sebagian . Dengan demikian partial
adalah 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 add
menginginkan 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 inc
tanpa 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.
Saya menemukan artikel ini, dan artikel yang dirujuknya, berguna, untuk lebih memahami currying: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Seperti yang disebutkan lainnya, ini hanyalah cara untuk memiliki fungsi satu parameter.
Ini berguna karena Anda tidak perlu berasumsi berapa banyak parameter yang akan dilewatkan, jadi Anda tidak perlu fungsi 2 parameter, 3 parameter dan 4 parameter.
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)
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.
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 first
yang sesuai dengan apa yang curriedSum
akan 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
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.
func callback
mengembalikan itu sendiri? Ini disebut @ callback(str)
begitu let callback = callback(str)
, panggilan balik hanya nilai pengembalianfunc callback
func callback(_:data:)
menerima dua parameter, di sini saya hanya memberikan satu, itu String
, jadi menunggu yang berikutnya ( NSData
), inilah mengapa sekarang let callback
ada fungsi lain yang menunggu data untuk diteruskan
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)());
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);
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!
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));
};
curry
danuncurry
fungsi Haskell. Yang penting di sini adalah isomorfisma ini sudah diperbaiki sebelumnya, dan oleh karena itu "built-in" ke dalam bahasa.