カテゴリ:一人読書会( 17 )
On Lisp 第17章 リードマクロ
 リードマクロは、普通のマクロよりも Lisp 言語そのものをいじってる感が強いですなぁ。

Lisp のS式の生涯で重要な3つの瞬間
- 読み込み時
ー コンパイル時
- 実行時

リードマクロ => 読み込み時に機能

17.1 マクロ文字
- マクロ文字: Lips のリーダに特別な扱いを要求する文字
(例、"開き括弧")
- set-macro-character, リードマクロの定義
- クォート (') は、Lisp 最古参のリードマクロの一つ
(set-macro-character #\'
#'(lambda (stream char)
(list 'quote (read stream t nil t))))

- read の引数の最後の3つ
1. ファイル終端に達したときにエラーを起こすか
2. 起こさないなら何の値を返すか
3. read を呼び出している間にも read が呼び出されるかどうか
* ほとんどすべてのリードマクロで、第2、第4引数は t であるべき
# 参考: read - refwiki

17.2 マクロ文字のディスパッチング
- シャープ (#): ディスパッチングマクロ文字
- リードマクロを1文字以上で定義するため
ー make-dispatch-macro-character で独自のディスパッチングマクロ文字を定義可能
- # がデフォルトで定義されている
ー set-dispatch-macro-character, ディスパッチングマクロ文字の新たな組み合わせの定義

17.3 デリミタ
- read-delimited-list, 指定した文字が現れるまで Lisp オブジェクトを読み込み
結果のリストを返す
# 参考: read-delimited-list - refwiki


On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-12-16 23:55 | 一人読書会
On Lisp 第16章 マクロを定義するマクロ
 「関数を返すマクロ」があるのであれば、「マクロを定義するマクロ」を考えるのは自然な発想ですな。

第16章  マクロを定義するマクロ
- 省略名を定義するマクロ
- アクセス用マクロを定義するマクロ
- アナフォリックマクロを定義するマクロ

16.1 省略
- よく使われるオペレータの名前は短くあるべき
- バッククォートのついた任意の式を見ただけで
展開結果を言えるようになるとは思うべきではない
- マクロでは論理的に不要なかっこは省くのが良い

16.2 属性
- オブジェクト o が属性 p を持ち、その値が v である
=> o の属性リストを利用 (* 便利だが効率の悪い表現)
(setf (get o p) v)


16.3 アナフォリックマクロ
- aif, awhen, awhile, acond, alambda, ablock, aand
- a+, alist
=> アナフォリックマクロの定義は重複したコードになる
=> アナフォリックマクロを自動定義するマクロを作成できる
=> defanaph


On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-12-12 23:55 | 一人読書会
On Lisp 第15章 関数を返すマクロ
そろそろ紹介されるマクロが大きく複雑になってきて、ついていくのが少し大変になってきた。

15.2 Cdr部での再帰
- シンボルマクロが役に立つ例
- シンボルマクロを使用した場合
(defmacro alrec (rec &optional base)
"cltl2 version"
(let ((gfn (gensym)))
`(lrec #'(lambda (it ,gfn)
(symbol-macrolet ((rec (funcall ,gfn)))
,rec))
,base)))
(alrec (and (oddp it) rec) t)

- シンボルマクロを使用しない場合
(defmacro alrec (rec &optional base)
"cltl1 version"
(let ((gfn (gensym)))
`(lrec #'(lambda (it ,gfn)
(labels ((rec () (funcall ,gfn)))
,rec))
,base)))
(alrec (and (oddp it) (rec)) t) ;; シンボルマクロを使う場合に比べて、
;; rec を呼び出すための括弧が余計


15.3 部分ツリーでの再帰
- 関数生成関数を書いて、それにマクロのきれいなインタフェースをかぶせる



On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-12-09 23:55 | 一人読書会
On Lisp 第14章 アナフォリックマクロ
 アナフォリックって何かと思ったら、要は Perl の $_ に相当するものだとのこと。考え方自体は割と単純。試しに、aif を Ruby でそれっぽく実装してみると、こんな感じか。
def aif(test, &then_form)
@it = test
if @it
then_form.call
end
end

aif 'a' do
puts @it #=> a
end

ただ、else は簡単には再現できそうにない。この辺りでマクロの強力さを思い知る。

  - アナフォラ => 「それ」、「it」、Perl の $_
- 意図的にシンボル it を補足する
- 意図的に変数補足を行うマクロのエクスポート
=> 補足されるシンボルのエクスポートも必要
* パッケージ foo 内の (捕捉される) it は、フルネームでは foo:it

14.1 アナフォリックな変種オペレータ
- アナフォリックな if, when, while, and, cond, lambda, block の例

14.2 失敗
- 真、偽、失敗の判別
=> 多値を利用する
* gethash は、第一返り値がデータ、第二返り値が成功 or 失敗の表現
3通りの場合を判別するイディオム (マクロによる隠蔽が有効)
(defun edible? (x)
(multiple-value-bind (val found?) (gethash x edible)
(if found?
(if val 'yes 'no)
'maybe)))


# 関数をクロージャとして使うテクニック (イディオム?)
# 図14.4 で gensym の複数回評価を避けるために使用
(let ((x 0))
(defun inc ()
(setq x (1+ x))))
(inc) ;=> 1
(inc) ;=> 2
(inc) ;=> 3


14.3 参照の透明性
- Gelernter と Jagannathan の (プログラミング言語に対する) 定義
1. どの部分式も、値の等しい別の式に置き換えることができる
2. 同一コンテキスト内では、同じ式はどこで何回使われても同じ値を返す



On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-12-02 23:55 | 一人読書会
On Lisp 第13章 コンパイル時の計算処理
 コンパイル時にできる事をできるだけやってしまうことで、高速化をはかることができる。
 ベジエ曲線の例はちょっとついていけなかった。ついていく必要も無い気がするけどw
 もう少し実用的な例は、後の章で出てくるらしい。
 
13.1 新しいユーティリティ
- 引数の一部がコンパイル時に分かっているときは、
マクロを使って効率の良いコードを生成できる。



On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-12-01 23:55 | 一人読書会
On Lisp 第12章 汎変数
 setf に絡むマクロの章。
 何となく基本テクニックっぽい感じがするのは気のせいか。

12.1 汎変数という概念
- インバージョン: 値を求める式からアサーションへの変換
- アサーション: 成り立つべき条件
- Common Lisp で頻繁に使われるアクセス関数 (car, cdr, nth, aref, etc...) には、
インバージョンに当たるものがあらかじめ定義されている
- 汎変数: setf の第一引数として機能する式

12.2 複数回の評価に関わる問題
- define-modify-macro
- arg1, 定義したいマクロの名前
- arg2, 汎変数以外に取る付加的な引数
- arg3, 汎変数の新しい値を返す関数 (名)

12.3 さらに複雑なユーティリティ
- get-setf-method, 汎変数の値をいじくるための情報を返す関数
# 注、ANSI Common Lisp の標準は、get-setf-expansion
# 参考: 代入を簡略化するマクロ
- (get-setf-method '(aref a (incf i))
;;=> (#:G3430 #:G3431) ; 一時変数のリスト
;; (A (INCF I)) ; 一時変数にそれぞれ代入すべき値のリスト
;; (#:G3432) ; 変更後の値を入れるための一時変数
;; (SYSTEM::STORE #:G3430 #:G3431 #:G3432) ; この束縛のもとで行うべき代入操作
;; (AREF #:G3430 #:G3431) ; 変更前の汎変数の値を返す式

12.5 インバージョンを定義する
- defsetf, インバージョン定義マクロ
defmacro と違い、引数のために自動的に gensym を生成する
- defun でも、setf に対するインバージョンを直接定義できる (CLTL2 準拠の場合)
- 非対称なインバージョンも定義可能
- アクセス用の式とそのインバージョンは、同じデータに対して働く必要は無い



On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-11-25 23:55 | 一人読書会
On Lisp 第11章 古典的なマクロ
 だんだんマクロの規模が大きくなってきた。がんばってついていこう。

 ちなみに、最近、仕事でコードを書いていて、「あ、これマクロでやれそう (やりたい)」と考える事が多くなってきた。徐々に洗脳が進んでいるらしい。

よく使われる種類のマクロ
- コンテキストを作るマクロ
- 条件付き評価のためのマクロ
- 反復評価のためのマクロ

11.1 コンテキストの生成
- コンテキストの種類
1. レキシカル環境
2. 世界の状態
- スペシャル変数の値
- データ構造の内容
- Lisp外部の状態
- etc...

11.2 with-系マクロ
- コンテキスト生成マクロの名前は with- で始めることが多い
- unwind-protect
- 複雑なマクロは、関数とマクロの組み合わせで書く方が実用的
# dynamic-extent宣言ってなに??
# あとで読む: Common Lisp のスコープとエクステント (PDF)

11.3 条件付き評価
- 定石のうち、明確なものと効率的なものとの選択に直面したときに、
前者を後者に変換するマクロを書く事で、切り抜けられる

11.4 反復
- 本体となる指揮を執って、それを反復評価するオペレータ
=> マクロでのみ表現可能

11.5 複数の値にわたる反復
# ちょっとでかいマクロが多くて、あまりついていけなかった・・・
# 要復習

11.6 マクロの必要性
- 引数をクロージャで括って、評価から保護する方法があるよ
* でも、マクロで書いた方がすっきりする場合多数



On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-11-20 19:27 | 一人読書会
On Lisp 第10章 マクロのその他の落とし穴
 評価の順番が問題になる例がヤバい。こんなのに遭遇したら、かなりの時間を取られそう。
 副作用と再帰に関しては、マクロ展開の結果がイメージできていれば、避けるのはそんなに難しくない気がする。

10.2 評価の順番
- 評価の順番が問題になる例
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,gstop ,stop)
(,var ,start (1+ ,var)))
((> ,var ,gstop))
,@body)))

(let ((x 1))
(for (i x (setq x 13))
(printc i)))
;=> 13
; NIL

stop "(setq x 13)" の副作用で、ループの初期値が 13 になってしまう。
# うーん、このハマリ方はわかりづらい・・・

10.3 マクロ展開関数の副作用
- 展開を行うコードは引数として渡された式にのみ依存すべきで、
値を返す他には周囲の世界に影響しようとすべきではない。
# この原則が重要そう。
# これさえ守っておけば、上の例のような問題は起きないはず。

10.4 再帰
- 末尾再帰関数は、反復形に変換してマクロのひな形に使う事が容易
; 末尾再帰関数
(defun ntha (n lst)
(if (= n 0)
(car lst)
(nsta (- n 1) (cdr lst))))

; 反復形に変換したマクロ
(defmacro nthc (n lst)
`(do ((n2 ,n (1- n2))
(lst2 ,lst (cdr lst2)))
((= n2 0) (car lst2))))


- マクロの引数そのものについての再帰は可能 (引数を無限に重ねる事はできない)
- マクロ引数の値に対する再帰は不可能 (無限ループ!)


On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-11-17 23:55 | 一人読書会
On Lisp 第9章 変数捕捉
 慣れるまでは、マクロを使う際に変数名の衝突ではまりそうで怖い。とりあえず、gensym 使っておけば大丈夫っぽく思えるけど、どうなんだろ。
 それから、Common Lisp の defmacro と Scheme の「健全なマクロ」の違いについてよくわかってないので、後で勉強する。

9.4 適切な名前によって捕捉を避ける
- Common Lisp のグローバル変数には、
先頭と末尾にアスタリスクがつく名前を付けるのが伝統
(例) *package*

9.5 事前評価によって捕捉を避ける
- 危険のある引数を、マクロ展開で作られる束縛よりも外で評価する。
=> 例えば、マクロを let で始める

9.6 Gensym によって捕捉を避ける
- 捕捉されうるシンボルを gensym で置き換える
<= マクロの変数捕捉を避ける確実な方法

9.7 パッケージによって捕捉を避ける
# パッケージについて:
# - defpackgage でパッケージを定義
# - in-package でカレントパッケージを指定、
#
# 参考: パッケージを作りたい
# シンボルとパッケージ

9.8 その他の名前空間に置ける捕捉
- do は暗黙のうちに、nil という名前のブロックに囲まれる
(block nil
(list 'a
(do ((x 1 (1+ x)))
(nil)
(if (> x 5)
(return-from nil x)
(princ x)))))
;=> (A 6)
* 外側で明示的に指定されている nil ブロックから抜けているつもりが、
暗黙に作られた do のすぐ外の nil ブロックに捕まってしまい、
結果として評価値が (A 6) になってしまっている。

(block foo
(list 'a
(do ((x 1 (1+ x)))
(nil)
(if (> x 5)
(return-from foo x)
(princ x)))))
;=> 6
* 外側のブロックを nil 以外の名前にすれば、無事に抜けられる

(block nil
(list 'a
(do ((x 1 (1+ x)))
(nil)
(if (> x 5)
(return x)
(princ x)))))
;=> (A 6)
* return は、暗黙的に作られた nil ブロックから抜ける


On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-11-15 23:55 | 一人読書会
On Lisp 第8章 いつマクロを使うべきか
マクロを使う際の注意点について。
この辺は、実際に使って経験を積まないと分からない (実感できない) 部分も多いだろうなぁ。

基本的には関数を使うべき。マクロが必要なときのみ、それを使う。

8.1 他の手段では不可能なとき
- 1. 変形, 引数等の変形が必要な場合
2. 変数束縛, レキシカルな束縛を変更する場合
3. 条件分岐による評価, 引数を特定条件下でのみ評価する場合
4. 複数回の評価, 引数を繰り返し評価する場合
5. 呼び出し側環境を利用する, 呼び出し側のコンテキストの変数等を利用する場合
* ただし、あまり望ましい使用方法ではない
6. 新しい環境を包み込む
7. 関数呼び出しを節約する
* コンパイル後のコードではマクロ呼び出しに関わるオーバーヘッドがない

8.2 マクロと関数どちらが良い?
- マクロの長所
1. コンパイル時の計算
2. Lisp との密な統合
3. 関数呼び出しの節約

- マクロの短所
1. 関数はデータだが、マクロはコンパイラへの指示に近い
2. ソースコードの明確さ (が失われる)
3. 実行時の明確さ (が失われる)
4. 再帰 (が関数のときほど簡単ではない)


On Lisp
Paul Graham

4274066371
オーム社 2007-03
売り上げランキング : 82240
おすすめ平均 star

Amazonで詳しく見る
by G-Tools
[PR]
by fkmn | 2009-11-12 23:55 | 一人読書会


とあるWebアプリケーションエンジニアの日記
S M T W T F S
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
カテゴリ
以前の記事
ブログパーツ
リンク
検索
タグ
最新のトラックバック
プログラミングが「出来る..
from とりあえず9JP?
Genographic ..
from ナンジャモンジャ
ジュセリーノ
from ありの出来事
くちこみブログ集(ライフ..
from くちこみブログ集(ライフ)(..
以降、丁寧語で行こう!
from エッセイ的な何か
その他のジャンル
ファン
記事ランキング
ブログジャンル
画像一覧

fkmnの最近読んだ本 フィードメーター - フッ君の日常 あわせて読みたい AX