Bu "sözcüksel kapanma" dır ve siz doğru "", "kapalı-over değişkeni", C'deki bir statik değişkene benzer, örneğin: sadece let
biçimindeki kodla görünür kapsamı "), ancak tüm program akışı boyunca, işleve yapılan her çağrıyla yeniden başlatılmak yerine, devam eder.
Sanırım şaşkın olduğunuz kısım şudur: "num
, next-num
içine girilerek oluşturulmuştur, bu bir yerel değişkendir." Bu, let
bloğunun next-num
işlevinin bir parçası olmadığı için doğru değil: aslında, next-num
'a bağlı olan işlevi oluşturan ve döndüren bir ifade. (Bu çok farklıdır, örn., Fonksiyonların sadece derleme zamanında oluşturulabildiği ve bunları en üst düzeyde tanımlayabildiği C'den çok farklıdır. Şemada işlevler, tüm ifadelerin geri dönebileceği tamsayılar veya listeler gibi değerlerdir). arasındaki farkı hatırlamak
(define next-num #f) ; dummy value
(let ((num 0))
(set! next-num
(lambda() (set! num (+ num 1)) num)))
Bu önemlidir: Burada
(neredeyse)
define
sadece bir işlev-dönen ifadenin değerine
next-num
ilişkilendirerek olduğunu daha açık hale getirir aynı şeyi yazmak için başka bir yolu
(define (some-var args ...) expression expression ...)
tüm
expressions
çağrıldığında yürütür
some-var
bir işlev yapar
ve
'u
expression
değerine bağlayan
, oradan ve sonra değerlendirilmiştir. o lambda
forma etrafında, lexically kapsamı değişkenin, num
eklenmesiyle, bu hemen hemen aynı
(define some-var
(lambda (args ...) expression expression ...))
Kişisel koduna denk mi çünkü Açıkçası, eski sürüm, gereksizdir.
Son olarak, burada kapanma değişkenleri ve statik değişkenler arasındaki önemli bir fark vardır, bu da kapanmaları çok daha güçlü kılar.Eğer yazılı olsaydı yerine aşağıdaki:
(define f (make-next-num 7))
(define g (make-next-num 2))
(f) ; => 8
(g) ; => 3
(f) ; => 9
Bu geçerli:
(define make-next-num
(lambda (num)
(lambda() (set! num (+ num 1)) num)))
sonra
make-next-num
her çağrı bu fonksiyona özeldir ayrı ve yeni bir
num
değişkeni ile anonim bir işlev yaratacak Sözcüksel kapanışlarla dillerin gücünün büyük bir kısmını oluşturan gerçekten harika ve güçlü bir hile.
Eklemek için düzenlenmiştir: Şema "num
'un next-num
çağrıldığında nasıl değişiklik yapacağını" nasıl bildiğini "soruyorsunuz. Taslak halinde, eğer uygulamada değilse, bu aslında oldukça basittir. Şemadaki her ifade, değerleri tutabilecek yerlere isimlerin birlikleri olan değişken bağların bir ortamı (bir arama tablosu) bağlamında değerlendirilir. Bir let
formunun veya bir işlev çağrısının her bir değerlendirmesi, mevcut ortamı yeni bağlamalar ile genişleterek yeni bir ortam oluşturur. lambda
formlarının kapatılmasını sağlamak için uygulama, bunları, işlevin kendisinden ve tanımlandığı ortamdan oluşan bir yapı olarak temsil eder. Bu işleve yapılan çağrılar, işlevin tanımlandığı bağlama ortamını genişleterek değerlendirilir - çağrıldığı ortam.
Eski Lisps (yakın zamana kadar Emacs Lisp dahil) lambda
'a sahipti, ancak sözcüksel kapsam değil, bu nedenle anonim işlevler oluşturabilseniz bile, bunlara yapılan çağrılar çağrı ortamından ziyade çağrı ortamında değerlendirilirdi ve kapanışları. Scheme'nin bu hakkı elde eden ilk dil olduğuna inanıyorum. Sussman ve Steele'in, şemanın uygulanmasına ilişkin orijinali Lambda Papers, diğer pek çok şeyin yanı sıra, kapsam belirleme anlamak isteyen herkes için büyük bir zihin genişletme okuması yapıyor.