Mükemmel kısmi uygulama 'körelme' mi diyor?


15

Groovy, 'körelme' adını verdiği bir konsepte sahiptir. İşte onların wiki'sinden bir örnek:

def divide = { a, b -> a / b }

def halver = divide.rcurry(2)

assert halver(8) == 4

Burada olup bitenler hakkındaki anlayışım, divide 2 değerine bağlı olmasıdır. Bu, kısmi bir başvuru şekli gibi gözükmektedir.

Kıvrılma terimi genellikle bir dizi bağımsız değişkeni alan bir işlevi yalnızca bir bağımsız değişkeni alan ve başka bir işlevi döndüren bir işleve dönüştürmek için kullanılır. Örneğin curry, Haskell'deki işlevin türü :

curry :: ((a, b) -> c) -> (a -> (b -> c))

Haskell kullanmadıysanız insan için a, bve ctüm jenerik parametrelerdir. curryiki bağımsız bir işlev alır alır ve bir işlev verir ave bir işlevi döner biçin c. Bunu daha net hale getirmek için türe ekstra bir çift parantez ekledim.

Harika örnekte neler olduğunu yanlış anladım mı yoksa sadece kısmi uygulama mı yanlış adlandırıldı? Ya da aslında her ikisini de yapıyor mu? Yani, dividecurried bir işleve dönüştürmek ve daha sonra 2bu yeni işleve kısmen uygulamak .


Yanıtlar:


14

Groovy'nin uygulaması curryaslında hiçbir noktada, perde arkasında bile köri yapmaz. Kısmi uygulama ile özdeştir.

curry, rcurryVe ncurryyöntem , bir geri CurriedClosurenesne bağlı argümanlar tutar. Ayrıca getUncurriedArguments, bağlı bağımsız değişkenlerle kendisine iletilen bağımsız değişkenlerin kompozisyonunu döndüren bir yöntem (yanlış adlandırılmış - işlevleri bozarsınız, bağımsız değişkenleri değil).

Bir kapatma çağrıldığında, sonuçta çağıran nesnenin bir örneği olup olmadığını açıkça kontrol eden invokeMethodyönteminiMetaClassImpl çağırır CurriedClosure. Öyleyse, yukarıda belirtilenleri getUncurriedArgumentsuygulamak için tüm argüman dizisini oluşturmak için kullanır :

if (objectClass == CurriedClosure.class) {
    // ...
    final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
    // [Ed: Yes, you read that right, curried = uncurried. :) ]
    // ...
    return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
}

Yukarıdaki kafa karıştırıcı ve biraz tutarsız bir isimlendirmeye dayanarak, bunu yazan her kimsenin iyi bir kavramsal anlayışa sahip olduğundan şüpheleniyorum, ama belki biraz aceleci ve - birçok akıllı insan gibi - kısmi uygulama ile körelemeyi sınırladı. Biraz talihsiz ise bu anlaşılabilir (Paul King'in cevabına bakınız); geriye dönük uyumluluğu bozmadan bunu düzeltmek zor olacaktır.

Önerdiğim bir çözümcurry , herhangi bir argüman iletilmediğinde, gerçek körelme yapacak şekilde yöntemin aşırı yüklenmesi ve yöntemin yeni bir partialişlev lehine argümanlarla çağrılmasının kaldırılmasıdır . Bu biraz garip gelebilir , ancak sıfır argümanla kısmi uygulama kullanmak için bir sebep olmadığından geriye dönük uyumluluğu en üst düzeye çıkarırken, işlev aslında gerçek körelme için yeni, farklı adlandırılmış bir işleve sahip olmanın (IMHO) çirkin durumundan kaçınırken adlı curryfarklı ve kafa karıştırıcı bir şey yapar.

Arama sonucunun currygerçek körelemeden tamamen farklı olduğunu söylemeye gerek yok . İşlevi gerçekten köreltiyorsa, şunları yazabilirsiniz:

def add = { x, y -> x + y }
def addCurried = add.curry()   // should work like { x -> { y -> x + y } }
def add1 = addCurried(1)       // should work like { y -> 1 + y }
assert add1(1) == 2 

Çünkü ... ve o çalışacak addCurriedgibi çalışması gerekir{ x -> { y -> x + y } } . Bunun yerine bir çalışma zamanı istisnası atar ve içinde biraz ölürsün.


1
Ben argüman> 2 ile fonksiyonları üzerinde rcurry ve ncurry bu gerçekten sadece kısmi uygulama değil curry olduğunu göstermektedir
jk.

@jk Aslında, sonunda not ettiğim gibi == 2 argümanına sahip fonksiyonlarda gösterilebilir. :)
Jordan Gray

3
@matcauthon Açıkçası, körelemenin "amacı", birçok argümanı olan bir fonksiyonu, her biri bir argümanla iç içe bir fonksiyonlar zincirine dönüştürmektir. Sanırım istediğin , Groovy'de haklı göstermek için örneğin LISP veya Haskell'den biraz daha zor olan, körili kullanmak isteyebileceğin pratik bir sebep . Mesele şu ki, muhtemelen çoğu zaman kullanmak istediğiniz şey, kısmi uygulama değil, kısmi bir uygulamadır.
Jordan Gray

4
+1and you die a little inside
Thomas Eding

1
Vay canına, cevabınızı okuduktan sonra daha iyi körili anlıyorum. Soruya özel olarak, "kısmi uygulama ile kapalı körelme" demiş olmanız hoşuma gitti.
GlenPeterson

3

Ben ikiden fazla argüman ile fonksiyonları düşünürken groovy köri aslında kısmi uygulama olduğunu düşünüyorum. düşünmek

f :: (a,b,c) -> d

onun körelmiş hali

fcurried :: a -> b -> c -> d

ancak groovy'nin köri eşdeğer bir şey döndürür (1 argümanı x ile çağrıldığı varsayılarak)

fgroovy :: (b,c) -> d 

f değerini x değerine sabit değeri ile çağıracak

yani groovy'nin köri N-1 argümanlarıyla işlevleri döndürebilirken, curried fonksiyonların düzgün bir şekilde sadece 1 argümanı vardır, bu nedenle groovy köri ile köri olamaz.


2

Groovy, köri yöntemlerinin isimlendirilmesini, kısmi uygulama için benzer isimlendirme kullanan belki de bu tür FP merkezli işlevsellik için talihsiz olan çok sayıda diğer saf olmayan FP dilinden ödünç aldı. Groovy'a dahil edilmek üzere birkaç "gerçek" körili uygulama öneriliyor. Onlar hakkında okumaya başlamak için iyi bir iş parçacığı burada:

http://groovy.markmail.org/thread/c4ycxdzm3ack6xxb

Mevcut işlevler bazı şekillerde kalacaktır ve yeni yöntemlerin nasıl adlandırılacağı vb. Çağrılırken geriye dönük uyumluluk dikkate alınacaktır - bu aşamada yeni / eski yöntemlerin son adlandırılmasının ne olacağını söyleyemem. olmak. Muhtemelen adlandırma konusunda bir uzlaşma ama göreceğiz.

Çoğu OO programcısı için iki terim arasındaki fark (körelme ve kısmi uygulama) tartışmalı olarak büyük ölçüde akademiktir; ancak, bunlara alıştıktan sonra (ve kodunuzu kim koruyacaksa bu kodlama stilini okuyacak şekilde eğitilir), noktasız veya örtük stil programlama ("gerçek" körükleme destekleri), belirli algoritma türlerinin daha kompakt bir şekilde ifade edilmesini sağlar ve bazı durumlarda daha zarif. Burada belli ki "güzelliğin seyircinin gözünde yattığı" söz konusudur, ancak her iki stili de destekleyebilme yeteneği Groovy'nin doğasına (OO / FP, statik / dinamik, sınıflar / senaryolar vb.) Uygundur.


1

IBM'de bulunan bu tanım göz önüne alındığında:

Köri terimi kısmi fonksiyonlar konseptini geliştiren matematikçi Haskell Curry'den alınmıştır. Kıvrılma, birçok bağımsız değişkeni alan bir işleve birden çok bağımsız değişken almayı, kalan bağımsız değişkenleri alan ve bir sonuç döndüren yeni bir işlevle sonuçlanır.

halverşimdi sadece bir parametre alan yeni (curried) fonksiyonunuz (veya kapanışınız). Aramak halver(10)5 ile sonuçlanır.

Bu nedenle, n-1 bağımsız değişkenli bir işlevi n bağımsız değişkenli bir işlevi dönüştürür. Aynı şey, köri ne yapar haskell örnek tarafından söylenir.


4
IBM'in tanımı yanlış. Körük olarak tanımladıkları şey aslında daha küçük bir işleve sahip bir işlev yapmak için bir işlevin argümanlarını bağlayan (düzelten) kısmi işlev uygulamasıdır. Currying, birden çok argüman alan bir fonksiyonu, her birinin bir argüman alan bir fonksiyon zincirine dönüştürür.
Jordan Gray

1
Vikipedi'nin tanımında IBM ile aynıdır: Matematik ve bilgisayar bilimlerinde, körükleme, bir işlevi birden çok argümanı (veya bir argümanı n-parçasını) alan bir şekilde dönüştürme işlevidir. her biri tek bir argümanla fonksiyonlar zinciri (kısmi uygulama). Groovy, bir işlevi (iki bağımsız rcurrydeğişkeni olan) bir işleve (bir bağımsız değişkeni alır) bir işleve (şimdi yalnızca bir bağımsız değişkeni olan) dönüştürür. Köri işlevini temel işlevime bir argümanla zincirledim ve sonuçta elde ettiğim işlevi elde ettim.
matcauthon

3
hayır wikipedia tanımı farklıdır - kısmi uygulama, curried işlevi çağırdığınızda - groovy'nin ne yaptığını tanımladığınızda değil
jk.

6
@jk doğrudur. Wikipedia açıklamasını tekrar okuyun ve döndürülen şeyin argümanlarla bir fonksiyon değil, her biri bir argümanla bir fonksiyonlar zinciri olduğunu göreceksiniz n - 1. Cevabımın sonundaki örneğe bakın; ayrıca yapılan ayrım hakkında daha fazla bilgi için makalenin sonraki bölümlerine bakınız. en.wikipedia.org/wiki/…
Jordan Gray

4
Çok önemli, güven bana. Yine, cevabımın sonundaki kod, doğru bir uygulamanın nasıl çalışacağını göstermektedir; bir şey için argüman almazdı. Mevcut uygulama gerçekten adlandırılmalıdır partial.
Jordan Gray
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.