Neden sürekli `` `tekrarlanan çağrılar ile değişelim 'bir değişmez

Diyelim ki böyle bir foo işlevi var:

(defun foo (e)
  (let ((lst '(a b c)))
    (delq e lst)))

Daha sonra şu şekilde kullanırız (sırayla birer birer değerlendirme):

(foo 'c) ; => (a b)
(foo 'b) ; => (a)
(foo 'a) ; => nil
(foo 'b) ; => (a)

Burada ne oldu?

Başka bir bar işlevi tanımlarsam:

(defun bar (e)
  (let ((lst (list 'a 'b 'c)))
    (delq e lst)))

Sonra aynı şekilde kullanın:

(bar 'c) ; => (a b)
(bar 'b) ; => (a c)
(bar 'a) ; => (b c)
(bar 'b) ; => (a c)

Buradaki sonuçlar bana mantıklı geliyor.

Öyleyse soru, foo işlevinin let biçimindeki sabit neden bu şekilde davranıyor? Amaçlandı mı?

11
Bu, bir stackoverflow.com/q/16670989/850781 adresinin bir kopyasıdır.
katma yazar shabbychef, kaynak
Başka bir anahtar teklif S.O. bu ( Ch f quote 'dan geliyor): "Uyarı: quote , dönüş değerini oluşturmaz, ancak yalnızca Lisp okuyucu tarafından önceden oluşturulmuş değeri döndürür ". Lisp’e yeni katılanların lisp okuyucusunun bütün kavramlarına sık sık haksız olduklarını ve bu nedenle “okuma” ve “değerlendirme” aşamaları arasındaki ayrımı öğrenmek ve anlamak için biraz zaman ayırmak, genel olarak dili anlamak için çok yararlı olduğunu düşünüyorum. Yukarıdaki gibi özel konuların yanı sıra.
katma yazar Mark Ireland, kaynak
Açıkça konuşmak gerekirse, ' elisp'deki bir okuyucu makrosu değildir (elisp'in okuyucu makroları yoktur), ancak kesinlikle okuma aşamasında işlenir. Yine de önemli olan bu değil. Demek istediğim, okuyucunun tüm metin lisp kodlarını lisp nesnelerine (bir kez) dönüştürdüğünü anlamaktı ve o, daha sonra gördüğünüz metin yerine değerlendirilen (genellikle tekrarlanan) nesnelerdir.
katma yazar Mark Ireland, kaynak
Nesneler değerlendirme sırasında potansiyel olarak manipülasyona maruz kaldıklarından (bu sorudaki gibi), herhangi bir zamanda değerlendirilenlerin orjinal olarak okunan kodu temsil etmesi için gerekli değildir. Elbette çoğu durumda tam olarak bunu yapacak - nesneler açısından sık sık düşünmenize gerek yok. Fakat sahne arkasında neler olup bittiğini bilmek, bu tür şeylerin bir faktör olduğu durumları tanımanıza ve anlamanıza olanak tanır.
katma yazar Mark Ireland, kaynak
@phils '' okuma 'aşamasında işlenen bir okuyucu makrosu, yani (' a 'b' c 'listesini) ' de değerlendirilir. eval "sahne?
katma yazar Mahesh, kaynak
@ phils Teşekkürler! Mantıklı.
katma yazar Mahesh, kaynak

1 cevap

İşte uygun şekilde düzenlenmiş özdeş soruya cevabım:

Kötü

foo is self-modifying code. This is extremely dangerous. While the variable lst disappears at the end of the let form, its initial value persists in the function object, and that is the value you are modifying. Remember that in Lisp a function is a first class object, which can be passed around (just like a number or a list), and, sometimes, modified. This is exactly what you are doing here: the initial value for lst is a part of the function object and you are modifying it.

Neler olduğunu gerçekten görelim:

(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a b c)))) (delq e lst)))
(foo 'c)
==> (a b)
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a b)))) (delq e lst)))
(foo 'b)
==> (a)
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a)))) (delq e lst)))
(foo 'a)
==> nil
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a)))) (delq e lst)))

İyi

bar 'da lst , fresh eksileri hücresine sıfırlanır ve bu nedenle güvenlidir. bar kodunu değiştirir değil .

Alt çizgi

Genel olarak, alıntı verilerini ele almak en iyisidir sabit olarak '(1) gibi - bunları değiştirmeyin değiştirmeyin:

quote , argümanı değerlendirmeden döndürür. (x alıntı) , x değerini verir.    Uyarı : alıntı , dönüş değerini oluşturmaz, ancak yalnızca geri döner   Lisp okuyucu tarafından önceden oluşturulmuş değer (bilgi düğümüne bakınız)    Basılı Açıklama ). Bunun anlamı (a. B) değil    (cons 'a' b) ile aynıdır: önceki cons. Alıntı gerekir   asla yan etkilerle değiştirilmeyecek sabitlere ayrılmak,   Kendini değiştiren kodu beğenmediğiniz sürece. Bilgi içindeki yaygın tuzağı görün   Beklenmeyen sonuçların bir örneği için Yeniden Düzenleme düğümü ne zaman   alıntı bir nesne değiştirildi.

bir listeyi değiştirmek gerekiyorsa, çubuğunda yaptığınız gibi alıntı yerine list veya cons veya copy-list ile oluşturun .

Bkz. daha fazla örnekler .

ps - değişken isimlendirme

Emacs Lisp bir lisp-2 'dir, bu nedenle bunlar değişkeninizi adlandırmak için lst değişkenini belirtmek için bir sebep değildir. listesi .

18
katma
evet bu çok kafa karıştırıcı! Ama aynı zamanda gerçekten ilginç, lisp-1 nad lisp-2'ler hakkında hiçbir fikrim yoktu.
katma yazar Jeff, kaynak
Detaylı açıklama için teşekkürler! Ayrıca, ELISP'in lisp-2 olduğunu biliyorum ama yine de kendimi karıştırmak istemiyorum. (liste ('a' b 'c' c listesi)) ( let formunda olmasına rağmen) gibi bir şey ilk bakışta kafa karıştırıcı görünüyor.
katma yazar Mahesh, kaynak