10

次のような関数を定義すると

(defun foo(x)
  (setf x somevalue))

xローカル変数またはグローバルとして定義されていますか? setf/q を使用すると、値がグローバルに設定されます。それがグローバルである場合、誰でもLisp以外でローカル変数を定義する方法を教えてもらえますletか?

ありがとう!

次の例を考えてみましょう

(let ((x 0)) 
  (defun foo (y) 
    (when (equal x 0) (setq x y)) 
    (when (< x y) (setq x y))
    x))

foolikeに何らかの入力を与えると、2 が返されます。この(foo 2)関数をもう一度実行すると、(foo 1)2 が返され、3 が返され(foo 3)ます。これが実際に実行したいことです。xclisp ターミナルから関数外の変数にアクセスしようとしても、アクセスできません。再度関数にアクセスすると、以前の の値が保持されているようですx

ありがとう!

4

3 に答える 3

10

C、C ++、Java、Pythonなどの他の言語との類似点を描くために、コードが変更するのは、Lisperが使用する表現ではない場合でも、「ローカル変数」です(Lisp用語での表現はローカルの「バインディング」になります) )。

例のように関数パラメーターを使用するか、次のような標準形式を使用して、ローカル変数を作成できます。

  • (let ((x 12)) ...)
  • (do ((x 0 (1+ i))) ...)
  • (dotimes (x 10) ...)
  • (loop for x from 0 to 10 do ...)

一方、実装では、すべてのローカル変数がパラメーターを使用して作成され、他の形式は単にそれに拡張されたマクロである可能性があります。例えば:

(let ((x 10)) ...)

と同等です

(funcall (lambda (x) ...) 10)

また、実際にコードフラグメントを読み取ると、x特別に宣言されている可能性があるため、ある意味で「グローバル変数」である可能性があることにも注意してください。

(defvar x 12)
;; ==> x

(defun bar ()
  (format t "The value of x is ~a" x))
;; ==> bar

(defun foo (x)
  (bar))
;; ==> foo

(foo 42)
The value of x is 42
;; ==> NIL

x
;; ==> 12

変数を「特別な」と宣言すると、(defvar ...)それを使用して処理が異なります。つまり、パラメーターとして使用するか、フォームで使用するたびに(let ..)、コードが実行するのは、現在の値を保存し、新しく提供された値を使用してからです。関数またはを終了した後に値を復元しますlet

したがって、これらの変数は両方とも「グローバル」(外部関数がそれらを見ることができるため)であると同時にローカル(関数またはletの終了後に前の値が復元されるため)でもあります。

標準的な規則では、特別な変数に「イヤーマフ」という名前を付けます。つまり、名前の最初と最後の両方にアスタリスクを付けます。

(defvar *x* 12)

これは、誰があなたのコードを読んで、変数が特別であることを理解するのに役立ちます。ただし、これは言語によって義務付けられておらず、特別な変数には任意の名前を使用できることに注意してください。

C、C ++、Java、Pythonの特別な変数に似たものはありません。

setqとについての最後の注意setf。Lispの低レベルを理解して、なぜ必要なのかを理解する必要があるため、ここでは少し注意setqが必要です。Common Lispを使用している場合は、単に忘れてsetq、常にを使用する必要がありますsetf

setfは、必要なときに拡張されるマクロsetqです(ただし、必要なときにsetq変更することもできsetfます(シンボルマクロ)。これは、初心者にとって混乱を招く可能性がある場所です)。

最後の例は「クロージャ」の場合です。関数(フォームで名前が付けられているかどうかに関係なく(lambda ...))を定義すると、関数は表示されている変数を「キャプチャ」して後で使用できます。よく見られるより単純なケースは「加算器」です。

(defun adder (x)
  (lambda (y) (incf x y)))

この関数は、渡された値を内部トータライザーに追加し続ける関数を返します。

(let ((f (adder 10)))
  (print (funcall f 3))
  (print (funcall f 9))
  (print (funcall f 11)))

出力は13(10 + 3)、22(13 + 9)、33(22 + 11)になります。

匿名関数はローカル変数を「キャプチャ」しx、関数を終了した後でも使用できますadder。C、C ++、Javaなどの言語では、変数を定義したスコープを終了すると、ローカル変数は存続できません。

C ++ 11には無名関数がありますが、それでも変数をキャプチャしてスコープを存続させることはできません(無名関数のローカル変数にコピーすることはできますが、これは同じではありません)。

于 2012-09-08T19:58:20.293 に答える
4

実際にxは、関数に対してローカルであるため、setfor setqing はローカル変数のみを変更しますx

質問の 2 番目の部分に答えるには、let 以外のローカル変数を定義する別の方法があります。

(funcall (lambda (var1 var2...)
           ... use var1 var2 ...)-
   val1 val2 ...)

実際、次のdefunように実装できます。

`(setf (symbol-function ,name) (lambda ,args ,@body))

私がチェックしたすべての実装はそれ以上のことをします。

また、symbol-function場所ではこれを行うことができます:

(setf (symbol-function '+) (function -))

それは一般的に悪い考えですが。

2 番目の質問

そこで起こっているのはx、 を含むスコープに対してローカルであるということdefunです。グローバル変数ではありません。あなたがそれでやっていることは、周囲のスコープ内のすべての字句スコープの ( /defunで作成されていない) 変数を「キャプチャ」し、将来のためにそれらを保持するクロージャを作成することです。の値を調べたい場合は、別の関数を追加しますdefvardefparameterx

(defun get-x () x)

の中にlet。別の言い方をすれば、関数が存在するxローカルです。提供した状況は次のようになります。let

(let ((x 3))
    (print x)
    (let ((y 7))
        (print (list x y))
        (setf x y))
    (print x))

ただし、内部は a ( a ) にlet置き換えられます。defunlambda

于 2012-09-08T19:14:36.497 に答える
0

コードxには、関数のパラメーターが含まれているため、f関数に対してローカルです。toを呼び出すとsetf、新しい変数は作成されませんが、既存のローカル変数の値が設定されるだけですxsetqの代わりに使用setfすると、同じように動作します。

于 2012-09-08T19:03:19.227 に答える