2011-03-24 6 views
1

ここでは、関数の動作がその変数の名前に依存することを示す短いelispコードを示します。これはバグですか?関数の動作は変数の名前に依存する必要がありますか?

関数は変数xを使用して宣言されています。その関数がx以外の名前の変数で呼び出されると、期待どおりに機能します。しかし、xという名前の変数で呼び出された場合、失敗します。

私のシステムは


g5.tokyo.stp.isas.jaxa.jpに2008-04-05のGNU Emacsの22.2.1(PowerPCの-リンゴdarwin8.11.0、カーボンバージョン1.6.0)であります

これをemacsバッファーに貼り付け、最後の括弧の後ろにカーソルを置き、\ Cx \ Ceを押して、2回目に呼び出されたときに関数make-zeroが正しく機能することを確認します。

(progn 
    (defun make-zero (x) 
    "Simple function to make a variable zero." 
    (set x 0)) 

    (setq x 10) 

    (insert "\n Variable x is now equal to " (number-to-string x)) 

    (setq y 20) 

    (insert "\n Variable y is now equal to " (number-to-string y)) 

    (insert "\n\n Let us apply make-zero to y") 

    (make-zero 'y) 

    (insert "\n Variable y is now equal to " (number-to-string y)) 

    (insert "\n\n Let us apply make-zero to x") 

    (make-zero 'x) 

    (insert "\n Variable x is now equal to " (number-to-string x)) 

    (insert "\n\n Why make-zero had no effect on x? Is it because the name of the 
variable in the definition of make-zero, namely 'x', is the same as the name of 
the variable when make-zero was called? If you change the name of the variable 
in the definition of make-zero from x to z, this strange behaviour will 
disappear. This seems to be a bug in elisp.")) 

答えて

2

これはElisp(とLispの一般的な)動的バインディングの性質と同じくらいバグではありません。 'は参照を渡しません(つまり、C/C++では&と似ていません)、評価されていないシンボルが渡されます。それが評価される対象は、評価対象のスコープに依存します。つまり、関数内のスコープ内にあるxを取得します。

Lispでは、この問題を回避するにはマクロを使うのが普通です。

(defmacro make-zero (x) (list 'set x 0)) 

または

(require 'cl) 
(defmacro make-zero (x) `(set ,x 0)) 
+0

2番目の例ではcl.elは必要ありません。これがなければ、うまくいくはずです。 – Drew

2

それはバグではありません。 Scoping Rules For Variable Bindingsのマニュアルエントリを読んでいる間は、あなたの価値があります。

あなたが書いた関数setは、シンボル(第1引数の値)をとり、その値を第2引数の値に変更します。あなたが書いたmake-zeroは、入力引数にローカルにxをバインドします。したがって、シンボルxを渡すと、setはxの最初のバインディングを変更します。これはローカルバインディングになります。ここで

が異なる例です、あなたはただ、次のを持っていたとしましょう:

コードのスニペットを見てみると
(defun print-something (something) 
    (set 'something "NEW VALUE") 
    (insert something)) 

(print-something "OLD") ; inserts "NEW VALUE" 

、それはsetラインがsomethingのローカル値を変更するという意味を成していますか?

シンボルsomethingのグローバル設定があるかどうかは関係ありません。

別の例は次のとおりである。

バインディングは、あなたが set行が変わることを期待する
(defvar x "some global value") ;# could have used setq here 
(let ((x "local binding")) 
    (set 'x "new value")) 

letによって作成されたものまたはdefvarによって作成されたもの

あなたが書いた関数は、ちょうどletと全く同じことです。あなたは、グローバル変数の前に見える変数のローカルバインディングを作成しています。

変数への参照を渡したいのであれば、それを行う唯一の安全な方法はmacrosですが、私はこれを推奨しますが、lispの基本を理解するまではありません(b/cマクロは間違いなく複雑な)。それが、あなたの情熱なら、私がマクロに潜入するのをやめさせてはいけないと言っていました。

Emacs lispをプログラミングするのに良い紹介はhereです。

geekosaur's answerあなたが望むものを達成する方法を示す素晴らしい仕事です。

関連する問題