Saya belajar lebih banyak tentang elisp dan menemui masalah berikut:
Jika saya ingin mengatur ulang variabel daftar, itu tidak akan diperbarui setelah evaluasi pertama. Berikut ini beberapa contoh kode:
(defun initilize ()
(setq example '(3)))
(defun modify ()
(initilize)
(message "%S" example)
(setcar example 2))
; M-x eval-buffer RET
(modify) ; message --> (3)
(modify) ; message --> (2)
(modify) ; message --> (2)
Saya tertarik pada dua hal. Yang pertama adalah untuk mempelajari lebih lanjut tentang apa yang terjadi "di bawah tenda" jadi mengapa itu bekerja pertama kali dan gagal pada panggilan berikutnya?
Pertanyaan kedua dan lebih praktis adalah bagaimana menginisialisasi ulang daftar dengan benar atau adakah cara lain yang umum untuk melakukan sesuatu seperti itu?
Satu solusi yang saya temukan sendiri adalah dengan menggunakan daftar yang dikutip dan mengevaluasi konten seperti ini:
(setq example `(,3))
list
quote
mutability
clemera
sumber
sumber
'(some list)
untuk menjadieq
ke'(some list)
- pernah .Ada umumnya ada jaminan dalam Lips bahwa kode yang tampak mengutip daftar kembali struktur daftar baru setiap kali. Dalam beberapa implementasi Lisp mungkin, atau mungkin beberapa waktu. Pada orang lain, itu tidak pernah terjadi. Kode Anda seharusnya tidak tergantung pada perilaku seperti itu dari implementasi. Jika Anda ingin struktur baru daftar, penggunaanlist
ataucons
atau setara.example
belum pernah dinyatakan sebagai variabel, jadisetq
harus bertindak seolah-olah mendeklarasikan variabel baru, tetapi nanti ketika Anda memanggilinitialize
lagi variabel baru sedang dibuat, sementaramodify
mengingat yang lama ... dalam hal apa pun ini bukan perilaku yang diharapkan, namun, penggunaansetq
dengan sesuatu yang belum diperkenalkan sebelumnya sebagai variabel mungkin juga tidak terdefinisi.'(3)
diperlakukan sebagai nilai literal, sehingga setelah Anda(setcar '(3) 2)
, setiap kali Anda melakukan(defvar foo '(3))
atau(let ((foo '(3)))
dan sebagainya Anda mungkin akan mendapatkan nilaifoo
sama dengan'(2)
. Saya katakan "mungkin" karena perilaku ini tidak dijamin, itu semacam optimisasi yang dilakukan penerjemah kapan pun rasanya, sesuatu yang dikenal sebagai konstanta penghapusan subekspresi (kasus tertentu). Jadi, apa yang ditulis abo-abo sebenarnya bukan alasannya. Ini lebih seperti memodifikasi string literal dalam C (yang biasanya menghasilkan peringatan).Jawaban:
Mungkin ini akan menghilangkan beberapa kebingungan:
Fungsi
initilize
Anda tidak menginisialisasi variabelexample
. Ini menetapkannya ke sel kontra tertentu - sel kontra yang sama setiap kali dipanggil. Pertama kaliinitilize
dipanggil,setq
ditugaskanexample
ke sel kontra baru, yang merupakan hasil evaluasi'(3)
. Panggilan selanjutnya untukinitilize
hanya menetapkan kembaliexample
ke sel kontra yang sama.Karena
initilize
baru saja menugaskan ulang sel kontra yang samaexample
,modify
hanya mengatur mobil sel kontra yang sama untuk2
setiap kali dipanggil.Untuk menginisialisasi daftar, gunakan
list
ataucons
(atau sexp backquote yang setara, seperti`(,(+ 2 1))
atau`(,3)
). Sebagai contoh, gunakan(list 3)
.Kunci untuk memahami ini adalah untuk mengetahui bahwa sel kontra yang dikutip dievaluasi hanya sekali, dan setelah itu sel kontra yang sama dikembalikan. Ini belum tentu cara semua Lisps berperilaku, tetapi itulah cara Emacs Lisp berperilaku.
Secara umum, perilaku mengevaluasi objek yang dapat diubah yang dikutip bergantung pada implementasi, jika tidak bergantung pada bahasa. Dalam Common Lisp, misalnya, saya cukup yakin bahwa tidak ada dalam definisi (spek) bahasa yang mendefinisikan perilaku dalam hal ini - itu diserahkan kepada implementasinya.
Rangkuman: Jangan berharap '(beberapa daftar) menjadi sama dengan' (beberapa daftar) - selamanya. Pada umumnya tidak ada jaminan dalam Lisp bahwa kode yang dengan jelas mengutip daftar mengembalikan struktur daftar baru setiap kali. Dalam beberapa implementasi Lisp mungkin, atau mungkin beberapa waktu. Pada orang lain, itu tidak pernah terjadi. Kode Anda seharusnya tidak tergantung pada perilaku seperti itu dari implementasi. Jika Anda ingin struktur baru daftar, penggunaan
list
ataucons
atau setara.sumber
Anda dapat menggunakan
(setq example (list 3))
untuk menghindari kesalahan ini.Apa yang terjadi adalah
init
pihak yang ditunjuk obyek yang awalnya berisi(3)
keexample
. Ini menetapkan nilai objek hanya sekali. Selanjutnya, Anda memodifikasi nilai ini.Inilah contoh Anda dalam C ++, jika Anda memahami itu dengan lebih baik:
sumber
initilize
fungsi dan meneleponmodify
lagi itu akan muncul(3)
lagi hanya karena saya mengevaluasi kembali fungsi.(3)
menjelaskan secara spesifik (tidak tahu persis), tetapi anggap sebagai objek sementara yang menjadi bagiannyainit
.init
Tubuh diaturexample
ke alamat objek sementara itu, tidak menyentuh nilainya.