Birkaç makale ve blogda curried fonksiyonlara referanslar gördüm ama iyi bir açıklama bulamıyorum (veya en azından mantıklı!)
add x y = x+y
(curried) add (x, y)=x+y
(cururried) ' den farklıdır
Birkaç makale ve blogda curried fonksiyonlara referanslar gördüm ama iyi bir açıklama bulamıyorum (veya en azından mantıklı!)
add x y = x+y
(curried) add (x, y)=x+y
(cururried) ' den farklıdır
Yanıtlar:
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.
[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 5
ve böylece listeyi oluşturmak için yazabilirim [5, 10, 15, 20, 25]
.
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.
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.
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 div
ve c, ama bu Haskell gerçekten karşıtı deyimsel olurdu.
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.
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
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 10
olarak 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.
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.
map
işlev yapmak istiyorsanız, bunu yapabilirsiniz . f
xss
map (map f) xss
İş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 .
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.
curry(func)
bir sarıcı function(a)
.sum(1)
, argüman Lexical Environment'a kaydedilir ve yeni bir sarıcı döndürülür function(b)
.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.Eğer partial
yarı yolda olduğunuzu anlarsanız. Buradaki fikir, partial
bir 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 add
yeni iki bağımsız değişkeni alır inc
işlevi yalnızca istediği b
zaten 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, add
iki 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 inc
açı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.
Bu makaleyi buldum ve körelmeyi daha iyi anlamak için referans aldığı makaleyi buldum: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
Diğerlerinin de belirttiği gibi, sadece bir parametre fonksiyonuna sahip olmanın bir yoludur.
Bu, kaç parametrenin geçirileceğini varsaymanız gerekmediği için kullanışlıdır, bu nedenle 2 parametreye, 3 parametreye ve 4 parametre işlevine ihtiyacınız yoktur.
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)
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.
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 x
ve ikinci işlev için bir işlev değeri döndürür. Bu ikinci işlev Int parametresini alır
y
.
İşte first
ilk 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
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.
func callback
kendisini dönen? @ callback(str)
let callback = callback(str)
func callback
func callback(_:data:)
iki parametre kabul ediyor, burada sadece bir tane veriyorum String
, bu, bir sonrakini bekliyor ( NSData
), bu yüzden şimdi let callback
verilerin aktarılmasını bekleyen başka bir işlev
İş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)());
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);
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!
"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));
};
curry
veuncurry
Haskell fonksiyonları. Burada önemli olan, bu izomorfizmlerin önceden sabitlenmiş olması ve bu nedenle de dilde "yerleşik" olmasıdır.