Giriş niteliğindeki bir Lisp kitabının ana bölümlerini tamamladıktan sonra, özel operatörün (quote)
(veya eşdeğer '
) işlevinin ne yaptığını hala anlayamadım , ancak bu gördüğüm tüm Lisp kodunda oldu.
Bu ne işe yarıyor?
Giriş niteliğindeki bir Lisp kitabının ana bölümlerini tamamladıktan sonra, özel operatörün (quote)
(veya eşdeğer '
) işlevinin ne yaptığını hala anlayamadım , ancak bu gördüğüm tüm Lisp kodunda oldu.
Bu ne işe yarıyor?
Yanıtlar:
Kısa cevap varsayılan değerlendirme kuralları Baypas ve yapılacak değil , ifade (sembol veya s-exp) değerlendirmek Yazılan tam olarak işlevine birlikte geçirmeden.
Uzun Cevap: Varsayılan Değerlendirme Kuralı
Normal (buna daha sonra geleceğim) işlev çağrıldığında, ona iletilen tüm argümanlar değerlendirilir. Bu, şunu yazabileceğiniz anlamına gelir:
(* (+ a 2)
3)
Bu da 2'yi (+ a 2)
değerlendirerek değerlendirir a
. Sembolün değeri a
, mevcut değişken bağlama setinde aranır ve sonra değiştirilir. Say a
şu anda 3 değerine bağlıdır:
(let ((a 3))
(* (+ a 2)
3))
(+ 3 2)
Alardık, sonra 3 ve 2'de 5'i elde ediyoruz. Orijinal formumuz şimdi (* 5 3)
15 veriyor.
quote
Zaten Açıklayın !
Peki. Yukarıda görüldüğü gibi, bir fonksiyonun tüm argümanları değerlendirilir, bu nedenle eğer sembolü a
değil de değerini iletmek isterseniz, onu değerlendirmek istemezsiniz. Lisp sembolleri hem değerleri hem de karma tabloların anahtarları gibi dizeleri başka dillerde kullandığınız işaretçileri ikiye katlayabilir.
İşte burada quote
devreye giriyor. Bir Python uygulamasından kaynak tahsisatlarını çizmek istediğinizi, bunun yerine çizimini Lisp'te yapmak istediğinizi varsayalım. Python uygulamanızın şuna benzer bir şey yapmasını sağlayın:
print("'(")
while allocating:
if random.random() > 0.5:
print(f"(allocate {random.randint(0, 20)})")
else:
print(f"(free {random.randint(0, 20)})")
...
print(")")
Size şöyle görünen çıktı vermek (biraz güzelleştirilmiş):
'((allocate 3)
(allocate 7)
(free 14)
(allocate 19)
...)
quote
Varsayılan kuralın uygulanmamasına neden olan ("tik") hakkında söylediklerimi hatırlıyor musunuz? İyi. Ne aksi olur değerleri olduğunu allocate
ve free
aranmasıdır ve bunu istemiyoruz. Lisp'imizde yapmak istiyoruz:
(dolist (entry allocation-log)
(case (first entry)
(allocate (plot-allocation (second entry)))
(free (plot-free (second entry)))))
Yukarıda verilen veriler için, aşağıdaki işlev çağrıları dizisi yapılacaktır:
(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)
Ama Ne Hakkında list
?
Eh, bazen do argümanları değerlendirmek istiyoruz. Bir sayıyı ve bir dizgeyi işleyen ve sonuçta ortaya çıkan şeylerin bir listesini veren şık bir fonksiyonunuz olduğunu varsayalım. Yanlış bir başlangıç yapalım:
(defun mess-with (number string)
'(value-of-number (1+ number) something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
Hey! İstediğimiz bu değil. Bazı argümanları seçerek değerlendirmek ve diğerlerini sembol olarak bırakmak istiyoruz . # 2'yi deneyin!
(defun mess-with (number string)
(list 'value-of-number (1+ number) 'something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
Sadece Değil quote
, Amabackquote
Çok daha iyi! Bu arada, bu model (çoğunlukla) makrolarda o kadar yaygındır ki, bunu yapmak için özel bir sözdizimi vardır. Geri alıntı:
(defun mess-with (number string)
`(value-of-number ,(1+ number) something-with-string ,(length string)))
Kullanmaya benzer quote
, ancak bazı argümanların önüne virgül koyarak açık bir şekilde değerlendirme seçeneğiyle. Sonuç, kullanmaya eşdeğerdir list
, ancak bir makrodan kod üretiyorsanız, genellikle döndürülen kodun yalnızca küçük parçalarını değerlendirmek istersiniz, bu nedenle geri alıntı daha uygundur. Daha kısa listeler list
için daha okunaklı olabilir.
Hey, Unutmuşsun quote
!
Peki bu bizi nereye bırakıyor? Oh doğru, quote
aslında ne yapıyor ? Sadece argümanlarını değerlendirilmemiş olarak döndürür! Başlangıçta normal işlevler hakkında söylediklerimi hatırlıyor musunuz? Bazı operatörler / işlevler ihtiyaç olduğuna dikkat dönüşler için değil onların argümanları değerlendirir. IF gibi - başka şubenin alınmasa değerlendirilmesini istemezsiniz, değil mi? Sözde özel operatörler , makrolarla birlikte bu şekilde çalışır. Özel operatörler aynı zamanda dilin "aksiyomu" - minimum kurallar kümesi - Lisp'in geri kalanını farklı şekillerde birleştirerek uygulayabilirsiniz.
quote
Yine de geri dönelim:
Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL
Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL
Şununla karşılaştır (Steel-Bank Common Lisp'de):
Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING {A69F6A9}>:
The variable SPIFFY-SYMBOL is unbound.
Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0]
Çünkü spiffy-symbol
mevcut kapsamda yok!
Özetliyor
quote
, backquote
(virgülle) ve list
listeler oluşturmak için kullandığınız araçlardan bazılarıdır, bunlar yalnızca değer listeleri değil, aynı zamanda hafif (a tanımlamaya gerek yoktur struct
) veri yapıları olarak da kullanılabilir!
Daha fazla bilgi edinmek istiyorsanız, zaten geniş çapta programlamayla ilgileniyorsanız, Lisp öğrenmeye pratik bir yaklaşım için Peter Seibel'in Practical Common Lisp kitabını öneririm . Sonunda Lisp yolculuğunuzda paketleri de kullanmaya başlayacaksınız. Ron Garret'in The Idiot's Guide to Common Lisp Packages size bunların iyi bir açıklamasını verecektir.
Mutlu hacklemeler!
this
ardından is
, daha sonra true
, ancak yalnızca döndürülen sonuncuyu bakın. (bu ve doğru ayrı ifadeler)
"Beni değerlendirme" yazıyor. Örneğin, bir listeyi kod olarak değil de veri olarak kullanmak istiyorsanız, önüne bir alıntı koyarsınız. Örneğin,
(print '(+ 3 4))
"(+ 3 4)"
(print (+ 3 4))
yazdırırken "7" yazdırır
unquote
komut var mı?
eval
: (print (eval '(+ 3 4)))
. Lisps'i bu kadar harika yapan şey budur: listeler koddur ve kod da listedir, böylece bir Lisp programı kendi kendini yönetebilir.
Diğer insanlar bu soruyu takdire şayan bir şekilde cevapladılar ve Matthias Benkard mükemmel bir uyarıda bulundu.
DAHA SONRA DEĞİŞTİRECEĞİNİZ LİSTELER OLUŞTURMAK İÇİN TEKLİF KULLANMAYIN. Spesifikasyon, derleyicinin alıntılanan listeleri sabitler olarak ele almasına izin verir. Genellikle, bir derleyici, sabitleri bellekte onlar için tek bir değer oluşturarak ve ardından sabitin göründüğü tüm konumlardan bu tek değere referans vererek optimize eder. Başka bir deyişle, sabiti anonim bir küresel değişken gibi ele alabilir.
Bu bariz sorunlara neden olabilir. Bir sabiti değiştirirseniz, tamamen ilgisiz kodda aynı sabitin diğer kullanımlarını çok iyi değiştirebilir. Örneğin, bazı değişkenleri bazı işlevlerde '(1 1) ile karşılaştırabilir ve tamamen farklı bir işlevde,' (1 1) ile bir liste başlatabilir ve sonra ona daha fazla şey ekleyebilirsiniz. Bu işlevleri çalıştırdıktan sonra, ilk işlevin artık şeylerle tam olarak eşleşmediğini görebilirsiniz, çünkü şimdi değişkeni '(1 1 2 3 5 8 13) ile karşılaştırmaya çalışıyor, bu da ikinci işlevin döndürdüğü şeydir. Bu iki işlev tamamen ilgisizdir, ancak sabitlerin kullanımından dolayı birbirlerini etkilerler. Tamamen normal bir liste yinelemesinin aniden sonsuz döngü yapması gibi daha çılgın kötü etkiler bile olabilir.
Karşılaştırma gibi sabit bir listeye ihtiyacınız olduğunda alıntı kullanın. Sonucu değiştireceğiniz zaman listeyi kullanın.
(list (+ 1 2))
çoğu zaman kullanman gerekiyor gibi görünüyor . Öyleyse (+ 1 2)
böyle bir örneğin içerisinin değerlendirilmesini nasıl engelliyorsunuz ? Bir unquote
emir var mı?
'((3))
, yoksa eşdeğer '((+ 1 2))
? İkincisi, daha fazla kullanmak zorunda list
: (list (list '+ 1 2))
. Ya da eşdeğerini istiyorsanız '(+ 1 2)
, sadece (list '+ 1 2)
. Ve unutmayın, listeyi değiştirmiyorsanız, alıntı kullanmaktan çekinmeyin: '(+ 1 2)
sadece onunla karşılaştırma yapıyorsanız, yanlış bir şey yok.
Bu soruya verilen bir cevap, QUOTE'un "liste veri yapılarını oluşturduğunu" söylüyor. Bu tam olarak doğru değil. QUOTE bundan daha temeldir. Aslında, QUOTE önemsiz bir operatördür: Amacı, herhangi bir şeyin olmasını önlemektir . Özellikle hiçbir şey yaratmaz.
(QUOTE X) temelde "hiçbir şey yapma, bana sadece X ver." X'in (QUOTE (ABC)) 'deki gibi bir liste veya (QUOTE FOO)' daki gibi bir sembol olması gerekmez. Her ne olursa olsun herhangi bir nesne olabilir. Aslında, (LIST 'QUOTE SOME-NESNE) tarafından üretilen listenin değerlendirilmesinin sonucu, her ne olursa olsun, her zaman SOME-OBJECT sonucunu verecektir.
Şimdi, (QUOTE (ABC)) 'nin elemanları A, B ve C olan bir liste yaratmış gibi görünmesinin nedeni, böyle bir listenin gerçekten döndüğü şey olmasıdır; ancak QUOTE formu değerlendirildiğinde, liste genellikle bir süredir mevcuttur (QUOTE formunun bir bileşeni olarak!), kodun yürütülmesinden önce yükleyici veya okuyucu tarafından oluşturulmuştur.
Bunun yeni başlayanlar için oldukça sık tökezleme eğiliminde olan bir sonucu, QUOTE formu tarafından döndürülen bir listeyi değiştirmenin çok akıllıca olmamasıdır. QUOTE tarafından döndürülen veriler, tüm amaç ve amaçlar için, yürütülen kodun bir parçası olarak kabul edilmeli ve bu nedenle salt okunur olarak ele alınmalıdır!
Alıntı, bir formun yürütülmesini veya değerlendirilmesini engeller, bunun yerine veriye dönüştürür. Genel olarak verileri daha sonra değerlendirerek çalıştırabilirsiniz.
alıntı liste veri yapıları oluşturur, örneğin aşağıdakiler eşdeğerdir:
(quote a)
'a
Listeler (veya ağaçlar) oluşturmak için de kullanılabilir:
(quote (1 2 3))
'(1 2 3)
Practical Common Lisp (çevrimiçi olarak okunabilen) gibi lisp üzerine bir giriş kitabı edinmenin en iyisi muhtemelen .
Emacs Lisp'te:
Ne alıntı yapılabilir?
Listeler ve semboller.
Bir sayıdan alıntı yapmak, sayının kendisi olarak değerlendirilir:
'5
ile aynıdır 5
.
Listelerden alıntı yaptığınızda ne olur?
Örneğin:
'(one two)
değerlendirir
(list 'one 'two)
hangi değerlendirilir
(list (intern "one") (intern ("two")))
.
(intern "one")
"bir" adlı bir sembol yaratır ve onu "merkezi" bir karma haritada saklar, böylece her söyleyişinizde 'one
, adı geçen sembol "one"
o merkezi karma haritada aranacaktır.
Ama sembol nedir?
Örneğin, OO dillerinde (Java / Javascript / Python) bir sembol name
, sembolün adı gibi bir alanı olan bir nesne olarak temsil edilebilir."one"
yukarıdaki ve veri ve / veya kod bu nesne ile ilişkilendirilebilir.
Yani Python'da bir sembol şu şekilde uygulanabilir:
class Symbol:
def __init__(self,name,code,value):
self.name=name
self.code=code
self.value=value
Örneğin Emacs Lisp'de bir sembol 1) kendisiyle ilişkilendirilmiş veriye sahip olabilir VE (aynı zamanda - aynı sembol için) 2) onunla ilişkili kod - bağlama bağlı olarak ya veri ya da kod çağrılır.
Örneğin, Elisp'te:
(progn
(fset 'add '+ )
(set 'add 2)
(add add add)
)
değerlendirir 4
.
Çünkü şu şekilde (add add add)
değerlendirir:
(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4
Yani, örneğin, Symbol
yukarıda Python'da tanımladığımız sınıfı kullanarak , bu add
ELisp-Symbol Python'da şu şekilde yazılabilir:Symbol("add",(lambda x,y: x+y),2)
.
IRC #emacs'taki insanlara sembolleri ve alıntıları bana açıkladıkları için çok teşekkürler.
Argümanın değerini iletmek yerine bir argümanın kendisini iletmek istediğimizde, o zaman alıntı kullanırız. Çoğunlukla C programlama Dilinde bulunmayan listeler, çiftler ve atomların kullanılması sırasında geçen prosedürle ilgilidir (çoğu insan C programlamayı kullanarak programlamaya başlar, Bu nedenle kafamız karışır) Bu, lisp'in bir lehçesi olan Scheme programlama dilindeki koddur. ve sanırım bu kodu anlayabilirsiniz.
(define atom? ; defining a procedure atom?
(lambda (x) ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f
Son satır (atom? 'Abc), abc'nin atom olup olmadığını kontrol etme prosedürüne olduğu gibi abc'yi geçiyor, ancak geçtiğinizde (atom? Abc) abc'nin değerini kontrol ediyor ve değerini o. O zamandan beri ona herhangi bir değer sağlamadık
Code is data and data is code. There is no clear distinction between them.
Bu, herhangi bir lisp programcısının bildiği klasik bir ifadedir.
Bir kodu alıntı yaptığınızda, bu kod veri olacaktır.
1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)
1 ]=> (+ 2 3 4)
;Value: 9
Bir kodu alıntı yaptığınızda, sonuç o kodu temsil eden veriler olacaktır. Dolayısıyla, bir programı temsil eden verilerle çalışmak istediğinizde, o programdan alıntı yaparsınız. Bu aynı zamanda atomik ifadeler için de geçerlidir, sadece listeler için değil:
1 ]=> 'code
;Value: code
1 ]=> '10
;Value: 10
1 ]=> '"ok"
;Value: "ok"
1 ]=> code
;Unbound variable: code
Lisp'e gömülü bir programlama dili yaratmak istediğinizi varsayarsak '(+ 2 3)
, programlara anlamsal bir yorum vererek, şemada alıntılanan (gibi ) ve oluşturduğunuz dilde kod olarak yorumlanan programlarla çalışacaksınız . Bu durumda verileri saklamak için alıntı kullanmanız gerekir, aksi takdirde veriler harici dilde değerlendirilecektir.
Alıntı, bağımsız değişkenlerinin dahili temsilini döndürür. Sözün ne işe yaramadığına dair çok fazla açıklamayı gözden geçirdikten sonra , ampul yanmaya başladı. Eğer REPL, işlev adlarını alıntı yaptığım zaman BÜYÜK HARFLERE dönüştürmediyse, aklıma gelmemiş olabilir.
Yani. Sıradan Lisp fonksiyonları, argümanlarını dahili bir temsile dönüştürür, argümanları değerlendirir ve fonksiyonu uygular. Alıntı, argümanlarını dahili bir temsile dönüştürür ve sadece onu döndürür. Bu alıntıda "değerlendirme yapma" demek teknik olarak doğrudur, ancak ne yaptığını anlamaya çalışırken bana ne yapmadığını söylemek sinir bozucuydu. Ekmek kızartma makinem de Lisp işlevlerini değerlendirmiyor; ama bir tost makinesinin ne yaptığını bu şekilde açıklamazsınız.
Anotörün kısa cevabı:
quote
Bunu değerlendirilmeden araçlar ve backquote alıntı ama bırakın olduğunu arka kapılar .
İyi bir referans:
Emacs Lisp Referans Kılavuzu bunu çok netleştirir
9.3 Alıntı yapma
Özel biçimli alıntı, değerlendirilmeden, yazıldığı şekliyle tek bağımsız değişkenini döndürür. Bu, kendi kendini değerlendiren nesneler olmayan sabit sembolleri ve listeleri bir programa dahil etmenin bir yolunu sağlar. (Sayılar, dizeler ve vektörler gibi kendi kendini değerlendiren nesnelerden alıntı yapmak gerekli değildir.)
Özel Form: alıntı nesnesi
This special form returns object, without evaluating it.
Alıntı programlarda çok sık kullanıldığından, Lisp bunun için uygun bir okuma sözdizimi sağlar. Bir kesme işareti karakteri ('' ') ve ardından gelen bir Lisp nesnesi (okuma sözdiziminde), birinci öğesi alıntı ve ikinci öğesi nesne olan bir listeye genişler. Bu nedenle, okuma sözdizimi 'x, (tırnak x) için bir kısaltmadır.
Alıntı kullanan bazı ifade örnekleri şunlardır:
(quote (+ 1 2))
⇒ (+ 1 2)
(quote foo)
⇒ foo
'foo
⇒ foo
''foo
⇒ (quote foo)
'(quote foo)
⇒ (quote foo)
9.4 Geri alıntı
Backquote yapıları, bir listeden alıntı yapmanıza, ancak bu listenin öğelerini seçerek değerlendirmenize olanak tanır. En basit durumda, özel form teklifiyle aynıdır (önceki bölümde açıklanmıştır; Alıntı yapma konusuna bakın). Örneğin, bu iki form aynı sonuçları verir:
`(a list of (+ 2 3) elements)
⇒ (a list of (+ 2 3) elements)
'(a list of (+ 2 3) elements)
⇒ (a list of (+ 2 3) elements)
Geri alıntı için argümanın içindeki özel işaretçi ',' sabit olmayan bir değeri belirtir. Emacs Lisp değerlendiricisi, ',' argümanını değerlendirir ve değeri liste yapısına koyar:
`(a list of ,(+ 2 3) elements)
⇒ (a list of 5 elements)
Liste yapısının daha derin düzeylerinde de ',' ile değiştirmeye izin verilir. Örneğin:
`(1 2 (3 ,(+ 4 5)))
⇒ (1 2 (3 9))
Ayrıca ', @' özel işaretini kullanarak değerlendirilmiş bir değeri sonuç listesine ekleyebilirsiniz. Eklenen listenin öğeleri, ortaya çıkan listenin diğer öğeleriyle aynı düzeyde öğeler haline gelir. '' 'Kullanmadan eşdeğer kod genellikle okunamaz. İşte bazı örnekler:
(setq some-list '(2 3))
⇒ (2 3)
(cons 1 (append some-list '(4) some-list))
⇒ (1 2 3 4 2 3)
`(1 ,@some-list 4 ,@some-list)
⇒ (1 2 3 4 2 3)