Next: 変数がvoidのとき, Previous: 変更不可な変数, Up: 変数 [Contents][Index]
グローバル変数は新しい値で明示的に置き換えるまで値が持続します。変数にローカル値(local value) — Lispプログラム内の特定の部分で効果をもつ — を与えると便利なときがあります。変数がローカル値をもつとき、わたしたちは変数がその値にローカルにバインド(locally bound)されていると言い、その変数をローカル変数(local variable)と呼びます。
たとえば関数が呼び出されるとき、関数の引数となる変数はローカル値(その関数の呼び出しにおいて実際の引数に与えられた値)を受け取ります。これらのローカルバインディングは関数のbody内で効果をもちます。他にもたとえばスペシャルフォームlet
は特定の変数にたいして明示的にローカルなバインディングを確立して、これはlet
フォームのbody内だけで効果を持ちます。
これにたいしてグローバルなバインディング(global binding)とは、(概念的には)グローバルな値が保持される場所です。
ローカルバインディングを確立すると、その変数の以前の値は他の場所に保存されます(または失われる)。わたしたちはこれを、以前の値がシャドー(shadowed)されたと言います。シャドーはグローバル変数とローカル変数の両方で発生し得ます。ローカルバインディングが効果を持つときには、ローカル変数にsetq
を使用することにより、指定した値をローカルバインディングに格納します。ローカルバインディングが効果を持たなくなったとき、以前にシャドーされた値が復元されます(または失われる)。
変数は同時に複数のローカルバインディングを持つことができます(たとえばその変数をバインドするネストされたlet
)。カレントバインディング(current
binding)とは、実際に効果を持つローカルバインディングのことです。カレントバインディングは、その変数の評価によりリターンされる値を決定し、setq
により影響を受けるバインディングです。
ほとんどの用途において、最内(innermost)のローカルバインディングとローカルバインディングをもたないグローバルバインディングを、カレントバインディングと考えることができます。より正確に言うと、スコープルール(scoping rule)と呼ばれるルールは、プログラム内でローカルバインディングが効果を持つ任意の与えられた場所を決定します。Emacs Lispのスコープルールはダイナミックスコープ(dynamic scoping)と呼ばれ、これは単に実行中のプログラム内の与えられた位置でのカレントバインディングを示しており、その変数がまだ存在すれば、その変数にたいしてもっとも最近作成されたバインディングです。ダイナミックスコープについての詳細、およびその代替であるレキシカルスコープ(lexical scoping)と呼ばれるスコープルールについては、変数のバインディングのスコーピングルールを参照してください。
スペシャルフォームlet
とlet*
は、ローカルバインディングを作成するために存在します:
このスペシャルフォームはbindingsにより指定される特定の変数セットにたいするローカルバインディングをセットアップしてから、formsのすべてをテキスト順に評価する。これはforms内の最後のフォームの値をリターンする。let
がセットアップしたローカルバインディングはformsのbody内でのみ効果をもつ。
bindingsの各バインディングは2つの形式のいずれかである。(i)
シンボルなら、そのシンボルはnil
にローカルにバインドされる。(ii) フォーム(symbol
value-form)
のリストなら、symbolはvalue-formを評価した結果へローカルにバインドされる。value-formが省略されたらnil
が使用される。
bindings内のすべてのvalue-formは、シンボルがそれらにバインドされる前に、記述された順番に評価される。以下の例ではz
はy
の新しい値(つまり1)にではなく、古い値(つまり2)にバインドされる。
(setq y 2) ⇒ 2
(let ((y 1) (z y)) (list y z)) ⇒ (1 2)
その一方でbindingsの順序は指定されない。以下の例では1か2のどちらかがプリントされる。
(let ((x 1) (x 2)) (print x))
したがって単一のlet
フォーム内で変数を複数回バインディングするのは避けること。
このスペシャルフォームはlet
と似ているが、次の変数値にたいするローカル値を計算する前に、ローカル値を計算してそれを変数にバインドする。したがてbindings内の式は、このlet*
フォーム内の前のシンボルのバインドを参照できる。以下の例を上記let
の例と比較されたい。
(setq y 2) ⇒ 2
(let* ((y 1)
(z y)) ; y
の値に今計算されたばかりの値を使用する
(list y z))
⇒ (1 1)
このスペシャルフォームはlet*
と同様だが、ローカル値を計算する前にすべての変数をバインドする。値はその後にバインドされた変数にローカルに割り当てられる。これはレキシカルバインディングが効力をもち、let*
の使用しても有効にならないバインディングを参照するクロージャを作成したい場合のみ有用。
たとえば以下は一度実行後にフックから自身を削除するクロージャ:
(letrec ((hookfun (lambda () (message "Run once") (remove-hook 'post-command-hook hookfun)))) (add-hook 'post-command-hook hookfun))
このスペシャルフォームはlet
と似ているが、すべての変数をダイナミックにバインドする。これが役に立つことは稀だろう —
通常ならノーマル変数はレキシカルに、スペシャル変数(defvar
で定義された変数)はダイナミックにバインドしたいと望むだろうし、これは正にlet
が行うことだからだ。
特定の変数がダイナミックにバインド(ダイナミックバインディングを参照)されていることを想定している古いコードとインターフェイスをとる際、それらの変数をdefvar
とするのは非現実的な場合にdlet
は有用かもしれない。dlet
はこれらの変数を一時的にスペシャルとしてバインドしてフォームを実行、それから再び変数を非スペシャルにする。
このスペシャルフォームはScheme言語からインスパイアされたロープ構文である。これはlet
のようにbindings内の変数をバインドしてからbodyを評価する。ただしnamed-let
は正規の引数がbindings内の変数で本体がbodyであるようなローカル関数にたいしてnameのバインドも行う。これによりnameを呼び出してbodyが自身を再帰的に呼び出すことができる。再帰呼び出しにおいては、バインドされる変数の新たな値としてnameに渡された引数が使用される。
数値にリストを加算するループの例:
(named-let sum ((numbers '(1 2 3 4)) (running-sum 0)) (if numbers (sum (cdr numbers) (+ running-sum (car numbers))) running-sum)) ⇒ 10
bodyの末尾位置(tail positions)におけるnameへの再帰呼び出しは、末尾呼び出し(tail calls)としての最適化が保証される。これは再帰の実行深さに関わらず追加のスタック空間を消費しないことを意味する。このような再帰呼び出しでは、変数にたいして新たな値でループ先頭に効果的にジャンプを行う。
一番最後に行うことが関数呼び出しならそれは末尾位置にあり、したがってその呼び出しのリターン値は上記sum
にたいする再帰呼び出しと同じようにbodyの値となる。
以下はローカルバインディングを作成する他の機能のリストです:
変数はバッファーローカルなバインディングを持つこともできます(バッファーローカル変数を参照)。数は多くありませんが、端末ローカル(terminal-local)なバインディングをもつ変数もあります(複数の端末を参照)。この種のバインディングは、通常のローカルバインディングのように機能することもありますが、これらはEmacs内のどこにいるかに依存してローカルになります。
この変数はEmacsが("Variable binding depth exceeds
max-specpdl-size"
というデータとともに)エラーをシグナルするまでに許容される、ローカル変数のバインディングとunwind-protect
クリーンアップ(Cleaning Up from Nonlocal Exitsを参照)の合計数にたいする制限を定義する。
不正に定義された関数における無限再帰をLispが回避するための1つの手段が、この制限およびそれを超過した際に関連付けられたエラーである。入れ子(nest)の深さにたいする別の制限としてmax-lisp-eval-depth
が提供されている。Evalを参照のこと。
デフォルト値は1600。Lispデバッガにエントリーできるように、余裕が残り少なければデバッガ自体の実行を保証するために値は増加される。
Next: 変数がvoidのとき, Previous: 変更不可な変数, Up: 変数 [Contents][Index]