私はGCLを使用してUbuntuでプログラミングしています。さまざまなソースからのCommonLISPに関するドキュメントから、let
がlocal変数を作成し、setq
がexisting変数の値を設定することを理解しています。 。以下の場合、2つの変数を作成し、それらの値を合計する必要があります。
setq
の使用(defun add_using_setq ()
(setq a 3) ; a never existed before , but still I'm able to assign value, what is its scope?
(setq b 4) ; b never existed before, but still I'm able to assign value, what is its scope?
(+ a b))
let
の使用(defun add_using_let ( )
(let ((x 3) (y 4)) ; creating variables x and y
(+ x y)))
どちらの場合も、私は同じ結果を達成しているようです。ここでsetq
とlet
を使用する違いは何ですか? setq
を使用する必要があるすべての場所で(構文的に簡単なため)let
を使用できないのはなぜですか?
setq
assigns値を変数に割り当てますが、let
は新しい変数/バインディングを導入します。例:で何が起こるか見てください
(let ((x 3))
(print x) ; a
(let ((x 89))
(print x) ; b
(setq x 73)
(print x)) ; c
(print x)) ; d
3 ; a
89 ; b
73 ; c
3 ; d
外側のlet
はローカル変数x
を作成し、内側のlet
は内側の変数をシャドウイングする別のローカル変数を作成します。 let
を使用して変数をシャドウイングしても、シャドウイングされた変数の値には影響しないことに注意してください。行x
のd
は外部のx
によって導入されたlet
であり、その値は変更されていません。 setq
は、呼び出される変数にのみ影響します。この例は、ローカル変数で使用されるsetq
を示していますが、特別な変数(つまり、動的にスコープされ、通常はdefparameter
またはdefvar
で定義される)で使用することもできます。
CL-USER> (defparameter *foo* 34)
*FOO*
CL-USER> (setq *foo* 93)
93
CL-USER> *foo*
93
setq
は(移植性が高い)create変数ではないのに対し、let
、defvar
、defparameter
、&cであることに注意してください。行う。 (まだ)変数ではない引数で呼び出されたときのsetq
の動作は定義されておらず、何をするかを決定するのは実装次第です。たとえば、SBCLは大声で不平を言います:
CL-USER> (setq new-x 89)
; in: SETQ NEW-X
; (SETQ NEW-X 89)
;
; caught WARNING:
; undefined variable: NEW-X
;
; compilation unit finished
; Undefined variable:
; NEW-X
; caught 1 WARNING condition
89
もちろん、これらの概念をよりよく理解するための最良の方法は、より多くのLISPコード(時間とともに付属)を読み書きし、HyperSpecのエントリを読み取り、相互参照、特に用語集のエントリに従うことです。たとえば、HyperSpecのsetq
およびlet
の簡単な説明は次のとおりです。
変数とバインディングについてもっと読みたいと思うかもしれません。 let
およびlet*
には、動的変数とspecial
宣言に関する特別な動作もあります(ただし、しばらくの間はそれについて知る必要はないでしょう)。場合によっては(おそらく知る必要はありません)。しばらくの間)変数が実際には変数ではない場合、setq
は実際にはsetf
と同等です。 HyperSpecに詳細があります。
Stack Overflowには、まったく重複していない質問がいくつかありますが、それでも、CommonLISPで使用できるさまざまな変数定義および代入演算子の使用法を理解するのに役立つ場合があります。
同じスコープ内の他の関数で値を使用できるようにしたいというまれな場合を除いて、ほとんどの場合、関数定義内で変数をバインドする方法にする必要があります。
私はemacsLISPマニュアルの説明が好きです:
letは、LISPインタープリターが変数を関数の一部ではない同じ名前の変数と混同しないように、シンボルを値にアタッチまたはバインドするために使用されます。
特別なフォームが必要な理由を理解するために、あなたが一般的に
the house', as in the sentence, “The house needs painting.” If you are visiting a friend and your Host refers to
the house 'と呼ぶ家を所有している状況を考えてみましょう。彼はあなたの家ではなく、彼の家を参照している可能性があります。別の家。あなたの友人が彼の家について言及していて、彼があなたの家について言及していると思うなら、あなたは混乱しているかもしれません。ある関数内で使用される変数が別の関数内で使用される変数と同じ名前であり、2つが同じ値を参照することを意図していない場合、LISPでも同じことが起こる可能性があります。 let特殊形式は、この種の混乱を防ぎます。
- http://www.gnu.org/software/emacs/manual/html_node/eintr/let.html
(setq x y)
は新しい値y
を記号x
で指定された変数に割り当て、オプションで新しいパッケージレベルの変数を定義します 1。これは、add_using_setq
を呼び出した後、現在のパッケージに2つの新しいパッケージレベルの変数があることを意味します。
(add_using_setq)
(format t "~&~s, ~s" a b)
3 4
を出力します-望ましい結果はありそうにありません。
対照的に、let
を使用する場合、関数の期間中、記号で指定された変数にのみ新しい値を割り当てるため、このコードはエラーになります。
(add_using_let)
(format t "~&~s, ~s" a b)
let
は、次のコードと同等であると考えてください。
(defun add-using-lambda ()
(funcall (lambda (a b) (+ a b)) 3 4))
余談ですが、他のプログラマーが書いたコードを調べて、名前を付けたりフォーマットしたりする方法を理解したいと思うでしょう。伝統的であることに加えて、それはあなたが本当に失いたくないいくつかの活版印刷の特性も持っています。
1この動作は非標準ですが、これは多くの一般的な実装で発生することです。それがかなり予測可能であるにもかかわらず、それは他の理由で悪い習慣と見なされます。ほとんどすべて同じ懸念が、グローバル変数の使用を思いとどまらせるでしょう。
lISPがまだ実行されている限り、シンボルの値をスコープから外すことができます。 (シンボルに値を割り当てます)
lISPがフォームの評価を終了した後は、LETで定義されたシンボルの値を取得できません。 (値をシンボルにバインドし、シンボルへの新しいバインドを作成します)
以下の例を検討してください。
;; with setq
CL-USER> (setq a 10)
CL-USER> a
10
;; with let
CL-USER> (let ((b 20))
(print b))
CL-USER> 20
CL-USER> b ; it will fail
; Evaluation aborted on #<UNBOUND-VARIABLE B {1003AC1563}>.
CL-USER>