'Currying' nedir?


652

Birkaç makale ve blogda curried fonksiyonlara referanslar gördüm ama iyi bir açıklama bulamıyorum (veya en azından mantıklı!)


12
[Yorum olarak bırakıldı, matematikçi olmayanlar için işe yaramayacağı için.] Kartezyen kapalı bir kategorinin tanımına göre, X -> X x A ve X arasında sabit bir sıfat ailesi (A ile doğal olarak parametrelenir) vardır. -> x ^ A. izomorfizmler hom (X X, A, Y) <-> hom (X, Y ^ A) olan curryve uncurryHaskell fonksiyonları. Burada önemli olan, bu izomorfizmlerin önceden sabitlenmiş olması ve bu nedenle de dilde "yerleşik" olmasıdır.
Alexandre

3
Haskell'de körüklemek için güzel bir öğretici var learnyouahaskell.com/higher-order-functions#curried-functions kısa yorumlar add x y = x+y(curried) add (x, y)=x+y(cururried) ' den farklıdır
Jaider

Yanıtlar:


872

Currying, birden fazla argüman alan bir işlevi, her birinin yalnızca bir argüman alan bir dizi fonksiyona böldüğünüz zamandır. İşte JavaScript'te bir örnek:

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

add(3, 4); // returns 7

Bu, a ve b olmak üzere iki argüman alan ve toplamlarını döndüren bir işlevdir. Şimdi bu işlevi köri yapacağız:

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

Bu, bir bağımsız değişkeni, a, ve başka bir bağımsız değişkeni, b işlevini döndüren bir işlevdir ve bu işlev toplamlarını döndürür.

add(3)(4);

var add3 = add(3);

add3(4);

İlk ifade, add (3, 4) ifadesi gibi 7 değerini döndürür. İkinci ifade, argümanına 3 ekleyecek olan add3 adında yeni bir fonksiyon tanımlar. Bazı insanlar buna kapatma derler. Üçüncü ifade, 3'e 4 eklemek için add3 işlemini kullanır ve sonuç olarak yine 7 üretir.


235
Pratik anlamda, bu kavramı nasıl kullanabilirim?
Çilek

43
@Strawberry, örneğin, [1, 2, 3, 4, 5]rasgele bir sayı ile çarpmak istediğiniz bir sayı listesine sahip olduğunuzu varsayalım . Haskell'de, map (* 5) [1, 2, 3, 4, 5]listenin tamamını çoğaltmak 5ve böylece listeyi oluşturmak için yazabilirim [5, 10, 15, 20, 25].
nyson

62
Harita işlevinin ne yaptığını anlıyorum, ama benim için göstermeye çalıştığınız noktayı anladığımdan emin değilim. Harita fonksiyonunun körili kavramını temsil ettiğini mi söylüyorsunuz?
Çilek

78
@Strawberry İlk argüman map, yalnızca 1 argüman alan bir işlev olmalıdır - listeden bir öğe. Çarpma - matematiksel bir kavram olarak - ikili bir işlemdir; 2 argüman alır. Bununla birlikte, Haskell'de bu cevabın *ikinci versiyonuna benzer şekilde curried bir işlevdir add. Sonuç, (* 5)tek bir argüman alan ve bunu 5 ile çarpan bir işlevdir ve bunu harita ile kullanmamızı sağlar.
Doval

26
@Strawberry Standard ML veya Haskell gibi işlevsel dillerle ilgili hoş bir şey, "ücretsiz" bir şekilde körelemenizdir. Çoklu argüman işlevini herhangi bir dilde yaptığınız gibi tanımlayabilir ve bir grup lambdayı kendiniz atmak zorunda kalmadan otomatik olarak curried bir sürümünü elde edebilirsiniz. Böylece, herhangi bir karışıklık veya rahatsızlık duymadan mevcut herhangi bir işlevden daha az argüman alan yeni işlevler üretebilirsiniz ve bu da onları diğer işlevlere geçirmeyi kolaylaştırır.
Doval

125

Bir fonksiyon cebirinde, birden fazla argüman alan işlevlerle (veya N-tuple olan eşdeğer bir argümanla) uğraşmak biraz yetersizdir - ancak, Musa Schönfinkel'in (ve bağımsız olarak Haskell Curry) kanıtladığı gibi, gerekli değil: ihtiyaç, bir argüman alan işlevlerdir.

Peki, doğal olarak ifade edebileceğiniz bir şeyle nasıl başa çıkıyorsunuz f(x,y)? Peki, bunu f(x)(y)- f(x)olarak adlandırın, çağırın g, bir işlevdir ve bu işlevi uygularsınız y. Başka bir deyişle, yalnızca bir bağımsız değişken alan işlevleriniz vardır - ancak bu işlevlerden bazıları diğer işlevleri döndürür (AYRICA bir bağımsız değişken alır ;-).

Her zamanki gibi, wikipedia'nın bu konuda güzel bir özet girişi var, birçok yararlı işaretçi (muhtemelen en sevdiğiniz dillerle ilgili olanlar dahil ;-) ve biraz daha titiz matematiksel tedavi.


1
Yukarıda benimkine benzer bir yorum varsayalım - fonksiyonel dillerin işlevleri tek bir argümanla sınırlandırdığını görmedim. Yanlış mıyım?
Eric M

1
@hoohoo: İşlevsel diller işlevleri tek bir bağımsız değişkenle sınırlamaz. Bununla birlikte, daha düşük, daha matematiksel düzeyde, sadece bir argüman alan işlevlerle uğraşmak çok daha kolaydır. (Örneğin lambda hesabında işlevler bir seferde yalnızca bir argüman alır.)
Sam DeFabbia-Kane

1
TAMAM. O zaman başka sorular. Aşağıdaki gerçek bir ifade midir? Lambda hesabı fonksiyonel programlama için bir model olarak kullanılabilir, ancak fonksiyonel programlama mutlaka lambda hesabı uygulanmaz.
Eric M

7
Vikipedi sayfalarının belirttiği gibi, çoğu FP dili sadece "uygulamak" yerine "süslemek" veya "artırmak" lambda hesabı (örneğin, bazı sabitler ve veri türleriyle), ancak o kadar da yakın değildir. BTW, örneğin Haskell'in "fonksiyonları tek bir argümanla sınırlandırdığı" izlenimini veren nedir? Elbette ki, bu körelme sayesinde alakasız olsa da; örneğin div :: Integral a => a -> a -> a- bu çoklu oklara dikkat edin? "Harita a işlev a 'yı eşleme ile" bir okuma ;-). Sen olabilir için bir (tek) tuple argümanı kullanmak divve c, ama bu Haskell gerçekten karşıtı deyimsel olurdu.
Alex Martelli

@Alex - wrt Haskell & arg sayısı, Haskell için çok fazla zaman harcamadım ve hepsi birkaç hafta önceydi. Yani yapmak kolay bir hataydı.
Eric M

100

Somut bir örnek:

Bir nesneye etki eden yerçekimi kuvvetini hesaplayan bir fonksiyonunuz olduğunu varsayalım. Formülü bilmiyorsanız, burada bulabilirsiniz . Bu işlev, gerekli üç parametreyi bağımsız değişken olarak alır.

Şimdi, yeryüzünde olmak, sadece bu gezegendeki nesneler için kuvvetleri hesaplamak istiyorsunuz. İşlevsel bir dilde, dünyanın kütlesini işleve geçirebilir ve sonra kısmen değerlendirebilirsiniz. Geri alacağınız şey, sadece iki argüman alan ve yeryüzündeki cisimlerin çekim kuvvetini hesaplayan başka bir işlevdir. Buna körelme denir.


2
Bir merak olarak, JavaScript için Prototip kütüphanesi, burada tam olarak açıkladığınız şeyi yapan bir "köri" işlevi sunar: prototypejs.org/api/function/curry
shuckster


7
Bu bana kısmi bir uygulama gibi geliyor. Anladığım kadarıyla, eğer körili uygularsanız, tek bir argümanla işlevler oluşturabilir ve bunları daha karmaşık işlevler oluşturmak için oluşturabilirsiniz. Bir şey mi kaçırıyorum?
neontapir

9
@ neontapir doğrudur. Shea'nın tarif ettiği şey körelmek değildir. Kısmi bir uygulamadır. Üç bağımsız değişkenli bir işlevi varsa ve f (1) olarak adlandırırsanız, geri aldığınız şey iki bağımsız değişkenli bir işlev değildir. Başka bir bağımsız değişken işlevi döndüren bir bağımsız değişken işlevini geri alırsınız. Körili bir fonksiyona sadece bir argüman iletilebilir. PrototyJJS'deki köri işlevi de köri değildir. Kısmi bir uygulamadır.
MindJuice

hayır (kısmi değerlendirmeye) ve hayır (köriye). bu kısmi uygulama olarak bilinir. Bunu sağlamak için köri gereklidir.
Ness Ness

47

Currying, işlevlere öncekinden daha az bir argüman almalarını sağlamak için uygulanabilen bir dönüşümdür.

Örneğin, F # 'da bu şekilde bir fonksiyon tanımlayabilirsiniz: -

let f x y z = x + y + z

Burada f işlevi x, y ve z parametrelerini alır ve bunları toplar, böylece: -

f 1 2 3

6 döndürür.

Bizim tanımımızdan bu nedenle f için köri fonksiyonunu tanımlayabiliriz: -

let curry f = fun x -> f x

Burada 'fun x -> fx', C # 'daki x => f (x)' e eşit bir lambda fonksiyonudur. Bu işlev, köri yapmak istediğiniz işlevi girer ve tek bir bağımsız değişken alan ve ilk bağımsız değişkeni girdi bağımsız değişkenine ayarlı olarak belirtilen işlevi döndüren bir işlevi döndürür.

Önceki örneğimizi kullanarak bir f köri elde edebiliriz: -

let curryf = curry f

Sonra aşağıdakileri yapabiliriz: -

let f1 = curryf 1

Bu da bize f1 yz = 1 + y + z'ye eşdeğer bir f1 fonksiyonu sağlar. Bu, aşağıdakileri yapabileceğimiz anlamına gelir: -

f1 2 3

Hangi 6 döndürür.

Bu işlem genellikle şu şekilde tanımlanabilen 'kısmi fonksiyon uygulaması' ile karıştırılır: -

let papply f x = f x

Gerçi birden fazla parametreye genişletebiliriz, yani: -

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

Kısmi bir uygulama, işlevi ve parametreleri alır ve bir veya daha fazla parametre gerektiren bir işlevi döndürür ve önceki iki örnekte gösterildiği gibi, doğrudan standart F # işlev tanımında uygulanır, böylece önceki sonucu elde edebiliriz: -

let f1 = f 1
f1 2 3

6 sonucunu döndürür.

Sonuç olarak:-

Körelme ile kısmi fonksiyon uygulaması arasındaki fark şudur: -

Currying bir işlev alır ve tek bir bağımsız değişkeni kabul eden ve ilk bağımsız değişkeni bu bağımsız değişkene ayarlanmış olarak belirtilen işlevi döndüren yeni bir işlev sağlar. Bu, çoklu parametreli fonksiyonları bir dizi tekli argüman işlevi olarak göstermemizi sağlar . Misal:-

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

Kısmi işlev uygulaması daha doğrudan - bir işlevi ve bir veya daha fazla bağımsız değişkeni alır ve ilk n bağımsız değişkeni belirtilen n bağımsız değişkenine ayarlanmış bir işlevi döndürür. Misal:-

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

Yani C # 'daki yöntemlerin kısmen uygulanabilmesi için önce körlenmesi gerekecek mi?
cdmckay

"Bu, çoklu parametreli fonksiyonları bir dizi tekli argüman fonksiyonu olarak göstermemize izin veriyor" - mükemmel, bu benim için her şeyi güzelleştirdi. Teşekkürler
Bulanık Analiz

44

Diğer işlevleri yapmak için işlevleri kullanmanın bir yolu olabilir.

Javascript dilinde:

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

Bu şekilde arama yapmamıza izin verir:

let addTen = add(10);

Bu çalıştığında 10olarak x;

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

yani bu işleve geri döndüğümüz anlamına gelir:

function(y) { return 10 + y };

Yani aradığın zaman

 addTen();

gerçekten arıyorsun:

 function(y) { return 10 + y };

Yani bunu yaparsanız:

 addTen(4)

aynı:

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

Bu yüzden addTen(), ne yaparsak yapalım her zaman on ekler. Benzer işlevleri aynı şekilde yapabiliriz:

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

Şimdi açık olan takip sorusu, neden dünyada bunu yapmak isteyesiniz ki? İstekli bir operasyonu döndürürx + y tembel olarak atlanabilecek bir dönüştürür, yani en az iki şey yapabiliriz 1. önbellek pahalı operasyonlar 2. fonksiyonel paradigmada soyutlamalar elde eder.

Curried fonksiyonumuzun şöyle göründüğünü hayal edin:

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

Bu işlevi bir kez çağırabiliriz, daha sonra birçok yerde kullanılacak sonucu aktarabiliriz, yani yalnızca bir kez hesaplama açısından pahalı olan şeyleri yaparız:

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

Soyutlamaları da benzer şekilde elde edebiliriz.


5
Burada gördüğüm doğal olarak ardışık bir sürecin en iyi adım adım açıklaması ve belki de partinin en iyi, en açıklayıcı cevabı.

4
@jonsilver tam tersini söylerdim, iyi bir açıklama değil. Verilen örneği açıklamanın iyi olduğuna katılıyorum, ancak insanlar varsayılan olarak düşünmeye eğilimlidirler, “evet tamamen açık ama aynı şeyi başka bir şekilde yapabilirdim, yani iyi olan şey ne? Başka bir deyişle, sadece körelmenin nasıl çalıştığını değil, aynı zamanda diğer on ekleme yollarına kıyasla neden işe yaramaz ve önemsiz bir gözlem olmadığını aydınlatmak için yeterli bağlam veya açıklamaya sahip olmasını isterdim.
whitneyland

29

Curried işlevi, ilk argümanı kabul edecek ve ikinci argümanı kabul eden bir işlev döndürecek şekilde yeniden yazılan birkaç argümanın işlevidir. Bu, birkaç bağımsız değişkenin işlevlerinin ilk bağımsız değişkenlerinin bir kısmının kısmen uygulanmasını sağlar.


5
"Bu, birkaç bağımsız değişkenin işlevlerinin bazı başlangıç ​​argümanlarının kısmen uygulanmasına izin verir." - bu neden faydalı?
acarlon

5
@acarlon İşlevler genellikle bir veya daha fazla argüman ile aynı şekilde çağrılır. Örneğin, bir liste listesi üzerinde bir mapişlev yapmak istiyorsanız, bunu yapabilirsiniz . fxssmap (map f) xss
Jon Harrop

1
Teşekkür ederim, bu mantıklı. Biraz daha okudum ve yerine düştü.
acarlon

4
Bu cevabın doğru ve özlü bir şekilde doğru olduğunu düşünüyorum. "Currying", birden çok argümanın işlevini alma ve her birini tek bir argüman alan ve tek bir argümanın işlevini döndüren ciddi işlevlere dönüştürme işlemidir veya son işlev durumunda, gerçek sonucu döndürür . Bu işlem sizin için dil tarafından otomatik olarak yapılabilir veya diğer dillerde curry () işlevini kullanarak curried sürümü oluşturabilirsiniz. Parametreli bir curried fonksiyonunun çağrılmasının curried olmadığını unutmayın. Körelme zaten oldu.
MindJuice

7

İşte Python'da bir oyuncak örneği:

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

(Python dışındaki programcıların dikkatini dağıtmak için + ile bitiştirmeyi kullanmanız yeterlidir.)

Eklemek için düzenleme:

Python'un bunu uygulama biçiminde kısmi nesne ve işlev ayrımını da gösteren http://docs.python.org/library/functools.html?highlight=partial#functools.partial adresine bakın .


Bunu almıyorum - bunu yaparsınız: >>> am_quote = köri (display_quote, "Alex Martelli") ama sonra bunu yaparsınız: >>> am_quote ("currying", "Her zamanki gibi wikipedia güzel bir özete sahiptir. .. ") Yani iki argümanlı bir fonksiyonunuz var. Körelmenin size oluşturacağınız üç farklı işlevi vermesi gerekir mi?
Eric M

Ben sadece bir parametre köri kısmi kullanarak, iki argüman ile bir işlev üretiyorum. İsterseniz, am_quote'u yalnızca belirli bir konuda Alex'ten alıntı yapan bir tane oluşturmak için daha fazla köri yapabilirsiniz. Matematik artalan sadece bir parametre ile fonksiyonlar sona erdirmeye odaklanmış olabilir - ama bunun gibi herhangi bir sayıda parametreyi düzeltmenin yaygın olarak (matematik açısından doğru değilse) currying olarak adlandırıldığına inanıyorum.
Anon

(btw - '>>>', Python etkileşimli yorumlayıcısında kodun bir parçası değil, bilgi
istemidir

Tamam args hakkında açıklama için teşekkürler. Python yorumlayıcı istemini biliyorum, satırları alıntı yapmaya çalışıyordum ama işe yaramadı ;-)
Eric M

Yorumunuzdan sonra, SO dahil olmak üzere, "currying" ve. Aşina olduğum kesin olmayan kullanımın birçok örneğine yanıt olarak "kısmi uygulama". Örnek için bakınız: stackoverflow.com/questions/218025/…
Anon

5

Currying bir işlevi callable as f(a, b, c)olarak callable olarak çevirir f(a)(b)(c).

Aksi takdirde, köpürme, bağımsız değişkenlerin bir parçası olan bir dizi işleve birden çok bağımsız değişken alan bir işlevi parçaladığınız zamandır.

Kelimenin tam anlamıyla, köri, işlevlerin bir dönüşümüdür: bir yoldan diğerine çağırma. JavaScript'te, orijinal işlevi korumak için genellikle bir sarıcı yaparız.

Currying bir işlev çağırmaz. Sadece dönüştürüyor.

İki bağımsız değişken işlevleri için köri yapan köri işlevi yapalım. Başka bir deyişle, curry(f)iki argüman f(a, b)onuf(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

Gördüğünüz gibi, uygulama bir dizi sarmalayıcıdır.

  • Sonucu curry(func)bir sarıcı function(a).
  • Bu şekilde çağrıldığında sum(1), argüman Lexical Environment'a kaydedilir ve yeni bir sarıcı döndürülür function(b).
  • Daha sonra sum(1)(2)son olarak çağırır function(b)2 sağlayan ve orijinal çoklu bağımsız değişken toplamına çağrı geçer.

4

Eğer partialyarı yolda olduğunuzu anlarsanız. Buradaki fikir, partialbir işleve argümanları önceden uygulamak ve yalnızca kalan argümanları isteyen yeni bir işlevi geri vermektir. Bu yeni işlev çağrıldığında, kendisine sağlanan bağımsız değişkenlerle birlikte önceden yüklenmiş bağımsız değişkenleri içerir.

Clojure'da +bir işlev vardır, ancak işleri açıkça netleştirmek için:

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

incİşlevin, geçilen numaraya 1 eklediğinin farkında olabilirsiniz .

(inc 7) # => 8

Bunu kullanarak kendimiz inşa edelim partial:

(def inc (partial add 1))

Burada, ilk argümanına 1 yüklenmiş başka bir işlev döndürüyoruz add. As addyeni iki bağımsız değişkeni alır incişlevi yalnızca istediği bzaten olmuştur 1 beri eskisi gibi değil 2 argümanlar - argüman kısmen uygulanmış. Böylece partial, varsayılan değerleri önceden verilen yeni işlevler oluşturmak için bir araçtır. Bu nedenle işlevsel bir dilde işlevler genellikle genelden özele argümanları sıralar. Bu, diğer işlevlerin oluşturulacağı bu tür işlevlerin yeniden kullanılmasını kolaylaştırır.

Şimdi, dilin, addiki argüman isteyen içgörüsel olarak anlayacak kadar akıllı olup olmadığını hayal edin . Bu ifadeyi engellemekten ziyade bir argümandan geçirdiğimizde, işlev argümanı kısmen uygularsa, büyük olasılıkla daha sonra diğer argümanı sunmayı düşündüğümüz anlayışımıza geçersek? Daha sonra incaçıkça kullanmadan tanımlayabiliriz partial.

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

Bazı diller bu şekilde davranır. İşlevleri daha büyük dönüşümlere dönüştürmek istendiğinde son derece yararlıdır. Bu, dönüştürücülere yol açacaktır.



3

Diğer tüm cevaplar gibi, körükleme kısmen uygulanan fonksiyonlar yaratmaya yardımcı olur. Javascript, otomatik körelme için yerel destek sağlamaz. Dolayısıyla, yukarıda verilen örnekler pratik kodlamaya yardımcı olmayabilir. Yaşam yazısında mükemmel bir örnek var (Bu aslında js için derleniyor) http://livescript.net/

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

Yukarıdaki örnekte, daha az bağımsız değişken verdiğinizde, canlı metin sizin için yeni bir curried işlevi oluşturur (çift)


3

Curry kodunuzu basitleştirebilir. Bunu kullanmanın ana nedenlerinden biri budur. Currying, n bağımsız değişkenini kabul eden bir işlevi, yalnızca bir bağımsız değişkeni kabul eden n işlevine dönüştürme işlemidir.

Buradaki ilke, closure (closure) özelliğini kullanarak, iletilen işlevin bağımsız değişkenlerini başka bir işlevde saklamak ve ona bir dönüş değeri olarak işlemek ve bu işlevler bir zincir oluşturur ve son bağımsız değişkenler tamamlanmak üzere iletilir operasyon.

Bunun yararı, programın esnekliğini ve okunabilirliğini artırabilen, aynı anda bir parametre ile ilgilenerek parametrelerin işlenmesini basitleştirebilmesidir. Bu aynı zamanda programı daha yönetilebilir hale getirir. Ayrıca kodu daha küçük parçalara bölmek de tekrar kullanılabilir hale getirir.

Örneğin:

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

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

Ben de yapabilirim ...

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

Bu, karmaşık kodu düzgün hale getirmek ve senkronize olmayan yöntemlerin vb.


2

Curried işlevi, yalnızca bir yerine birden çok bağımsız değişken listesine uygulanır.

Burada iki Int parametresi (x ve y) ekleyen düzenli, eğri olmayan bir işlev vardır:

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

İşte benzer bir işlevi curried. İki Int parametresinin bir listesi yerine, bu işlevi her biri bir Int parametresinin iki listesine uygularsınız:

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

Burada olan şey, çağırdığınızda curriedSum, aslında arka arkaya iki geleneksel işlev çağrısına sahip olmanızdır. İlk işlev çağırma, adlı tek bir Int parametresi alır xve ikinci işlev için bir işlev değeri döndürür. Bu ikinci işlev Int parametresini alır y.

İşte firstilk geleneksel fonksiyon çağrısının ne yapacağını ruh olarak yapan bir fonksiyon curriedSum:

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

İlk işleve 1 uygulanması - diğer bir deyişle, ilk işlevi çağırmak ve 1'den geçmek - ikinci işlevi kullanmaktır:

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

İkinci işleve 2'nin uygulanması sonucu verir:

scala> second(2)
res6: Int = 3

2

Köriliğe bir örnek, fonksiyonlara sahip olduğunuzda şu anda parametrelerden sadece birini biliyor olabilirsiniz:

Örneğin:

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
}

Burada, geri gönderirken ikinci parametreyi bilmediğiniz için, onu performAsyncRequest(_:)işleve göndermek için başka bir lambda / kapatma oluşturmanız gerekir.


edilir func callbackkendisini dönen? @ callback(str)let callback = callback(str)func callback
Deniliyor

Hayır, func callback(_:data:)iki parametre kabul ediyor, burada sadece bir tane veriyorum String, bu, bir sonrakini bekliyor ( NSData), bu yüzden şimdi let callbackverilerin aktarılmasını bekleyen başka bir işlev
S2dent

2

İşte n no ile fonksiyon körelemesi için genel ve en kısa versiyon örneği. parametreler.

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

Burada C # 'de körük uygulamasının basit bir açıklamasını bulabilirsiniz. Yorumlarda, körüğün nasıl yararlı olabileceğini göstermeye çalıştım:

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 Java Script'in üst düzey işlevlerinden biridir.

Currying, ilk argümanı alıp, geri kalan argümanları kullanan ve değeri döndüren bir işlevi döndürecek şekilde yeniden yazılan birçok argümanın bir işlevidir.

Şaşkın?

Bir örnek görelim,

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

Bu, aşağıdaki kıvrılma fonksiyonuna benzer,

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

Peki bu kod ne anlama geliyor?

Şimdi tanımı tekrar okuyun,

Currying, ilk argümanı alıp, geri kalan argümanları kullanan ve değeri döndüren bir işlevi döndürecek şekilde yeniden yazılan birçok argümanın bir fonksiyonudur.

Hala, Karışık mı? Derinlemesine açıklayayım!

Bu işlevi çağırdığınızda,

var curryAdd = add(5);

Size böyle bir işlev döndürür,

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

Buna buna üst düzey işlevler denir. Anlamı, bir işlevi sırayla çağırmak başka bir işlevi döndürür üst düzey işlevi için kesin bir tanımdır. Bu, efsane Java Script için en büyük avantajdır. Öyleyse köriye geri dön,

Bu satır ikinci argümanı curryAdd işlevine geçirir.

curryAdd(6);

sonuçta,

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

Umarım burada köriliğin kullanımını anlarsın. Yani, avantajlara gelince,

Neden Kıvrılıyor?

Kod yeniden kullanılabilirliği kullanır. Daha az kod, Daha Az Hata. Nasıl daha az kod olduğunu sorabilirsiniz?

ECMA script 6 yeni özellik ok fonksiyonları ile kanıtlayabilirim.

Evet! ECMA 6, bize ok fonksiyonları adı verilen harika bir özellik sunar,

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

Ok işlevinin yardımıyla yukarıdaki işlevi aşağıdaki gibi yazabiliriz,

x=>y=>x+y

Havalı değil mi?

Yani, Daha Az Kod ve Daha az hata !!

Bu üst düzey işlev sayesinde kolayca hatasız bir kod geliştirilebilir.

Sana meydan okuyorum!

Umut, körelenin ne olduğunu anladın. Herhangi bir açıklamaya ihtiyacınız varsa lütfen buraya yorum yapmaktan çekinmeyin.

Teşekkürler, iyi günler!


0

"ReasonML'de Kıvrılma" örneği vardır.

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));
  };
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.