繰り返しの呼び出しで `let`の定数が変わるのはなぜですか?

このような foo 関数があるとします:

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

次に、次の方法でそれを使用します(1つずつ順次評価)。

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

ここで何が起こったのですか?

別の bar 関数を定義した場合:

(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 関数の let 形式の定数がこのように動作するのはなぜですか?それは意図されていますか?

11
追加された 編集された
ビュー: 4
これは、 stackoverflow.com/q/16670989/850781 の欺瞞です。
追加された 著者 shabbychef,
S.O.の複写で強調された別の重要な引用。これは(これは Ch f quote のものです): "警告: quote は戻り値を構成しませんが、Lisp読者が事前に構築した値"私は、lispへの新規参入者は、しばしば、lisp読者の概念全体を忘れていると思うので、「読んだ」段階と「評価する」段階の区別を学び理解するのに時間をかけて、一般的な言語を理解することは非常に有益です。上記のような特定の問題と同様に
追加された 著者 Mark Ireland,
厳密に言えば、 'はelispの読者マクロではありませんが(読者マクロはありません)、読込みフェーズでは必ず処理されます。しかし、それは重要なことではありません。私の指摘は、読者がすべてのテキストのlispコードをlispオブジェクトに変換することを理解していたことです。それは、表示されるテキストではなく、後でevalされるオブジェクトです。
追加された 著者 Mark Ireland,
そして、オブジェクトは評価中に操作の対象となる可能性があるので(この質問のように)、いつでもevalされているものは、最初に読み込まれたコードを表す必要はありません。もちろん、ほとんどの状況でそれは正確に行うでしょう - あなたはしばしばオブジェクトの観点から考える必要はありません。しかし、何が起こっているのかを知ることで、このようなことが要因となる状況を認識し理解することができます。
追加された 著者 Mark Ireland,
@phils 'は読書段階で処理される読者マクロであり、(list' a 'b' c) 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)))

いいもの

bar では、 lst新鮮なコンスセルに初期化されているので安全です。 bar はそのコードを変更しません

ボトムライン

一般に、引用されたデータを扱うのが最善です '(1)のように定数を変更する - 変更しない

quote は引数を評価せずに返します。 (quote x)x を生成します。   警告quote は戻り値を構成しませんが、   Lispリーダーによって事前に構築された値(infoノードを参照)   印刷表現)。これは、(a。b)が   と同じです(cons 'a' b):前者は短所ではありません。引用する必要があります   副作用によって決して変更されない定数のために予約され、   あなたが自己修正コードを好きでない限り。情報の共通の落とし穴を参照してください   予期しない結果の例については、再編成を参照してください。いつ   引用されたオブジェクトは変更されます。

リストを変更する必要がある場合は、 barのように quote ではなく list または cons または copy-list

詳細</​​a> を参照してください。

ps - 変数の名前付け

Emacs Lispは lisp-2 なので、これらは変数 lst の代わりに list

18
追加された
はい、それは非常に混乱しています!しかし、本当に興味深いことに、私はlisp-1 nad lisp-2のことを知らなかった。
追加された 著者 Jeff,
詳細な説明をありがとう!また、私はELispがlisp-2であることを知っていますが、私は自分自身を混乱させたくありません。 let フォームにありますが)(list(a 'b' c))のようなものは、最初の視点では非常に混乱しています。
追加された 著者 Mahesh,