Чаму канстанта ў `let` змяніць з паўторным выклікамі?

Скажам, у нас ёсць такі Foo функцыі:

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

Затым мы выкарыстоўваем яго наступным спосабам (паслядоўна ацэньваючы адзін за адным):

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

Што тут адбылося?

Калі я вызначаю іншы бар функцыі:

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

Затым выкарыстоўваць яго такім жа чынам:

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

Вынікі тут, здаецца разумным мне.

Так пытанне ў тым, чаму канстанта ў хай форма Foo функцыі паводзяць сябе такім чынам? прызначана яна?

11
Гэта контратип stackoverflow.com/q/16670989/850781
дададзена аўтар shabbychef, крыніца
Іншы ключавы цытаты выдзелены ў двух экземплярах на С.О. гэта (што з Ch ф цытатай ): «Увага: цытата ня канструюе вяртаецца значэнне, але проста вяртае значэнне, якое было папярэдне збудаваных Лісп чытача ». Я думаю, што пачаткоўцы LISP часта не звяртаючы ўвагі на ўсю канцэпцыю LISP чытача, і таму прымаць некаторы час, каб вывучыць і зразумець адрозненне паміж «чытаць» і «Eval» этапы вельмі карысна для разумення мовы ў цэлым, так як а таксама канкрэтныя пытанні, як адзін з прыведзеных вышэй.
дададзена аўтар Mark Ireland, крыніца
Строга кажучы, ' ня чытач макрасаў ў Elisp (Elisp не мае чытача макрасаў), але гэта, вядома, апрацоўваецца падчас чытання фазы. Гэта не важна, хоць. Мой пункт гледжання было пра разуменне таго, што чытач пераўтворыць ўсе тэкставую коду LISP у LISP аб'екты (адзін раз), і гэта тыя аб'екты, якія пасля Эвальд (часта некалькі разоў), а не тэкст, які вы бачыце.
дададзена аўтар Mark Ireland, крыніца
І таму, што аб'екты патэнцыйна схільныя маніпуляцыям падчас ацэнкі (як у гэтым пытанні), што быць Эвальд ў любы момант часу не <�я> патрабуецца , каб прадставіць код, які першапачаткова чытаць. Вядома, у большасці выпадкаў гэта будзе рабіць менавіта гэта - не часта даводзіцца думаць у тэрмінах аб'ектаў. Але ведаючы, што адбываецца за кулісамі, дазваляе распазнаваць і разумець сітуацыі, у якіх такога роду рэчы з'яўляецца фактарам.
дададзена аўтар Mark Ireland, крыніца
@phils Ці вы маеце на ўвазе, што з'яўляецца чытачом макрас, які апрацоўваецца ў "чытаць" стадыі, у той час як (спіс 'а «Ь» с) ацэньваецца ў " Eval »этап?
дададзена аўтар Mahesh, крыніца
@phils Дзякуй! Гэта мае сэнс.
дададзена аўтар Mahesh, крыніца

1 адказы

Вось мой адказ на ідэнтычны пытанне , належным чынам адрэдагаваны:

дрэнны

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.

Давайце на самай справе бачым, што адбываецца:

(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)))

добры

У бар , LST ініцыялізуецца свежыя мінусы клеткі і, такім чынам, з'яўляецца бяспечным. <�Код> бар гэта <�ет> не змяніць свой код.

Bottom Line

Увогуле, лепш лячыць цытуемы дадзеных як '(1) як канстанты - Do <�моцны> не змяніць іх:

цытата вяртае аргумент без яго ацэнкі. <�Код> (цытата х) дае х .   <�Моцны> Папярэджаньне : цытата ня канструюе вяртаецца значэнне, але толькі вяртаецца   значэнне, якое было папярэдне збудаваны Лісп чытача (гл інфа вузла    друкаванае ўяўленне ). Гэта азначае, што (а. Б) ня   ідэнтычны (Канса «а» б) : былы ня супраць. цытаванне павінна   быць зарэзерваваны для канстант, якія ніколі не будуць мадыфікаваны пабочнымі эфектамі,   калі Вам не падабаецца самомодифицирующийся код. Глядзіце агульную пастку ў інфармацыі   вузел Перагрупоўка для прыкладу нечаканых вынікаў калі   у двукоссі аб'ект змяняецца.

Калі вам трэба змяніць спіс , стварыць яго з спіс або мінусах або копія-ліст замест цытата , як вы робіце ў бар .

См больш прыклады .

пс - пераменная найменне

Emacs Lisp з'яўляецца сюсюкаюць-2 таму яны няма ніякіх падставаў назваць сваю зменную LST замест спіс .

18
дададзена
ды, што гэта вельмі збівае з толку! Але таксама вельмі цікава, я не меў ні найменшага падання аб сюсюкаюць-1-над-LISP-2s.
дададзена аўтар Jeff, крыніца
Дзякуй за падрабязнае тлумачэнне! Акрамя таго, я ведаю, што Elisp з'яўляецца шепелявость-2, але ўсё ж, я не хачу, каб заблытаць сябе. Нешта накшталт (спіс (спіс «а» Ь «с)) (хоць у хай форма) выглядае вельмі заблытанай на першы погляд.
дададзена аўтар Mahesh, крыніца