'Kapatma' ve 'lambda' arasındaki fark nedir?


811

Birisi açıklayabilir mi? Arkalarındaki temel kavramları anlıyorum ama sık sık birbirlerinin yerine kullanıldığını görüyorum ve kafam karışıyor.

Ve şimdi burada olduğumuza göre, normal bir işlevden nasıl farklılar?


85
Lambdalar bir dil yapısıdır (anonim işlevler), kapaklar birinci sınıf işlevleri (anonim olsun ya da olmasın) uygulamak için bir uygulama tekniğidir . Ne yazık ki, bu genellikle birçok insan tarafından karıştırılır.
Andreas Rossberg



PHP kapanışları için php.net/manual/tr/class.closure.php adresine bakın . Bir JavaScript programcısının beklediği şey bu değildir.
PaulH

SasQ'nun cevabı mükemmel. IMHO, bu soruyu görüntüleyenleri bu yanıta yönlendirirse SO kullanıcıları için daha yararlı olacaktır.
AmigoNico

Yanıtlar:


703

Bir lambda sadece anonim bir işlevdir - adı olmayan bir işlev. Şema gibi bazı dillerde, adlandırılmış işlevlere eşdeğerdir. Aslında, fonksiyon tanımı bir lambda'yı dahili olarak bir değişkene bağlama olarak yeniden yazılır. Python gibi diğer dillerde, aralarında bazı (oldukça gereksiz) farklılıklar vardır, ancak aksi takdirde aynı şekilde davranırlar.

Bir kapak herhangi bir fonksiyondur fazla kapatır çevre tanımlandığı edildiği. Bu, parametre listesinde olmayan değişkenlere erişebileceği anlamına gelir. Örnekler:

def func(): return h
def anotherfunc(h):
   return func()

Bunun nedeni, hataya neden olur funcdeğil üzerinde yakın çevre anotherfunc- htanımlanmamış. funcsadece küresel çevre üzerinde kapanır. Bu çalışacak:

def anotherfunc(h):
    def func(): return h
    return func()

Çünkü burada, functanımlanan anotherfuncpiton 2.3 ve daha büyük (veya bu gibi bazı numarası) onlar ne zaman ve neredeyse buna sahip kapanışları düzeltmek (mutasyon hala iş değil), bu araçlar üzerinde kapatır anotherfunc içinde 'nin çevre ve kutu erişim değişkenleri o. Python 3.1+ sürümünde, mutasyon anahtar kelimeyi kullanırken denonlocal çalışır .

Başka bir önemli nokta - artık değerlendirilmese bile çevrenin funckapanmasına devam edecektir . Bu kod da çalışır:anotherfuncanotherfunc

def anotherfunc(h):
    def func(): return h
    return func

print anotherfunc(10)()

Bu 10 basacaktır.

Bu, fark ettiğiniz gibi, lambda s ile ilgisi yoktur - iki farklı (ilgili olmasına rağmen) kavramlardır.


1
Claudiu, belirsiz bilgi python'umun hiçbir zaman doğru kapanışları olmadı. Ben bakmıyorum mutasyon problemini çözdüler mi? Oldukça mümkün ...
Simon

simon - haklısın, hala tam olarak doğru değiller. python 3.0 bu sorunu çözmek için bir kesmek ekleyeceğini düşünüyorum. ('yerel olmayan' tanımlayıcı gibi). bunu yansıtmak için cevaplarımı değiştireceğim.
Claudiu

2
@AlexanderOrlov: İkisi de lambdas ve kapaklar. Java daha önce isimsiz iç sınıflar aracılığıyla kapanmıştı. Artık bu işlevsellik lambda ifadeleri ile sözdizimsel olarak daha kolay hale getirildi. Yani muhtemelen yeni özelliğin en alakalı yönü şimdi lambdaların olmasıdır. Onlara lambda demek yanlış değil, onlar gerçekten lambdas. Java 8 yazarları neden kapatıldıklarını vurgulamamayı seçebilirler, bildiğim bir şey değil.
Claudiu

2
@AlexanderOrlov, Java 8 lambdas gerçek kapaklar olmadığından, kapakların simülasyonlarıdır. Python 2.3 kapanışlarına daha çok benzerler (değiştirilemez, bu nedenle başvurulan değişkenlerin 'etkin bir şekilde nihai' olması şartı) ve dahili olarak kapalı kapsamda belirtilen tüm değişkenleri gizli parametreler olarak alan kapanmayan işlevlere derlenir.
Logan Pickup

7
@Claudiu Belirli bir dil uygulamasına (Python) yapılan atıfın cevabı aşırı karmaşıklaştırabileceğini düşünüyorum. Soru tamamen dile-agnostiktir (ayrıca dile özgü etiketler de yoktur).
Matthew

318

Bu StackOverflow sorununun cevaplarında bile lambdas ve kapaklar etrafında çok fazla karışıklık var. Uygulamadan belirli programlama dilleri veya diğer clueless programcılar ile kapanışları öğrenen rastgele programcılara sormak yerine, kaynağa (her şeyin başladığı yer) bir yolculuk yapın . Lamdalar ve kapaklar , ilk elektronik bilgisayarlar bile var olmadan önce 30'larda Alonzo Kilisesi tarafından icat edilen Lambda Calculus'tan geldiğinden, bahsettiğim kaynak bu .

Lambda Matematik dünyadaki en basit programlama dilidir. İçinde yapabileceğiniz tek şey: ►

  • UYGULAMA: Bir ifadeyi diğerine uygulama, belirtilir f x.
    (Bir fonksiyon çağrısı olarak düşünün, fonksiyon nerede fve xtek parametresidir)
  • ÖZET: İfadede oluşan bir sembolü, bu sembolün sadece bir "yuva", değerle doldurulmayı bekleyen boş bir kutu, olduğu gibi bir "değişken" olduğunu işaretlemek için bağlar. Bir Yunan harfini λ(lambda), ardından sembolik adı (örneğin x), ardından .ifadeden önce bir nokta ekleyerek yapılır . Bu daha sonra ifadeyi bir parametre bekleyen bir işleve dönüştürür . Örneğin: ifadeyi alır ve bu ifadedeki sembolün bağlı bir değişken olduğunu söyler - parametre olarak sağladığınız bir değerle değiştirilebilir. Bu şekilde tanımlanan fonksiyonun anonim olduğunu unutmayın
    λx.x+2x+2x
    - Eğer henüz başvuruda bulunamaz yüzden, bir adı yok ama olabilir hemen arayın onu böyle, bekliyor parametresini sağlayarak (uygulamayı hatırlayın?) İt: (λx.x+2) 7. Daha sonra ifade (bu durumda değişmez bir değer) 7, uygulanan xlambda'nın alt ifadesinde olduğu gibi ikame edilir x+2, böylece elde edersiniz 7+2, bu daha sonra 9yaygın aritmetik kurallarına indirgenir .

Biz gizemlerinden biri çözdük Yani:
lambda olan anonim işlev Yukarıdaki örnekten, λx.x+2.


Farklı programlama dillerinde, işlevsel soyutlama sözdizimi (lambda) farklı olabilir. Örneğin, JavaScript'te şöyle görünür:

function(x) { return x+2; }

ve hemen aşağıdaki gibi bir parametreye uygulayabilirsiniz:

(function(x) { return x+2; })(7)

veya bu anonim işlevi (lambda) bazı değişkenlere kaydedebilirsiniz:

var f = function(x) { return x+2; }

bu etkin bir şekilde bir ad verir, ona fbaşvurmanıza ve daha sonra birkaç kez çağırmanıza olanak tanır, örneğin:

alert(  f(7) + f(10)  );   // should print 21 in the message box

Ama ismini vermek zorunda değildin. Hemen arayabilirsin:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

LISP'de lambdalar şu şekilde yapılır:

(lambda (x) (+ x 2))

ve böyle bir lambda'yı hemen bir parametreye uygulayarak çağırabilirsiniz:

(  (lambda (x) (+ x 2))  7  )


Tamam, şimdi diğer gizemi çözme zamanı: kapanış nedir . Bunu yapmak için lambda ifadelerindeki semboller ( değişkenler ) hakkında konuşalım .

Söylediğim gibi, lambda soyutlamasının yaptığı, alt ifadesinde bir sembolü bağlamaktır , böylece ikame edilebilir bir parametre haline gelir . Böyle bir sembol bağlı olarak adlandırılır . Fakat ifadede başka semboller varsa ne olur? Örneğin: λx.x/y+2. Bu ifadede sembol x, λx.kendisinden önceki lambda soyutlaması ile bağlıdır . Ancak diğer sembol, ybağlı değildir - ücretsizdir . Ne olduğunu ve nereden geldiğini bilmiyoruz, bu yüzden ne anlama geldiğini ve hangi değeri temsil ettiğini bilmiyoruz ve bu nedenle ne yanlama geldiğini anlayana kadar bu ifadeyi değerlendiremeyiz .

Aslında, aynı diğer iki sembollerle gider 2ve +. Bu iki simgeye o kadar aşinayız ki, genellikle bilgisayarın onları tanımadığını unuturuz ve bunları bir yerde, örneğin bir kütüphanede veya dilin kendisinde tanımlayarak ne anlama geldiğini söylememiz gerekir.

Aklınıza gelebilecek ücretsiz olarak adlandırılır onun "dair bağlam", içinde, ifade dışında başka bir yerde tanımlandığı gibi semboller ortamı . Ortam, bu ifadenin bir parçası olduğu daha büyük bir ifade olabilir (Qui-Gon Jinn'in dediği gibi: "Her zaman daha büyük bir balık vardır";)) veya bir kütüphanede veya dilin kendisinde ( ilkel olarak ).

Bu, lambda ifadelerini iki kategoriye ayırmamızı sağlar:

  • KAPALI ifadeler: bu ifadelerde ortaya çıkan her sembol bir lambda soyutlaması ile bağlıdır . Başka bir deyişle, bağımsızlar ; herhangi bir çevre bağlamının değerlendirilmesini gerektirmez. Bunlara birleştiriciler de denir .
  • AÇIK ifadeler: bu ifadelerdeki bazı semboller bağlı değildir - yani, içinde meydana gelen bazı semboller ücretsizdir ve bazı harici bilgiler gerektirirler ve bu nedenle bu sembollerin tanımlarını sağlayana kadar değerlendirilemezler.

Bir kapatabilirsiniz açık sağlayarak lambda ifade ortamını bazı değerlere onları bağlanarak bu bedava sembolleri tanımlar (lambdas aka sayılar, dizeleri, anonim fonksiyonlar ne olursa olsun ...).

Ve buraya gelen kapatma kısmı: kapatma a lambda ifadesi değerleri vermek dış bağlamda (çevre) tanımlanan sembollerin bu özel kümesidir serbest semboller artık onları özgür olmayan hale bu ifadede. Halen bazı "tanımsız" serbest semboller içeren açık bir lambda ifadesini, artık ücretsiz sembol içermeyen kapalı bir ifadeye çevirir .

Örneğin, aşağıdaki lambda ifadesine sahipseniz: λx.x/y+2sembol xsınırlıdır, sembol yücretsizken, bu nedenle ifade openne yanlama geldiğini (ve aynı ile +ve 2de ücretsizdir) söylemediğiniz sürece ifade değerlendirilir ve değerlendirilemez . Ancak, bunun gibi bir ortamınız olduğunu varsayalım :

{  y: 3,
+: [built-in addition],
2: [built-in number],
q: 42,
w: 5  }

Bu ortam bizim lambda ifadeden bütün "tanımsız" (ücretsiz) semboller için malzemeler tanımları ( y, +, 2) ve birkaç ekstra semboller ( q, w). Tanımlamamız gereken semboller ortamın bu alt kümesidir:

{  y: 3,
+: [built-in addition],
2: [built-in number]  }

ve bu tam olarak lambda ifademizin kapanışı :

Başka bir deyişle, açık bir lambda ifadesini kapatır . İlk etapta isim kapatma buradan geliyor ve bu yüzden bu konudaki pek çok insanın cevapları tam olarak doğru değil: P


Peki neden yanılıyorlar? Neden bu kadar çok kişi kapanmaların hafızadaki bazı veri yapıları olduğunu ya da kullandıkları dillerin bazı özellikleri olduğunu ya da kapanışları neden lambdalarla karıştırdıklarını söylüyor? : P

Sun / Oracle, Microsoft, Google vb. Kurumsal marketoidler suçlanacak, çünkü bu yapıları kendi dillerinde (Java, C #, Go vb.) Genellikle lambda olması gereken şeyi "kapatmalar" olarak adlandırırlar. Ya da sözcüksel kapsam belirleme uygulamak için kullandıkları belirli bir tekniği "kapanışlar" olarak adlandırırlar, yani bir fonksiyonun tanımlandığı sırada dış kapsamında tanımlanan değişkenlere erişebilir. Genellikle işlevin bu değişkenleri "kapsadığını", yani dış işlevin yürütülmesi bittikten sonra yok edilmelerini önlemek için bunları bir veri yapısına yakaladığını söylerler. Ama bu sadece yapımı bitti sonrası factum , sadece şeyler daha kafa karıştırıcı hale getirir "folklor etimoloji" ve pazarlama,

Ve söyledikleri şeyde her zaman biraz gerçek olduğu gerçeği nedeniyle daha da kötüsü, bu da onu yanlış olarak kolayca reddetmenize izin vermez: P Açıklayayım:

Birinci sınıf vatandaş olarak lambdas kullanan bir dil uygulamak istiyorsanız, onların çevrelerindeki bağlamda tanımlanan sembolleri kullanmasına izin vermeniz gerekir (yani, lambdaslarınızda serbest değişkenler kullanmak için). Ve bu semboller, çevredeki işlev geri döndüğünde bile orada olmalıdır. Sorun, bu sembollerin, işlevin bazı yerel depolamasına (genellikle çağrı yığınında) bağlı olmasıdır; bu, işlev geri döndüğünde artık olmayacaktır. Bu nedenle, bir lambda'nın beklediğiniz şekilde çalışması için, tüm bu serbest değişkenleri bir şekilde dış bağlamından "yakalamanız" ve dış bağlam gitmiş olsa bile bunları daha sonra için saklamanız gerekir. Yani, kapatmayı bulmanız gerekiyorlambda'nızı (kullandığı tüm bu harici değişkenleri) ve başka bir yerde saklayın (bir kopya oluşturarak veya onlar için önceden, yığının dışında bir yerde hazırlayarak). Bu hedefe ulaşmak için kullandığınız gerçek yöntem, dilinizin "uygulama ayrıntısıdır". Burada önemli olan kapatma , kümesidir serbest değişkenler gelen çevre ihtiyaç yerde kaydedilmesini senin lambda.

İnsanların, dilin uygulamalarında kullandıkları gerçek veri yapısını kapatmayı "kapatmanın" kendisi olarak uygulamak için çağırmaları çok uzun sürmedi. Yapı genellikle şöyle görünür:

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

ve bu veri yapıları, diğer işlevlere parametre olarak aktarılır, işlevlerden döndürülür ve değişkenlerde depolanır, lambdaları temsil eder ve bu ortamlarda çalışacak çevreye ve makine koduna erişmelerine izin verir. Ancak bu , kapamanın kendisini değil , bir kapama gerçekleştirmenin bir yoludur .

Yukarıda açıkladığım gibi, lambda ifadesinin kapatılması, çevresindeki lambda ifadesinde bulunan serbest değişkenlere değerler veren, ifadeyi etkili bir şekilde kapatarak ( henüz değerlendirilemeyen açık bir lambda ifadesini çevirerek) tanımların alt kümesidir. bir kapalı içinde yer alan tüm semboller, tanımlı olduğu, daha sonra değerlendirilebilir lambda ifade).

Başka bir şey, programcıların ve dil satıcılarının bu kavramların gerçek köklerinden habersiz bir "kargo kültü" ve "voo-doo büyüsü" dür.

Umarım bu sorularınızı cevaplar. Ancak herhangi bir takip sorunuz varsa, yorumlarda sormaktan çekinmeyin ve daha iyi açıklamaya çalışacağım.


50
Dile özgü değil genel olarak şeyleri açıklayan en iyi cevap
Shishir Arora

39
bir şeyleri açıklarken bu tür bir yaklaşımı seviyorum. En başından itibaren, işlerin nasıl çalıştığını ve sonra mevcut yanlış anlamaların nasıl yaratıldığını açıklamak. Bu cevabın zirveye çıkması gerekiyor.
Sharky

1
@SasQ> lambda ifadesinin kapatılması, kendi lambda ifadesinde yer alan serbest değişkenlere değer veren tanımların alt kümesidir. <Örnek veri yapınızda, bu, "işaretçi lambda fonksiyonunun ortamı "kapatılıyor mu?
Jeff M

3
Lambda hesabı bana makine dili gibi gelse de, "yapılmış" bir dilin aksine "bulunan" bir dil olduğu konusunda hemfikirim. Ve böylece keyfi sözleşmelere çok daha az maruz kalıyor ve gerçekliğin altında yatan yapıyı yakalamak için çok daha uygun. Linq, JavaScript, F # ile ilgili daha ulaşılabilir / erişilebilir özellikleri bulabiliriz, ancak Lambda hesabı dikkat dağılmadan konunun özüne iner.
StevePoling

1
Her seferinde biraz farklı ifadelerle noktanızı birkaç kez tekrarladığınızı takdir ediyorum. Kavramın güçlendirilmesine yardımcı olur. Keşke daha fazla insan bunu yapsaydı.
johnklawlor

175

Çoğu insan işlevleri düşündüğünde, adlandırılmış işlevleri düşünür :

function foo() { return "This string is returned from the 'foo' function"; }

Bunlar elbette isimle adlandırılır:

foo(); //returns the string above

Lambda ifadeleri ile anonim işlevlere sahip olabilirsiniz :

 @foo = lambda() {return "This is returned from a function without a name";}

Yukarıdaki örnekte, lambda'yı atandığı değişken üzerinden çağırabilirsiniz:

foo();

Bununla birlikte, değişkenlere anonim işlevler atamaktan daha kullanışlı olan, bunları daha üst düzey işlevlere veya diğer işlevlerden, yani diğer işlevleri kabul eden / döndüren işlevlerden geçirmektir. Bu gibi birçok durumda, bir işlevi adlandırmak gereksizdir:

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

Bir kapatma adlandırılmış veya anonim bir işlev olabilir, ancak işlevin tanımlandığı kapsamdaki değişkenleri "kapatırsa" olarak bilinir; yani, kapatma, yine de, kapanış kendisi. İşte adlandırılmış bir kapatma:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

Çok fazla görünmüyor ama ya bütün bunlar başka bir işlevdeyse ve incrementXharici bir işleve geçtiyseniz ?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

Fonksiyonel programlamada durum bilgisi olan nesneleri bu şekilde elde edersiniz. "İncrementX" isimlendirilmesine gerek olmadığından, bu durumda lambda kullanabilirsiniz:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }

14
burada hangi dili kullanıyorsun
Claudiu

7
Temelde sözde kod. İçinde bazı lisp ve JavaScript'in yanı sıra tasarladığım "@" ("at") adında, değişken bildirim operatörünün adını taşıyan bir dil var.
Mark Cidade

3
@MarkCidade, bu dil @ nerede? Bir dokümantasyon ve boşaltma var mı?
Pacerier

5
Neden Javascript almıyorsunuz ve @ işareti ile değişken bildirme kısıtlaması eklemiyorsunuz? Bu biraz zaman kazandırır :)
Nemoden

5
@Pacerier: Dili uygulamaya başladım: github.com/marxidad/At2015
Mark Cidade

54

Tüm kapaklar lambda değildir ve tüm lambdalar kapak değildir. Her ikisi de işlevlerdir, ancak bilmeye alışık olduğumuz gibi değil.

Bir lambda esasen standart fonksiyon bildirme yönteminden ziyade satır içi olarak tanımlanan bir fonksiyondur. Lambdalar genellikle nesneler olarak geçirilebilir.

Kapatma, vücudunun dışındaki alanlara başvurarak çevresini çevreleyen bir işlevdir. Ekteki durum, kapatmanın çağrıları boyunca kalır.

Nesneye yönelik bir dilde, kapaklar normalde nesneler aracılığıyla sağlanır. Bununla birlikte, bazı OO dilleri (örneğin C #), durumu içine alacak nesneler olmayan tamamen işlevsel diller (lisp gibi) tarafından sağlanan kapakların tanımına daha yakın olan özel işlevler uygular .

İlginç olan, Lambdas ve Closures'ın C # 'a girmesinin fonksiyonel programlamayı genel kullanıma daha yakın hale getirmesidir.


Öyleyse, kapanışların bir lambdas alt kümesi olduğunu ve lambdaların bir işlev alt kümesi olduğunu söyleyebilir miyiz?
sker

Kapaklar lambdasların bir alt kümesidir ... ancak lambdalar normal işlevlerden daha özeldir. Dediğim gibi lambdalar satır içi olarak tanımlanır. Esasen, başka bir işleve geçirilmedikçe veya bir dönüş değeri olarak döndürülmedikçe bunlara başvurmanın bir yolu yoktur.
Michael Brown

19
Lambdalar ve kapakların her biri tüm fonksiyonların bir alt kümesidir, ancak sadece lambdalar ve kapaklar arasında bir kesişme vardır, burada kapakların kesişmeyen kısmı, kapaklar ve kesişen olmayan lamdalar, ilişkili değişkenler.
Mark Cidade

Bence lambdalar işlevlerden daha temel kavramlar. Gerçekten programlama diline bağlıdır.
Wei Qiu

15

Bu kadar basit: lambda bir dil yapısıdır, yani anonim işlevlerin sözdizimi; kapatma, onu uygulamak için kullanılan bir tekniktir - veya bu konuda adlandırılmış veya anonim herhangi bir birinci sınıf işlevdir.

Daha kesin olarak, bir kapatma, birinci sınıf bir işlevin çalışma zamanında, "kodunun" bir çifti ve bu kodda kullanılan tüm yerel olmayan değişkenler üzerinde "kapanan" bir ortam olarak nasıl temsil edildiğidir. Bu şekilde, bu değişkenlere, kaynaklandıkları dış kapsamlardan çıkılsa bile erişilebilir.

Ne yazık ki, birinci sınıf değerler olarak fonksiyonları desteklemeyen veya sadece sakatlanmış biçimde destekleyen pek çok dil var. İnsanlar genellikle "gerçek olanı" ayırt etmek için "kapatma" terimini kullanırlar.


12

Programlama dilleri açısından tamamen iki farklı şeydir.

Temel olarak bir Turing tam dili için, yalnızca soyutlama, uygulama ve azaltma gibi çok sınırlı öğelere ihtiyacımız var. Soyutlama ve uygulama, lamdba ifadesini oluşturma yolunuzu sağlar ve azaltma, lambda ifadesinin anlamını dertermine eder.

Lambda, hesaplama sürecini özetleyebileceğiniz bir yol sağlar. örneğin, iki sayının toplamını hesaplamak için, iki parametre x, y alan ve x + y döndüren bir işlem soyutlanabilir. Şemada şöyle yazabilirsiniz:

(lambda (x y) (+ x y))

Parametreleri yeniden adlandırabilirsiniz, ancak tamamladığı görev değişmez. Hemen hemen tüm programlama dillerinde lambda ifadesine işlevler adı verilen bir ad verebilirsiniz. Ancak çok fazla fark yoktur, kavramsal olarak sadece sözdizimi şekeri olarak kabul edilebilirler.

Tamam, şimdi bunun nasıl uygulanabileceğini hayal edin. Lambda ifadesini bazı ifadelere her uyguladığımızda, ör.

((lambda (x y) (+ x y)) 2 3)

Parametreleri değerlendirilecek ifadeyle değiştirebiliriz. Bu model zaten çok güçlü. Ancak bu model, sembollerin değerlerini değiştirmemize izin vermez, örn. Durum değişikliğini taklit edemeyiz. Bu yüzden daha karmaşık bir modele ihtiyacımız var. Kısa yapmak için, lambda ifadesinin anlamını hesaplamak istediğimizde, bir sembol çifti ve karşılık gelen değeri bir ortama (veya tabloya) koyarız. Daha sonra geri kalanı (+ xy) tablodaki karşılık gelen sembollere bakarak değerlendirilir. Şimdi çevre üzerinde doğrudan çalışmak için bazı temel ilkeler sağlarsak, durum değişikliklerini modelleyebiliriz!

Bu arka planla, bu işlevi kontrol edin:

(lambda (x y) (+ x y z))

Lambda ifadesini değerlendirdiğimizde, xy'nin yeni bir tabloya bağlanacağını biliyoruz. Ama nasıl ve nerede z arayabiliriz? Aslında z, serbest değişken olarak adlandırılır. Z içeren bir dış ortam olmalıdır. Aksi takdirde, ifadenin anlamı sadece x ve y bağlanması ile belirlenemez. Bunu netleştirmek için, şemada aşağıdaki gibi bir şeyler yazabilirsiniz:

((lambda (z) (lambda (x y) (+ x y z))) 1)

Yani z bir dış tabloda 1'e bağlı olacaktır. Hala iki parametreyi kabul eden bir fonksiyon elde ediyoruz, ancak bunun gerçek anlamı dış ortama da bağlı. Başka bir deyişle, dış ortam serbest değişkenleri kapatır. Set! Yardımı ile fonksiyonu durumsal hale getirebiliriz, yani matematik anlamında bir fonksiyon değildir. Ne döndürdüğü sadece girdiye değil, z'ye de bağlıdır.

Bu zaten çok iyi bildiğiniz bir şey, bir nesne yöntemi neredeyse her zaman nesnelerin durumuna dayanır. Bu yüzden bazı insanlar "kapanışlar fakir insanın nesneleridir" derler. Fakat birinci sınıf fonksiyonları gerçekten sevdiğimiz için nesneleri de fakir insanın kapanışı olarak düşünebiliriz.

Düzeni, gerçek kapanışları olan en eski dillerden biri olduğu için fikirleri göstermek için kullanıyorum. Buradaki tüm malzemeler SICP bölüm 3'te çok daha iyi sunulmaktadır.

Özetle, lambda ve kapatma gerçekten farklı kavramlardır. Bir lambda bir işlevdir. Kapatma, bir çift lambda ve lambda'yı kapatan ilgili ortamdır.


Yani artık hiçbir serbest değişken bulunmayana kadar tüm kapanmalar iç içe lambdalarla değiştirilebiliyor mu? Bu durumda kapakların özel tür lambdalar olarak görülebileceğini söyleyebilirim.
Trilarion

8

Kavram yukarıda açıklananla aynıdır, ancak PHP geçmişindeyseniz, bu PHP kodunun kullanımını açıklar.

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

işlev ($ v) {return $ v> 2; } lambda fonksiyon tanımıdır. Bir değişkende saklayabiliriz, böylece tekrar kullanılabilir:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

Şimdi, filtrelenmiş dizide izin verilen maksimum sayıyı değiştirmek isterseniz ne olur? Başka bir lambda işlevi yazmanız veya bir kapatma oluşturmanız gerekir (PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

Kapatma, kendi ortamında değerlendirilen ve işlev çağrıldığında erişilebilen bir veya daha fazla bağlı değişkene sahip bir işlevdir. Oyunda bir takım kavramların olduğu fonksiyonel programlama dünyasından geliyorlar. Kapaklar lambda işlevleri gibidir, ancak kapağın tanımlandığı dış ortamdaki değişkenlerle etkileşime girme kabiliyetine sahip oldukları için daha akıllıdır.

İşte PHP'nin daha basit bir örneği:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

Bu makalede iyi açıklanmıştır.


7

Bu soru eski ve birçok cevap aldı.
Şimdi resmi olmayan kapanış projeleri olan Java 8 ve Resmi Lambda ile sorunu yeniden canlandırıyor.

Java bağlamında cevap ( Lambdas ve kapanışlar üzerinden - fark nedir? ):

"Kapatma, serbest değişkenlerinin her birini bir değere bağlayan bir ortamla eşleştirilmiş bir lambda ifadesidir. Java'da lambda ifadeleri, kapaklar aracılığıyla uygulanacaktır, bu nedenle iki terim toplulukta birbirinin yerine kullanılmaya başlanmıştır."


Lamdalar Java'da kapatılarak nasıl uygulanır? Bu, Lamdas ifadesinin eski bir stil anonim sınıfa dönüştürüldüğü anlamına mı geliyor?
hackjutsu

5

Basitçe söylemek gerekirse, kapanış kapsamla ilgili bir numaradır, lambda anonim bir işlevdir. Lambda ile daha zarif bir şekilde kapanmayı gerçekleştirebiliriz ve lambda genellikle daha yüksek bir işleve geçirilen bir parametre olarak kullanılır


4

Lambda ifadesi sadece anonim bir işlevdir. örneğin düz java'da şöyle yazabilirsiniz:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

burada Sınıf Fonksiyonu sadece java kodunda inşa edilmiştir. Şimdi mapPersonToJob.apply(person)onu kullanmak için bir yeri arayabilirsiniz . bu sadece bir örnek. Bunun için sözdizimi olmadan önce bir lambda. Lambdas bunun için kısa bir yol.

Kapanış:

bir Lambda bu kapsam dışındaki değişkenlere erişebildiğinde kapanır. sihrini söyleyebilirim, sihirli bir şekilde yaratıldığı ortamın etrafına sarılabilir ve değişkenleri kapsamının dışında kullanabilir (dış kapsam. açık olmak gerekirse, bir kapatma, bir lambda'nın DIŞ KAPSAMINA erişebileceği anlamına gelir.

Kotlin'de bir lambda her zaman kapanışına erişebilir (dış kapsamındaki değişkenler)


0

Bir işlevin, işlemi gerçekleştirmek için harici değişken kullanıp kullanmadığına bağlıdır.

Dış değişkenler - bir işlevin kapsamı dışında tanımlanan değişkenler.

  • Lambda ifadeleri vatansızdır çünkü işlemleri gerçekleştirmek için parametrelere, dahili değişkenlere veya sabitlere bağlıdır.

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • İşlemleri gerçekleştirmek için parametreler ve sabitlerle birlikte harici değişkenler (yani işlev gövdesinin kapsamı dışında tanımlanan değişken) kullandığı için kapanışlar durumu korur .

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

Java kapatma oluşturduğunda, n değişkenini işlevle birlikte tutar, böylece diğer işlevlere aktarıldığında veya herhangi bir yerde kullanıldığında başvurulabilir.

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.