2009年12月21日月曜日

Common LispとEmacs Lispの違い [関数のあたり]

というわけで、Prolog on Emacsの完成を目指して、Common LispとEmacs Lispのスコープ関係の違いを確認してみようと思う。
スコープ自体は、Common Lispがレキシカルとダイナミックの両用で、Emacs Lispはダイナミックのみというだけでさして難しくはないのだが、では、言語の中のいろいろな機構の振舞いはそれによってどういう違いがあるかというと、また別の話である。

まず、関数のあたりから調べてみよう。


* 関数のあたり
** symbolへの格納
- まず、symbolまわりがどうなっているかをなんとな
く。
- Emacs LispはLisp2であり、インターフェイスも
Common Lisp風。

*** Common Lisp の場合
CL-USER> (setq hoge 1)
1
CL-USER> (defun hoge (x) (+ 2 x))
HOGE
CL-USER> hoge
1
CL-USER> (hoge 3)
5
CL-USER> (symbol-value 'hoge)
1
CL-USER> (symbol-function 'hoge)
#<Interpreted Function HOGE>
CL-USER> (compile 'hoge)
HOGE
NIL
NIL
CL-USER> (symbol-function 'hoge)
#<Function HOGE>
CL-USER>

*** Emacs Lisp の場合
(setq hoge 1)
(defun hoge (x)
(+ 2 x))
hoge ; => 1
(hoge 3) ; => 5

(symbol-value 'hoge) ; => 1
(symbol-function 'hoge) ; => (lambda (x) (+ 2 x))
(byte-compile 'hoge)
(symbol-function 'hoge) ; => #[(x) T\207" [x] 1]



** 関数定義

*** 'defun'
- 名前をもつ関数を作成する。
- 名前はグローバルになる。

**** Common Lisp の場合
- マクロ。

CL-USER> (defun defun-at-global () 'global)
DEFUN-AT-GLOBAL
CL-USER> (defun-at-global)
GLOBAL
CL-USER> (let ((hoge))
(defun defun-in-let ()
'let))
DEFUN-IN-LET
CL-USER> (defun-in-let)
LET
CL-USER>

**** Emacs Lisp の場合
- special form。

(defun defun-at-global ()
'global)
(defun-at-global) ; => global
(let ((hoge))
(defun defun-in-let ()
'let))
(defun-in-let) ; => let

*** 'lambda'
**** Common Lisp の場合
- lambda formは、リストである。ただし、'lambda'を
第一要素とするなど、lambda expressionたるリスト
の構造の規約がある。この意味での'lambda'は単なる
シンボルである。
- a function name を求める場所に、lambda formを使
うことができる。(ただし、常に可能というわけでは
ない) このとき、そのlambda formをlambda
expressionと言う。lambda expressionは、関数の振
る舞いを直接表現していると言える。
- lambda formsがよく使われる場所は2つある。ひとつ
目は、通常の関数呼出し、すなわちリストの第一要
素として。例えば次のとおり。

CL-USER> ((lambda (x y) (+ x y)) 1 2)
3
CL-USER>


- ふたつ目は、'function' special operator の引数と
して。例えば次のとおり。

CL-USER> (function (lambda (x) (+ 1 x)))
#<Interpreted Function (unnamed) @ #x1000eec532>
CL-USER>

- このイディオムのためのリーダーマクロが存在する。
それが"#'"であり、後続のS式を(function )でくる
む。

CL-USER> #'(lambda (x) (+ 1 x))
#<Interpreted Function (unnamed) @ #x1000d2bb22>
CL-USER>

- また、略記のためのマクロ'lambda'がある。

CL-USER> (lambda (x) (+ 1 x))
#<Interpreted Function (unnamed) @ #x1000d5a402>
CL-USER> (macroexpand-1 '(lambda (x) (+ 1 x)))
#'(LAMBDA (X) (+ 1 X))
T
CL-USER>


**** Emacs Lisp の場合
- Emacs Lispでは、lambda expressionsは普通の
expression (式)であり、評価すると自分自身になる。
例は次のとおり。

(lambda (x y) (+ x y)) ; = > (lambda (x y) (+ x y))

- Emacs Lispでは、関数は、Cで書かれたものと、Lisp
で書かれたものがある。Lispで書かれたものは、バ
イトコンパイルされていないものと、バイトコンパ
イルされているものがある。バイトコンパイルされ
ていないLisp関数は、全てlambda expressionsで記
述されている。よって、言語仕様としては、Common
Lispと比べて、Emacs Lispの方がlambda
expressionsを関数概念の中心に置いている。多少
Schemeに近い部分がある(ただしクロージャが無い点
がずいぶん違う)。

- lambda expressions はそのままで関数定義なので、
関数が使えるところならどこでも使え
る。

((lambda (x y) (+ x y)) 1 2) ; => 3

- 'function'は、基本的には'quote'にすぎない。

(function (lambda (x y) (+ x y))) ; => (lambda (x y) (+ x y))


*** Closures
- Common Lispではクロージャを使える。Emacs Lisp で
はクロージャは使えない。
- Common Lispでクロージャを生成するには、レキシカ
ル環境を参照しつつ関数を定義する。Common Lispで
は、クロージャをレキシカルクロージャやクロージャ
オブジェクトとも呼ぶ。
- Common Lispでは、クロージャは関数の一種と位置づ
けられている。

*** fletとlablels
- Common Lispでは、fletやlabelsを用いて、局所的な
関数をつくることができる。別の言い方をすると、
関数の名前をスコーピングできる。
- Emacs Lisp自体では、このような機構は提供されて
いない。clパッケージにてマクロで提供されている。



** 'function' special operator/form
- 'function'の振る舞いは'flet'との兼ね合いが重要
である。

*** fletとの兼ね合い
**** Common Lisp の場合
- functionは、レキシカル環境で、名前に紐付けられた
関数オブジェクト(関数定義とも言う)を返すspecial
operator。文脈によって、symbol-functionとは返す
ものが異なる。

CL-USER> (defun hoge (x) (+ 2 x))
HOGE
CL-USER> (flet ((hoge (x) (+ 3 x)))
(funcall (function hoge) 1))
4
CL-USER> (flet ((hoge (x) (+ 3 x)))
(funcall (symbol-function 'hoge) 1))
3
CL-USER>

- 名前に紐付けられた関数定義が存在しない場合、
functionはエラーをあげる。

CL-USER> (function hoge)
#<Interpreted Function HOGE>
CL-USER> (function piyo)
; Evaluation aborted.
CL-USER>

**** Emacs Lisp の場合
- 一方、Emacs Lispでは、fletはマクロである。マクロ
でシミュレートするにあたって、symbolのfunctionセ
ルをつかっている。よって、functionと
symbol-functionの挙動は文脈によらず同じ。

(defun hoge (x) (+ 2 x))
(flet ((hoge (x) (+ 3 x)))
(funcall (function hoge) 1)) ; => 4
(flet ((hoge (x) (+ 3 x)))
(funcall (symbol-function 'hoge) 1)) ; => 4

- functionはspecial form。'function'は、プログラ
マにとっては'quote'と同じ。コンパイラ
は、'function'が使われているときはそれは
Function cellしか使われないことがわかるので、最
適化ができる。

- 'quote'と変わらないだけあって、関数定義が存在し
なくてもエラーにならない。

(defun hoge () nil)
(setq piyo nil)
(functionp hoge) ; => t
(functionp piyo) ; => nil
(function hoge) ; => hoge
(function piyo) ; => piyo



** funcall
*** Common Lisp の場合
- funcallは関数。引数は、'a function
designator'。'function designators'は関数を指し
示すなんらかのLisp Objectsのこと。関数なので、引
数は eager に評価される。アタリマエだが、評価さ
れた結果、function designatorsになるものならなん
でも引数にしてよい。
- function designators
- 関数オブジェクト
- この場合、'funcall'は、関数オブジェクトをそ
のまま使う。
- シンボル
- この場合、'funcall'は、global environmentを
参照して、symbolのfunction cellに格納されて
いる関数定義を利用する。

- 関数オブジェクトの例

CL-USER> (funcall #'car '(1 2 3))
1
CL-USER> (funcall #'(lambda (x y) (+ x y)) 1 2)
3
CL-USER> (funcall (lambda (x y) (+ x y)) 1 2)
3
CL-USER> (setq hoge #'(lambda (x y) (+ x y)))
#
CL-USER> (funcall hoge 1 2)
3
CL-USER>

- シンボルの例

CL-USER> (funcall 'car '(1 2 3))
1
CL-USER>


*** Emacs Lisp の場合
- funcallは関数。引数を eager に評価して、評価結
果によって振舞が違う。
- 評価結果がシンボルの場合。
- そのシンボルのfunction cellにある関数定義を
採用。
- 評価結果が関数の場合
- それを採用。関数がLisp系でもC系でも。
- 例

(setq hoge (lambda () 'fn-for-value-cell))
(defun hoge ()
'fn-for-function-cell)
(symbol-value 'hoge) ; => (lambda () 'fn-for-value-cell)
(symbol-function 'hoge) ; => (lambda () 'fn-for-function-cell)
(funcall hoge) ; => fn-for-value-cell
(funcall 'hoge) ; => fn-for-function-cell

(symbol-function 'list) ; => #<subr list>
(funcall (symbol-function 'list) 1 2) ; => (1 2)


こつこつ。

0 件のコメント: