Körelme veya kısmi uygulama hakkında özel olan nedir?


9

Her gün Fonksiyonel programlama ile ilgili makaleler okuyorum ve mümkün olduğunca bazı uygulamaları uygulamaya çalışıyorum. Ancak körelme veya kısmi uygulamada neyin benzersiz olduğunu anlamıyorum.

Bu Groovy kodunu örnek olarak alalım:

def mul = { a, b -> a * b }
def tripler1 = mul.curry(3)
def tripler2 = { mul(3, it) }

Ben arasındaki fark nedir anlamıyorum tripler1ve tripler2. İkisi de aynı değil mi? 'Currying' Groovy, Scala, Haskell vb. parametreleri tripler2çoğu dilde (C bile) orijinal işleve (gibi ) iletecek işlev veya kapatma

Burada bir şey mi eksik? Grails uygulamamda curried ve kısmi uygulamayı kullanabileceğim yerler var ama bunu yapmaktan çekiniyorum çünkü kendime "Bu nasıl farklı?" Diye soruyorum.

Lütfen beni aydınlat.

EDIT: kısmi uygulama / köri varsayılan parametreleri orijinal işlevine ileten başka bir işlev oluşturmak / çağırmak daha basit olduğunu söylüyorlar mı?


1
Birisi lütfen "köri" veya "köri" etiketlerini oluşturabilir mi?
Vigneshwaran

C'de nasıl köri yaparsın?
Giorgio


1
@Vigneshwaran: AFAIK, köriyi destekleyen bir dilde başka bir işlev oluşturmanız gerekmez. Örneğin, Haskell'de bu bir int parametresi alan bir işlev f x y = x + yanlamına gelir f. f x( fUygulandı x) sonucu, bir int parametresi alan bir işlevdir. Sonuç f x y(ya da (f x) yyani, üzerine f xuygulanan y) hiçbir girdi parametresi almayan ve azaltarak değerlendirilen bir ifadedir x + y.
Giorgio

1
Aynı şeyleri elde edebilirsiniz, ancak C ile harcadığınız çaba miktarı, daha fazla acı verici ve varsayılan davranış olduğu
haskell

Yanıtlar:


8

Kurutma, n girdiyi her biri 1 girdi alan n işleve çeviren bir işlevi çevirmek / temsil etmekle ilgilidir. Kısmi uygulama, bazı girişlerin bir işleve sabitlenmesi ile ilgilidir.

Kısmi uygulama için motivasyon öncelikle yüksek dereceli fonksiyon kütüphaneleri yazmayı kolaylaştırmasıdır. Örneğin, C ++ STL'deki algoritmaların tümü büyük ölçüde tahminleri veya tekli fonksiyonları alır, bind1st kütüphane kullanıcısının bir değeri bağlı olmayan sıra dışı olmayan fonksiyonlara bağlanmasına izin verir. Kütüphane yazarının, ikili sürümler sağlamak için tekli fonksiyonları alan tüm algoritmalar için aşırı yüklenmiş fonksiyonlar sağlaması gerekmez.

Kendi kendini kurutmak yararlıdır çünkü ücretsiz olarak istediğiniz yere kısmi uygulama sağlar, yani artık bind1stkısmen uygulamak gibi bir işleve ihtiyacınız yoktur .


olan curryingşey groovy özgü veya diller arasında uygulanabilir?
katılımcı

@ foampile onun diller arasında uygulanabilir bir şey, ama ironik bir köri gerçekten yapmaz programmers.stackexchange.com/questions/152868/…
jk.

@jk. Körelme / kısmi uygulamanın başka bir işlev oluşturup çağırmaktan daha verimli olduğunu mu söylüyorsunuz?
Vigneshwaran

2
@Vigneshwaran - mutlaka daha performanslı değildir, ancak programcının zamanı açısından kesinlikle daha verimlidir. Ayrıca, köri işleminin birçok işlevsel dil tarafından desteklendiğini, ancak genellikle OO veya prosedür dillerinde desteklenmediğini unutmayın. (Ya da en azından dilin kendisi tarafından değil.)
Stephen C

6

Ancak aynı şeyi (sol köri, sağ köri, n köri veya kısmi uygulama), çoğu dilde (tripler2 gibi) parametreleri orijinal işleve (tripler2 gibi) iletecek başka bir adlandırılmış veya anonim işlev veya kapatma oluşturarak yapabilirim. hatta C.)

Ve optimizer buna bakacak ve anlayabileceği bir şeye derhal devam edecek. Currying son kullanıcı için hoş bir numara ama dil tasarımı açısından çok daha iyi faydaları var. Bu var gerçekten tekli olarak tüm yöntemleri ele güzel başka bir yöntem olabilir.A -> BB

Üst düzey işlevleri yerine getirmek için hangi yöntemleri yazmanız gerektiğini basitleştirir. Dildeki statik analizinizin ve optimizasyonunuzun, bilinen bir şekilde çalışan yalnızca bir çalışma yolu vardır. Parametre bağlama, çemberlerin bu ortak davranışı gerçekleştirmesini gerektirmek yerine tasarımdan çıkar.


6

@Jk olarak. curried, kodu daha genel hale getirmeye yardımcı olabilir.

Örneğin, şu üç işleve sahip olduğunuzu varsayalım (Haskell'de):

> let q a b = (2 + a) * b

> let r g = g 3

> let f a b = b (a 1)

Buradaki işlev fbağımsız değişken olarak iki işlevi alır 1, ilk işleve geçer ve ilk çağrının sonucunu ikinci işleve geçirir.

Eğer fkullanarak qve rargüman olarak çağrılacak olsaydık, etkili bir şekilde yapardı:

> r (q 1)

nerede quygulanacağı 1ve başka bir işleve ( qcurried olduğu gibi ) döneceği ; bu döndürülen işlev r, argümanı verilecek argümanı olarak geçirilir 3. Bunun sonucu bir değer olacaktır 9.

Şimdi diyelim ki başka iki fonksiyonumuz daha var:

> let s a = 3 * a

> let t a = 4 + a

bunları da aktarabiliriz fve argümanlarımızın ya da olup olmadığına bağlı olarak 7ya bir değeri alabiliriz . Bu işlevlerin ikisi de bir işlev yerine bir değer döndürdüğünden, veya öğesinde kısmi bir uygulama gerçekleşmez .15s tt sf s tf t s

Biz yazılı olsaydı file qve rbunun yerine kısmi uygulaması, örneğin zihninde bir lambda (anonim işlev) kullanmış olabilir:

> let f' a b = b (\x -> a 1 x)

ancak bu, genelliğini kısıtlardı f'. fbağımsız değişkenlerle çağırılabilir qve rveya sve tancak f'yalnızca qve r- ile çağrılabilir f' s tve f' t sher ikisi de hataya neden olur.

DAHA

Eğer f'bir ile adlandırılan q'/ r'burada çift q'fazla iki bağımsız aldı, q'halen kısmen uygulanmaktadır sona ereceğini f'.

Alternatif olarak, içeriden değil qdışarıya sarılabilirsin f, ama bu seni kötü bir yuvalanmış lambda ile bırakacaktı:

f (\x -> (\y -> q x y)) r

hangi aslında curried qilk etapta oldu!


Gözlerimi açtın. Cevabınız, curried / kısmen uygulanan işlevlerin, orijinal işleve argüman ileten yeni bir işlev oluşturmaktan ne kadar farklı olduğunu anlamamı sağladı. 1. Köri / paed fonksiyonlarının (f (q. Curry (2) gibi) etrafından geçmek, sadece geçici bir kullanım için gereksiz olarak ayrı fonksiyonlar oluşturmaktan daha içindir. (Groovy gibi fonksiyonel dillerde)
Vigneshwaran

2. Sorumda, "Ben de aynısını C'de yapabilirim" dedim. Evet, ancak işlevleri orijinal olarak iletemediğiniz, parametreleri orijinale ileten ayrı bir işlev yaratamayacağınız işlevsel olmayan dillerde, currying / pa'nın tüm avantajlarına sahip değildir
Vigneshwaran

Groovy'nin Haskell'in desteklediği genelleme türünü desteklemediğini fark ettim. Ben yazmak zorunda def f = { a, b -> b a.curry(1) }yapmak için f q, rişlerin ortaya çıkmasına def f = { a, b -> b a(1) }ya def f = { a, b -> b a.curry(1)() }yönelik f s, tçalışmalara. Tüm parametreleri geçmeniz veya açıkça körelediğinizi söylemelisiniz. :(
Vigneshwaran

2
@Vigneshwaran: Evet, Haskell'in ve körüğün birlikte çok iyi gittiğini söylemek güvenlidir . ; Böylece, işlev uygulamasını gösterir ve boşluk varsayılan olarak Haskell, fonksiyonları (doğru tanımı) curried olduğunu] Not f x yaraçlarının neler birçok dil yazardı f(x)(y), değil f(x, y). Belki de qböyle adlandırılmasını beklemek için yazarsanız kodunuzu Groovy çalışır q(1)(2)?
CA McCann

1
@Vigneshwaran Yardımcı olabilirim sevindim! Kısmi bir uygulama yaptığınızı açıkça söylemek zorunda kaldığınız acısını hissediyorum. Clojure'da yapmalıyım (partial f a b ...)- Haskell'e alışkınım, diğer dillerde programlama yaparken çok iyi bir şekilde körelmeyi özlüyorum (son zamanlarda F #'da çalışıyorum, ancak neyse ki bunu destekliyor).
paul

3

Kısmi uygulama ile ilgili iki önemli nokta vardır. Birincisi sözdizimsel / kolaylıktır - @jk'ın belirttiği gibi, bazı tanımların okunması ve yazılması kolaylaşır. (Bunun ne kadar harika olduğu hakkında daha fazla bilgi için Pointfree programlamaya göz atın !)

İkincisi, @telastyn'in belirttiği gibi, bir işlevler modeliyle ilgilidir ve sadece uygun değildir. Örneklerimi alacağım Haskell sürümünde, kısmi uygulama ile diğer dillere aşina olmadığım için, tüm işlevler tek bir argüman alır. Evet, hatta aşağıdaki gibi işlevler yapar:

(:) :: a -> [a] -> [a]

tek bir argüman almak; işlev türü yapıcısının ilişkilendirilebilirliği nedeniyle ->, yukarıdakine eşdeğerdir:

(:) :: a -> ([a] -> [a])

bir işlev alan ave bir işlev döndüren bir işlevdir [a] -> [a].

Bu, aşağıdaki gibi işlevler yazmamızı sağlar:

($) :: (a -> b) -> a -> b

herhangi bir işlevi uygun türde bir bağımsız değişkene uygulayabilir . Çılgın olanlar bile:

f :: (t, t1) -> t -> t1 -> (t2 -> t3 -> (t, t1)) -> t2 -> t3 -> [(t, t1)]
f q r s t u v = q : (r, s) : [t u v]

f' :: () -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)]
f' = f $ ((), 'a')  -- <== works fine

Pekâlâ, bu tartışmalı bir örnekti. Ancak daha kullanışlı olanı, bu yöntemi içeren Uygulamalı tip sınıfını içerir:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

Gördüğünüz gibi, tür biti $alıp götürmekle aynıdır Applicative fve aslında bu sınıf bir bağlamda işlev uygulamasını açıklar. Yani normal fonksiyon uygulaması yerine:

ghci> map (+3) [1..5]  
[4,5,6,7,8]

İşlevleri Uygulamalı bir bağlamda uygulayabiliriz; örneğin, bir şeyin mevcut veya eksik olabileceği Belki bağlamında:

ghci> Just map <*> Just (+3) <*> Just [1..5]
Just [4,5,6,7,8]

ghci> Just map <*> Nothing <*> Just [1..5]
Nothing

Şimdi gerçekten harika olan kısım, Uygulamalı tip sınıfının birden fazla argümanın işlevleri hakkında hiçbir şeyden bahsetmemesi - yine de onlarla başa çıkabilir, hatta 6 argümanın işlevleri gibi f:

fA' :: Maybe (() -> Char -> (t2 -> t3 -> ((), Char)) -> t2 -> t3 -> [((), Char)])
fA' = Just f <*> Just ((), 'a')

Bildiğim kadarıyla, bazı genel uygulama kavramları olmadan genel formdaki Uygulamalı tip sınıfı mümkün olmayacaktır. Dil kısmi uygulama yoksun, sen, elbette - (orada herhangi bir programlama uzmanları için! Yanılıyorsam beni düzeltin lütfen) olabilir öyle, sadece aynı değil ... çeşit içinde inşa, ancak ? :)


1
Applicativekörelmeden veya kısmi uygulama kullanamazsınız fzip :: (f a, f b) -> f (a, b). Üst düzey işlevlere sahip bir dilde, bu, köri ve kısmi uygulamayı işlevin bağlamına kaldırmanıza izin verir ve eşdeğerdir (<*>). Üst düzey fonksiyonlar olmadan fmap, her şey işe yaramaz.
CA McCann

@CAMcCann geri bildiriminiz için teşekkür ederiz! Bu cevapla başımın üstünde olduğumu biliyordum. Öyleyse yanlış dedim mi?

1
Kesinlikle ruh olarak doğrudur. Kılları "genel form", "mümkün" tanımları üzerine bölmek ve "kısmi uygulama anlayışına" sahip olmak, büyüleyici f <$> x <*> ydeyimsel stilin kolayca çalıştığı gerçeğini değiştirmez, çünkü körelme ve kısmi uygulama kolayca çalışır. Başka bir deyişle, hoş olan , burada mümkün olandan daha önemlidir .
CA McCann

İşlevsel programlamanın kod örneklerini her gördüğümde, bunun ayrıntılı bir şaka olduğuna ve var olmadığına ikna oldum.
Kieveli

1
@Kieveli bu şekilde hissetmek talihsiz bir durum. Orada temelleri iyi bir anlayış ile çalışmaya başlayacak birçok iyi öğreticiler vardır.
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.