Kari JavaScript: apa saja aplikasi praktisnya?


172

Saya tidak berpikir saya sudah gulai kari. Saya mengerti apa yang dilakukannya, dan bagaimana melakukannya. Saya tidak bisa memikirkan situasi yang akan saya gunakan.

Di mana Anda menggunakan currying di JavaScript (atau di mana perpustakaan utama menggunakannya)? Manipulasi DOM atau contoh pengembangan aplikasi umum diterima.

Salah satu jawaban menyebutkan animasi. Fungsinya seperti slideUp, fadeInmengambil elemen sebagai argumen dan biasanya merupakan fungsi kari yang mengembalikan fungsi orde tinggi dengan built-in "fungsi animasi". Mengapa itu lebih baik daripada hanya menerapkan fungsi yang lebih tinggi dengan beberapa default?

Apakah ada kekurangan untuk menggunakannya?

Seperti yang diminta di sini adalah beberapa sumber daya yang baik pada currying JavaScript:

Saya akan menambahkan lebih banyak saat mereka muncul di komentar.


Jadi, menurut jawaban, aplikasi currying dan parsial pada umumnya adalah teknik kenyamanan.

Jika Anda sering "memperbaiki" fungsi tingkat tinggi dengan memanggilnya dengan konfigurasi yang sama, Anda dapat mengubah (atau menggunakan parsial Resig) fungsi tingkat yang lebih tinggi untuk membuat metode pembantu yang sederhana dan ringkas.


dapatkah Anda menambahkan tautan ke sumber yang menjelaskan apa itu JS currying? tutorial atau posting blog akan bagus.
Eric Schoonover

2
svendtofte.com berkepanjangan tetapi jika Anda melewatkan seluruh bagian dari "Kursus kilat di ML" dan mulai lagi di "Cara menulis JavaScript yang dikerjakan", ini menjadi pengantar yang bagus untuk mencari kari di js.
danio

1
Ini adalah titik awal yang baik untuk memahami apa sebenarnya aplikasi kari dan sebagian: slid.es/gsklee/functional-programming-in-5-minutes
gsklee

1
Tautan ke svendtofte.comtampak sudah mati - menemukannya di mesin WayBack meskipun di web.archive.org/web/20130616230053/http : //www.svendtofte.com/... Maaf, blog.morrisjohns.com/javascript_closures_for_dummies tampaknya mati juga
phatskat

1
BTW, versi parsial Resig kurang (tentu bukan "pada uang") karena kemungkinan akan gagal jika salah satu argumen pra-diinisialisasi ("kari") diberi nilai yang tidak ditentukan . Siapa pun yang tertarik dengan fungsi kari yang baik harus mendapatkan yang asli dari funcitonal.js Oliver Steele , karena tidak memiliki masalah itu.
RobG

Jawaban:


35

@Hank Gay

Menanggapi komentar EmbiggensTheMind:

Saya tidak bisa memikirkan contoh di mana kari — dengan sendirinya — berguna dalam JavaScript; itu adalah teknik untuk mengubah panggilan fungsi dengan beberapa argumen menjadi rantai panggilan fungsi dengan argumen tunggal untuk setiap panggilan, tetapi JavaScript mendukung banyak argumen dalam panggilan fungsi tunggal.

Dalam JavaScript — dan saya mengasumsikan sebagian besar bahasa aktual lainnya (bukan lambda calculus) —tetapi umumnya dikaitkan dengan aplikasi parsial. John Resig menjelaskannya dengan lebih baik , tetapi intinya adalah yang memiliki beberapa logika yang akan diterapkan pada dua argumen atau lebih, dan Anda hanya tahu nilai untuk beberapa argumen tersebut.

Anda dapat menggunakan sebagian aplikasi / currying untuk memperbaiki nilai-nilai yang diketahui dan mengembalikan fungsi yang hanya menerima yang tidak diketahui, untuk dipanggil nanti ketika Anda benar-benar memiliki nilai yang ingin Anda sampaikan. Ini memberikan cara yang bagus untuk menghindari pengulangan ketika Anda akan memanggil JavaScript yang sama berulang-ulang dengan semua nilai yang sama kecuali satu. Untuk mencuri contoh John:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );

2
Saya telah mencoba menjelaskannya dengan beberapa contoh dan demo di artikel tertulis saya.
Zaheer Ahmed

6
Ini benar-benar jawaban yang buruk. Kari tidak ada hubungannya dengan aplikasi parsial. Currying memungkinkan komposisi fungsi. Komposisi fungsi memungkinkan penggunaan kembali fungsi. Penggunaan kembali fungsi meningkatkan pemeliharaan kode. Sangat mudah!

2
@ tuan Sir, Anda adalah jawaban yang sangat buruk. Currying jelas tentang membuat fungsi lebih enak. Anda jelas melewatkan poinnya.
Callat

112

Inilah penggunaan kari yang menarik DAN praktis dalam JavaScript yang menggunakan penutupan :

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

Ini bergantung pada curryekstensi Function, meskipun seperti yang Anda lihat hanya menggunakan apply(tidak ada yang terlalu mewah):

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}

5
Ini bagus! Saya melihatnya mirip dengan kutipan lisp yang mengatakan "Lisp adalah bahasa pemrograman yang dapat diprogram"
santiagobasulto

2
Menarik, tetapi contoh ini tampaknya tidak berhasil. offset+inputakan ada undefined + 1.60936dalam milesToKmcontoh Anda ; yang menghasilkan NaN.
Nathan Long

3
@Nathan - offset tidak dapat ditentukan - standarnya adalah 0
AngusC

6
Dari apa yang saya baca (baru saja), "curry" biasanya bukan bagian dari trik trik Function, kecuali jika Anda menggunakan perpustakaan Prototype atau menambahkannya sendiri. Sangat keren.
Roboprog

11
Hal yang sama dapat dicapai dengan metode ES5 bind (). Bind menciptakan fungsi baru yang ketika dipanggil memanggil fungsi asli dengan konteks argumen pertamanya dan dengan urutan argumen berikutnya (mendahului setiap yang diteruskan ke fungsi baru). Jadi Anda dapat melakukan ... var milesToKm = converter.bind (this, 'km', 1.60936); atau var farenheitToCelsius = converter.bind (ini, 'derajat C', 0,5556, -32); Argumen pertama, konteksnya, ini, tidak relevan di sini sehingga Anda hanya bisa lulus tidak terdefinisi. Tentu saja Anda perlu menambah prototipe Function dasar dengan metode bind Anda sendiri untuk fallback non ES5
hacklikecrack

7

Saya menemukan fungsi yang menyerupai python functools.partiallebih berguna dalam JavaScript:

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

Mengapa Anda ingin menggunakannya? Situasi umum di mana Anda ingin menggunakan ini adalah ketika Anda ingin mengikat thisfungsi ke suatu nilai:

var callback = partialWithScope(Object.function, obj);

Sekarang ketika panggilan balik dipanggil, thismenunjuk ke obj. Ini berguna dalam situasi acara atau untuk menghemat ruang karena biasanya membuat kode lebih pendek.

Currying mirip dengan parsial dengan perbedaan bahwa fungsi currying mengembalikan hanya menerima satu argumen (sejauh yang saya mengerti).


7

Setuju dengan Hank Gay - Ini sangat berguna dalam bahasa pemrograman fungsional tertentu yang sebenarnya - karena itu adalah bagian yang perlu. Misalnya, di Haskell Anda tidak dapat mengambil beberapa parameter ke suatu fungsi - Anda tidak dapat melakukannya dalam pemrograman fungsional murni. Anda mengambil satu param pada satu waktu dan membangun fungsi Anda. Dalam JavaScript tidak perlu, meskipun contoh-contoh seperti "konverter" dibuat-buat. Inilah kode konverter yang sama, tanpa perlu kari:

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

Saya sangat berharap Douglas Crockford, dalam "JavaScript: The Good Parts", telah menyebutkan beberapa sejarah dan penggunaan sebenarnya dari currying daripada sambutannya yang begitu saja. Untuk waktu yang paling lama setelah membaca itu, saya bingung, sampai saya mempelajari pemrograman Fungsional dan menyadari dari mana asalnya.

Setelah beberapa pemikiran lagi, saya berpendapat ada satu kasus penggunaan yang valid untuk currying dalam JavaScript: jika Anda mencoba menulis menggunakan teknik pemrograman fungsional murni menggunakan JavaScript. Sepertinya kasus penggunaan yang jarang terjadi.


2
Kode Anda jauh lebih mudah dipahami daripada milik Prisoner Zero dan memecahkan masalah yang sama tanpa menjilat atau rumit apa pun. Anda mendapat 2 jempol dan dia memiliki hampir 100. Sosok.
DR01D

2

Bukan sihir atau apa pun ... hanya singkatan yang menyenangkan untuk fungsi anonim.

partial(alert, "FOO!") setara dengan function(){alert("FOO!");}

partial(Math.max, 0) sesuai dengan function(x){return Math.max(0, x);}

Panggilan ke parsial ( terminologi MochiKit . Saya pikir beberapa perpustakaan lain memberikan fungsi metode .curry yang melakukan hal yang sama) terlihat sedikit lebih bagus dan kurang berisik daripada fungsi anonim.


1

Sedangkan untuk perpustakaan yang menggunakannya, selalu ada Fungsional .

Kapan itu berguna di JS? Mungkin saat yang sama itu berguna dalam bahasa modern lainnya, tetapi satu-satunya waktu saya dapat melihat diri saya menggunakannya adalah bersamaan dengan aplikasi parsial.


Terima kasih Hank - tolong bisakah Anda mengembangkannya ketika itu berguna secara umum?
Dave Nolan

1

Saya akan mengatakan bahwa, paling mungkin, semua perpustakaan animasi di JS menggunakan currying. Daripada harus lulus untuk setiap panggilan satu set elemen yang terkena dampak dan fungsi, menjelaskan bagaimana elemen harus berperilaku, ke fungsi urutan yang lebih tinggi yang akan memastikan semua hal waktu, umumnya lebih mudah bagi pelanggan untuk dirilis, seperti API publik beberapa berfungsi seperti "slideUp", "fadeIn" yang hanya menggunakan elemen sebagai argumen, dan itu hanya beberapa fungsi kari mengembalikan fungsi urutan tinggi dengan "fungsi animasi" bawaan bawaan.


Mengapa lebih baik untuk menjelajah fungsi yang lebih tinggi daripada sekadar menyebutnya dengan beberapa default?
Dave Nolan

1
Karena sangat modular untuk dapat menjelajah "doMathOperation" dengan penambahan / perkalian / kuadrat / modulus / pendidikan lainnya sesuai keinginan daripada membayangkan semua "default" yang dapat didukung fungsi yang lebih tinggi.
gizmo

1

Ini sebuah contoh.

Saya sedang menginstruksikan sekelompok bidang dengan JQuery sehingga saya dapat melihat apa yang sedang dilakukan pengguna. Kode ini terlihat seperti ini:

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(Untuk pengguna non-JQuery, saya katakan bahwa setiap kali beberapa bidang mendapatkan atau kehilangan fokus, saya ingin fungsi trackActivity () dipanggil. Saya juga dapat menggunakan fungsi anonim, tetapi saya harus menduplikasinya 4 kali, jadi saya mengeluarkannya dan menamainya.)

Sekarang ternyata salah satu bidang itu perlu ditangani secara berbeda. Saya ingin dapat memberikan parameter pada salah satu panggilan yang akan diteruskan ke infrastruktur pelacakan kami. Dengan kari, aku bisa.


1

Fungsi JavaScript disebut lamda dalam bahasa fungsional lainnya. Ini dapat digunakan untuk menyusun api baru (fungsi yang lebih kuat atau kompleks) berdasarkan input sederhana pengembang lain. Kari adalah salah satu tekniknya. Anda dapat menggunakannya untuk membuat api yang disederhanakan untuk memanggil api yang kompleks. Jika Anda adalah develper yang menggunakan api yang disederhanakan (misalnya Anda menggunakan jQuery untuk melakukan manipulasi sederhana), Anda tidak perlu menggunakan kari. Tetapi jika Anda ingin membuat api yang disederhanakan, kari adalah teman Anda. Anda harus menulis kerangka javascript (seperti jQuery, mootools) atau pustaka, maka Anda dapat menghargai kekuatannya. Saya menulis fungsi kari yang disempurnakan, di http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html. Anda tidak perlu menggunakan metode kari untuk melakukan kari, itu hanya membantu melakukan kari, tetapi Anda selalu dapat melakukannya secara manual dengan menulis fungsi A () {} untuk mengembalikan fungsi B lainnya () {}. Untuk membuatnya lebih menarik, gunakan fungsi B () untuk mengembalikan fungsi C lainnya ().


1

Saya tahu utasnya yang lama tetapi saya harus menunjukkan bagaimana ini digunakan di perpustakaan javascript:

Saya akan menggunakan perpustakaan lodash.js untuk menggambarkan konsep-konsep ini secara konkret.

Contoh:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || '); 
}

Aplikasi Sebagian:

var partialFnA = _.partial(fn, 1,3);

Kari:

var curriedFn = _.curry(fn);

Mengikat:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

pemakaian:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

perbedaan:

setelah currying kita mendapatkan fungsi baru tanpa parameter pre bound.

setelah aplikasi parsial kita mendapatkan fungsi yang terikat dengan beberapa parameter prebound.

dalam mengikat kita dapat mengikat konteks yang akan digunakan untuk menggantikan 'ini', jika tidak terikat default fungsi apa pun akan menjadi lingkup jendela.

Nasihat: Tidak perlu menemukan kembali roda. Aplikasi parsial / penjilidan / kari sangat terkait. Anda dapat melihat perbedaan di atas. Gunakan makna ini di mana saja dan orang-orang akan mengenali apa yang Anda lakukan tanpa masalah dalam pemahaman ditambah Anda harus menggunakan lebih sedikit kode.


0

Saya setuju bahwa kadang-kadang Anda ingin membuat bola bergulir dengan menciptakan fungsi semu yang akan selalu memiliki nilai argumen pertama diisi. Untungnya, saya menemukan perpustakaan JavaScript baru yang disebut jPaq (h ttp: // jpaq.org/ ) yang menyediakan fungsi ini. Hal terbaik tentang perpustakaan adalah fakta bahwa Anda dapat mengunduh bangunan Anda sendiri yang hanya berisi kode yang Anda perlukan.



0

Hanya ingin menambahkan beberapa sumber daya untuk Functional.js:

Ceramah / konferensi yang menjelaskan beberapa aplikasi http://www.youtube.com/watch?v=HAcN3JyQoyY

Perpustakaan Functional.js yang diperbarui: https://github.com/loop-recur/FunctionalJS Beberapa pembantu yang baik (maaf baru di sini, tidak ada reputasi: p): / loop-recur / PreludeJS

Saya telah menggunakan perpustakaan ini banyak baru-baru ini untuk mengurangi pengulangan di perpustakaan pembantu IRC klien js. Ini hal yang hebat - sangat membantu membersihkan dan menyederhanakan kode.

Selain itu, jika kinerja menjadi masalah (tapi lib ini cukup ringan), mudah untuk hanya menulis ulang menggunakan fungsi asli.


0

Anda dapat menggunakan ikatan asli untuk solusi satu baris yang cepat

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45


0

Menusuk lain, dari bekerja dengan janji.

(Penafian: JS noob, yang berasal dari dunia Python. Bahkan di sana, currying tidak terlalu banyak digunakan, tetapi kadang-kadang bisa berguna. Jadi saya membungkus fungsi currying - lihat tautan)

Pertama, saya mulai dengan panggilan ajax. Saya memiliki beberapa proses khusus untuk dilakukan pada kesuksesan, tetapi pada kegagalan, saya hanya ingin memberikan umpan balik kepada pengguna bahwa memanggil sesuatu menghasilkan beberapa kesalahan . Dalam kode saya yang sebenarnya, saya menampilkan umpan balik kesalahan di panel bootstrap, tetapi saya hanya menggunakan logging di sini.

Saya telah memodifikasi url langsung saya untuk membuat ini gagal.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

Sekarang, di sini untuk memberi tahu pengguna bahwa sebuah batch gagal, saya perlu menulis info itu di penangan kesalahan, karena yang didapat hanyalah tanggapan dari server.

Saya masih hanya memiliki info yang tersedia pada waktu pengkodean - dalam kasus saya, saya memiliki sejumlah batch yang mungkin, tetapi saya tidak tahu yang mana yang gagal mem-parsing respons server tentang url yang gagal.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

Ayo lakukan. Output konsol adalah:

menghibur:

bad batch run, dude utility.js (line 109) response.status:404

Sekarang, mari kita ubah hal-hal sedikit dan menggunakan kegagalan generik dapat digunakan kembali handler, tetapi juga salah satu yang kari pada saat runtime dengan kedua dikenal-di-kode-waktu memanggil konteks dan info runtime tersedia dari acara.

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

menghibur:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

Lebih umum, mengingat seberapa luas penggunaan panggilan balik di JS, currying sepertinya alat yang sangat berguna untuk dimiliki.

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/ http://www.drdobbs.com/open-source/currying-and-partial-functions-in- javasc / 231001821? pgno = 2

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.