Subsections

書式と評価

アトム(atom)

cons以外のデータオブジェクトは、たとえ複雑な構造をしていたとしても、 すべてatomである。 空リストとして()でしばしば書かれるNILもatomである。 すべてのatomは、symbolを除いていつもそれ自身評価されている。 しかしながら、他のCommon Lispの実行のなかでは、atomの評価に引用符を要求されることがある。

スコープ

すべてのsymbolは、値と結び付いている。 symbolは、主にくくられた文脈から決定される値によって評価される。 ここに2種類の変数バインドがある。それは、 ローカルまたは静的バインドとスペシャルまたは動的バインドである。 ローカルにバインドされた変数はlambda書式または letlet*の特殊書式においてspecialと宣言されない限り 外から見ることはできない。 ローカルバインドは入れ子が可能で、外側のローカルバインドやスペシャルバインドを隠して、 最も内側のレベルで定義されている1つのバインドのみ見ることができる。 スペシャル変数は2つの方法で使用される。 1つは、グローバル変数として、もう1つは動的に覗けるローカル変数として用いる。 このローカル変数は、バインドの効果の中にある限りローカルスコープの 外にいてさえ見ることができる。 後者の場合、スペシャル変数はspecialで宣言される必要がある。 その宣言は、コンパイラだけでなくインタプリタでも認識される。 Common Lispによると、スペシャル変数は不明瞭なスコープと動的な 広さを持っていると言われている。

あるスコープのなかで、ローカル変数が存在するとしても、 同じ変数名を内部スコープの中でspecialとして再宣言することができる。 symbol-value関数は、ローカルスコープに構わずspecial値を引き出す ために使用することができる。 set関数は、スペシャル変数としてのみ働く。すなわち、 specialとして宣言していない限り、lambdaやlet変数の値を 変更するために使用することはできない。

(let ((x 1))
   (declare (special x))
   (let* ((x (+ x x)) (y x))
      (let* ((y (+ y y)) (z (+ x x)))
         (declare (special x))
         (format t "x=~S y=~s z=~s~%" x y z) ) ) )
--> x=1 y=4 z=2

symbolは、defconstantマクロにより定数として宣言することができる。 一旦宣言すると、その後値を変更しようとするとエラーが発生する。 そのうえ、そのような定数symbolは、ローカル変数としてさえ変数名として 使用されることを禁じられる。 NILやTは、そのような定数の例である。 keywordパッケージのsymbolは、いつも作成されるときに定数として宣言される。 対照的に、defvardefparameterマクロは、スペシャル変数として symbolを宣言する。 defvarは、symbolがバインドされていない時のみ値の初期化を行い、 値が既に割り当てられているときは何もしない。 それに対して、defparameterはいつも値をリセットする。

symbolが参照され、symbolのためのローカルバインドがなかったとき、 そのspecial値は、引き出される。 しかしながら、そのspecial値にまだ値が割り当ててなかったならば、 unbound variableエラーが発生する。

一般化変数

一般的に、どんな値および属性もオブジェクトのスロット(またはスタック) で表現される。 スロットの値を引き出すかまたは変えるときは、 2つの基本的な命令、accessupdateで行わなければならない。 オブジェクトの全てのスロットに対して2つの異なった基本命令を定義する代りに EusLispでは、Common Lispのように、一般化変数コンセプトに基づいた 画一的な更新命令を備えている。 このコンセプトのなかで、共通書式は、値のアクセス書式あるいはスロットの位置指定 として認識される。 したがって、それぞれのスロットに対してアクセスする書式を覚えてさえおけば、更新は そのアクセス書式とsetfマクロを組み合わせることにより 実現できる。 例えば、car値をリストの外に取り出すのと同じ様に(setf (car '(a b)) 'c) としてsetfを使用したとき、(car x)xのcarスロットのなかの 値を置き換えることに使用することができる。

この方法は、ユーザーが定義したオブジェクト全てに対して 適用できる。 クラスや構造体が定義されるとき、それぞれのスロットに対する accessやupdate書式は、自動的に定義される。 それらの書式は、それぞれマクロとして定義されている。その名前は、 クラス名とスロット名の連結となる。 例えば、consのcarは(cons-car '(a b c))で処理することができる。

(defclass person :super object :slots (name age))
(defclass programmer :super person :slots (language machine))
(setq x (instantiate programmer))
(setf (programmer-name x) "MATSUI"
      (person-age x) 30)
(incf (programmer-age x))
(programmer-age x)   --> 31
(setf (programmer-language x) 'EUSLISP
      (programmer-machine x) 'SUN4)

行列要素も同じ手法でアクセスすることができる。

(setq a (make-array '(3 3) :element-type :float))
(setf (aref a 0 0) 1.0 (aref a 1 1) 1.0 (aref a 2 2) 1.0)
a --> #2f((1.0 0.0 0.0) (0.0 1.0 0.0) (0.0 0.0 1.0))

(setq b (instantiate bit-vector 10))  --> #*0000000000
(setf (bit b 5) 1)
b --> #*0000010000

特定のオブジェクトに特別なsetfメソッドを定義するために defsetfマクロを用意している。

(defsetf symbol-value set)
(defsetf get (sym prop) (val) `(putprop ,sym ,val ,prop))

特殊書式


Table 2: EusLispの特殊書式
and flet quote
block function return-from
catch go setq
cond if tagbody
declare labels the
defmacro let throw
defmethod let* unwind-protect
defun progn while
eval-when or

全ての特殊書式は、表2にリストされている。 macrolet, compiler-let,progvは、該当しない。 特殊書式は、文脈の評価および制御フローの管理のための 基本的な言語構造である。 インタプリタとコンパイラは、これらの構造をそれぞれ正しく処理する ために特殊な知識を持っている。それに対して、アプリケーションメソッド は全ての関数に対し画一的である。 ユーザーは、独自の特殊書式定義を追加することはできない。

マクロ

マクロは、言語構造を拡張するために役立つメソッドである。 マクロが呼び出されたとき、引数は評価されずに マクロの本体(マクロ拡張関数)へ受け渡される。 それから、マクロ拡張関数は、引数を拡張し、新しい書式を返す。 結果となった書式は、マクロの外側で再び評価される。 引数のリストにマクロまたは特殊書式を用いるとエラーになる。 macroexpand関数は、マクロ展開のために使用することができる。

インタプリタのときマクロはゆっくりと実行されるが、 コンパイルすることにより実行速度の向上を図ることができる。 なぜなら、マクロ展開はコンパイル時に一度だけ行われ、 実行時にそのオーバーヘッドは残らない。 しかし、マクロ関数の中におけるevalあるいはapplyの呼出は、 インタプリタの実行とコンパイル後の実行との間に違う結果をもたらす。

関数

関数は、単にリストの最初の要素がlambdaであるようなlambda書式によって 表現される。 もしlambda書式がdefunを使ってsymbolを定義するとき、 グローバル関数名として参照することができる。 lambda書式は、次の文法で与えられる。


 (lambda ({var}* 
)}*]

) \includegraphics[width=7.5cm]{fig/threadobj.ps} ((:keyword var) [initform])}*
]
)}*])
{declaration}*
{form}*)

ここにEXPR,LEXPR,FEXPRなどのような型の関数はない。 関数への引数は、いつもその関数を実行する前に評価される。 受ける引数の数は、lambda-listによって決定される。 lambda-listは、lambda書式のためにパラメータの列を記す。

&optional, &rest, &key &aux はそれぞれ、lambda-list のなかに特殊な意味を持っていて、これらのsymbolは、変数名として使用 することはできない。 &optionalや&keyパラメータのsupplied-p変数は、サポートされていない。

lambda書式は、普通のリストデータと区別できないため、 function特殊書式を用いて、インタプリタやコンパイラに関数として 認識するように知らせなければならない。 [*]functionは、関数の上に環境を固定するために重要である。 そのため、すべてのローカル変数はその関数が違ったローカルスコープの他の関数を 通ってきたとしてさえ、アクセスすることができる。 次のプログラムは、letsumがlambda書式の中に見えるため、 インタプリタとコンパイル後のどちらも何もしない。

(let ((x '(1 2 3)) (sum 0))
  (mapc '(lambda (x) (setq sum (+ sum x))) x))

予想した結果が得られるためには、次のように書くべきである。

(let ((x '(1 2 3)) (sum 0))
   (mapc #'(lambda (x) (setq sum (+ sum x))) x ))

#'は、functionの略語である。 すなわち、#'(lambda (x) x)(function (lambda (x) x))と同等である。 ここは、funarg問題と呼ばれる別の例を示す。

(defun mapvector (f v)
    (do ((i 0 (1+ i)))
       ((>= i (length v)))
       (funcall f (aref v i))))
(defun vector-sum (v)
    (let ((i 0))
       (mapvector #'(lambda (x) (setq i (+ i x))) v)
       i))
(vector-sum #(1 2 3 4)) --> 10

EusLispのclosureは、不定な大きさを持つことができない。 すなわち、closureはその外側の大きさで可能な大きさまで持つことができる。 これはclosureが'generators'のプログラミングのために使用されないことを意味する。 次のプログラムは何もしない。

(proclaim '(special gen))
(let ((index 0))
   (setq gen #'(lambda () (setq index (1+ index)))))
(funcall gen)

しかしながら、その同じ目的がオブジェクト指向プログラミングで実現できる。 なぜなら、オブジェクトはそれ自身の固定変数を持つことができるためである。

(defclass generator object (index))
(defmethod generator
 (:next () (setq index (1+ index)))
 (:init (&optional (start 0)) (setq index start) self))
(defvar gen (instance generator :init 0))
(send gen :next)
This document was generated using the LaTeX2HTML translator on Sat Feb 5 14:36:44 JST 2022 from EusLisp version 138fb6ee Merge pull request #482 from k-okada/apply_dfsg_patch