リストの適切な再初期化?内部で何が起こっていますか?


8

私はもう少しelispを教えています、そして次の問題に遭遇しました:

リスト変数をリセットしたい場合、最初の評価後に更新されません。ここにいくつかのサンプルコードがあります:

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

私は2つのことに興味があります。1つ目は、「内部」で何が起こっているのかを詳しく知ることです。なぜそれが初めて機能し、その後の呼び出しで失敗するのでしょうか。

2番目のより実用的な質問は、リストを適切に再初期化する方法ですか、またはそのようなことを行う別の一般的な方法はありますか?

私が見つけた1つの回避策は、引用リストを使用して、次のようにコンテンツを評価することです。

(setq example `(,3)) 

2
概要:DOは期待しない'(some list)ようにeqする'(some list)- これまで .Thereは、一般的に目に見えて、リストを引用していること、コードが新しいリスト構造を毎回返すことLispでは保証するものではありません。一部のLisp実装では、そうなる場合もあれば、場合によってはそうなる場合もあります。他では、それは決してしません。いずれにしても、コードは実装からのそのような動作に依存してはなりません。新しいリスト構造が必要な場合はlistconsまたは同等のものを使用してください。
ドリュー

(この質問は重複していると思いますが、重複がどこにあるのかわかりません。)
Drew

1
ここでの問題はexample、変数として宣言されたことがないためsetq、新しい変数を宣言するかのように動作する必要があると思いますが、後でinitializeもう一度呼び出すと、新しい変数が作成され、modify古い変数が記憶されます...いずれにせよ、これは予期される動作ではsetqありませんが、変数として以前に導入されていないものでの使用も未定義になる可能性があります。
wvxvw 2015年

3
わかりました、私は何が起こるかを理解しました。'(3)はリテラル値として扱われるので、一度(setcar '(3) 2)、を行う(defvar foo '(3))などのときに(let ((foo '(3)))fooと等しい値を取得する可能性があります'(2)。「可能性が高い」と言うのは、この動作が保証されていないためです。定数サブ式の除去(特定のケース)として知られる、インタプリタが気が向くたびに行う一種の最適化です。ですから、abo-aboが書いたのは正確な理由ではありません。これは、Cで文字列リテラルを変更するようなものです(通常は警告が生成されます)。
wvxvw 2015年

回答:


5

多分これはいくつかの混乱を取り除くでしょう:

  • 関数initilizeは変数を初期化しませんexample。特定のコンスセルに設定されます-呼び出されるたびに同じコンスセル。が最初にinitilize呼び出されたときに、が評価された結果である新しいコンスセルにsetq割り当てexampleられ'(3)ます。同じコンスセルにinitilize再割り当てexampleするための後続の呼び出し。

  • initilize同じコンスセルをに再割り当てするだけなのでexample、呼び出されるたびにmodify同じコンスセルの車をに設定するだけ2です。

  • リストを初期化するには、listor cons(または同等のバッククォートsexp、`(,(+ 2 1))orなど`(,3))を使用します。たとえば、を使用します(list 3)

これを理解する鍵は、引用されたコンスセルが1回だけ評価され、その後同じコンスセルが返されることを知ることです。これは必ずしもすべてのLispが動作する方法ではありませんが、Emacs Lispが動作する方法です。

より一般的には、引用された可変オブジェクトを評価する動作は、言語に依存しない場合でも、実装に依存します。たとえば、Common Lispでは、この点で動作を定義する言語の定義(仕様)には何もないことは確かです。実装次第です。

要約: '(some list)がeq to'(some list)であることを期待しないでください-リストを目に見える形で引用するコードが毎回新しいリスト構造を返すという保証はLisp にはありません。一部のLisp実装では、そうなる場合もあれば、場合によってはそうなる場合もあります。他では、それは決してしません。いずれにしても、コードは実装からのそのような動作に依存してはなりません。新しいリスト構造が必要な場合はlistconsまたは同等のものを使用してください。


1
回答の最初の2行の降順のトーンは必要ですか?そうでなければ啓発的な答え。
asjo 2015年

@asjo:意図は決して屈服することではありませんでした。ごめんなさい。うまくいけば、今はもっとはっきりしている。
2015年

少しクリアしてくれてありがとう。「引数」という用語は、setq関数の引数 `(、3)を意味しましたが、引用されたリストの3を実際に評価しているので、少し不明確だと理解しています。それを編集します。
クレメラ2015年

@ドリューグレート!今読んだ方がわかりやすいです。
asjo 2015年

5

(setq example (list 3))このエラーを回避するために使用できます。

何が起こるかはinit、最初に含まれ(3)ているオブジェクトをに割り当てますexample。オブジェクトの値は1回だけ設定されます。その後、この値を変更します。

あなたがそれをよりよく理解しているなら、これはC ++でのあなたの例です:

#include <stdio.h>
#include <string.h>
char* example;
char* storage = 0;
char* create_object_once (const char* str) {
  if (storage == 0) {
    storage = new char[strlen (str)];
    strcpy (storage, str);
  }
  return storage;
}
void init () {
  example = create_object_once ("test");
}
void modify () {
  init ();
  printf ("%s\n", example);
  example[0] = 'f';
}
int main (int argc, char *argv[]) {
  modify ();
  modify ();
  modify ();
  return 0;
}

1
ありがとう、C ++はわかりませんが、あなたの例は理解できます。ただし、まだ気になることが1つあります。コード例では、まだ設定されていない場合にのみ初期値を使用してオブジェクトを作成する変数ストレージを導入しています。それはEmacs Lispインタープリターが何をしていることにどのように変換されますか?つまり、initilize関数を再評価してmodify再度呼び出した場合(3)、関数を再評価したためにのみ再び表示されます。
クレメラ2015年

1
詳細を知ることはできません(正確にはわかりません)が、(3)の一部である一時オブジェクトと考えてinitください。initのボディexampleはその一時オブジェクトのアドレスに設定され、その値には影響しません。
abo-abo 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.