#:g1

Lisp本積読解消: Common Lisp Programming for Artificial Intelligence 6.2 Macros

Posted 2017-02-20 21:03:26 GMT

今回は、Common Lisp Programming for Artificial Intelligenceのマクロの解説の箇所を読む。
1989年出版の本で、2010年にAmazonで¥550位で購入

内容は年代と題名から察せられるように、GOFAIな本で、Common Lispを解説しつつも本題はAIという本。

6.2 Macros

マクロの説明としては、それ程詳しくはないが、評価機がどのようにマクロを扱うか、という視点で説明しているのは、面白いと思った。

評価機の視点で、通常の関数とマクロを比較し、なんとなく使い方を理解させるという戦略だろうか。

それが済むと、早速バッククォートを利用した簡単なマクロの作成方法を解説し終了。

基本的にコード生成の説明に近いと思った。

マクロ書法、作成上の注意点、macroletについての説明などは無し。

気になった所

マクロのユースケースとして、高速化が挙げられているが、やはりこの時代はこういうのが多かったのだろうか。この辺りに古さを感じる。

また、練習問題で作成させるマクロが、関数として実装した方が適切な物が多く、マクロである必然性に乏しいため、あまり適切な問題ではないなあという感想。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: LISPマシン・プログラミング技法 3.6 マクロ

Posted 2017-02-19 18:01:00 GMT

今回は、LISPマシン・プログラミング技法のマクロ解説を読む。

2014年にAmazonで100円で購入。
この本は、Symbolics Lispマシンの入門書だが、Lispプログラミングについても、そこそこの分量を割いている。

3.6 マクロ

簡単にソースコードの変換の仕組みであることが説明され、マクロの利用が有効な手段となる事例として、

  1. プログラムが長い
  2. 入力がめんどくさい
  3. 間違えやすい
  4. いろんなところで用いられる

位を挙げている。

ざっくり言えば複雑さを制御する手段として説明したいらしい。

詳細については、Symbolics Common Lisp: Language Concept “Macros” を参照せよとあり、後は簡単にマクロ展開のツールの説明がされるのみ

マクロ展開のツールの説明

Lisp Listener(REPL)では、Show Expanded Lisp Codeコマンド、関数としては、(scl:mexp)が紹介されている。

Show Expanded Lisp Codeというのは非常に長いコマンドだが、実際には、s e l位を打ち込めば補完してくれるので、呼び出し自体はそれ程面倒でもない。
とはいえ、Listenerに直接入力することはあまりないと思う。

(scl:mexp)の方は、実行するとCommon Lispのinspectのような感じで入力を待つので、そこにフォームを入力する。

一番手軽なのは、やはりZmacsのコマンドで、Macro Expand Expression (c-sh-M)だろう。
SLIMEにも同様のコマンドがあるが、Lispマシンから受け継いできたコマンドともいえる。
マクロを再帰的に展開するには、Macro Expand Expression All (m-sh-M)を使う。

なお、GNU Emacsだとターミナル経由で利用されることが多かったためか、Shiftのあるなしは検出しないことが多いが、Symbolicsでは、Shiftも良く使い、マニュアルではshで表し、単体の文字は大文字で書かれる。

結び

良い機会なので、後でSymbolics Common Lisp: Language Conceptのマクロの箇所も読んでみようかなと思う。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Common LISPcraft 13. Macros / 14. Macro Characters

Posted 2017-02-18 19:38:37 GMT

今回は、Common LISPcraftのマクロの箇所を読む。
2009年位に600円位で購入。
出版は、1984年(第二版1986年)で、時代的にCommon Lispの仕様は、CLtL1。
なお、邦訳はされていない。

Common LISPcraftの前に、LISPcraftというFranz Lispを対象にした本が、同じくWilensky氏によって書かれていて、それのCommon Lisp版ということで、Common LISPcraftという題名になっている。
しかし、題名は引き継いでいるものの殆ど新規に書下したように見える。

Chap. 13 Macros

アクセサの別名を作る所から始まって、マクロ展開の解説、setf系マクロの定義の方法など、割合に網羅的に解説されている。
解説は丁寧だが、古い方言や書法の話が入ったりしているので、ちょっと古さを感じてしまう。
バッククォートや、macroletの解説はなし。

Chap. 14 Macro Characters

リーダーマクロの解説。通常のマクロ文字の解説の後、バッククォートの説明があり、次にディスパッチ・マクロ文字の解説がされる。
解説は、かなり丁寧で、これ程丁寧にリーダーマクロの基本を解説している本は無かったような気がする。

バッククォートの解説の後、13章では導入しなかったバッククォートを利用してマクロを書くことが解説される。 この辺りの流れは、どうやら前回眺めたArtificial Intelligence Programmingに影響を受けているようで、解説している内容も割と似ている(がこちらの方が簡素)。

ちなみに、13章のマクロのネタにも、Artificial Intelligence Programmingのネタが使われている。

結び

この本は基本的なスタンスとして機能は網羅的に解説するように思えた。

網羅的に解説しつつも取って付けたような感じはしないので、解説が上手いのかなあと思ったりするが、例題にCommon Lispより前の古えのLisp方言の機能を真似るようなものが多く、ちょっと古臭いのが残念。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Artificial Intelligence Programming 2nd ed. | 2. Macros and Read-Macros

Posted 2017-02-14 17:24:27 GMT

今回は、Artificial Intelligence Programming 2nd ed.を読む。 2013年にAmazonで565円位で購入。
第一版の方は、人工知能プログラミング / 日本コンピュータ協会として翻訳もされているらしいが、この本は何故かAmazonでは取り扱いがない。
第一版の方は出版が1981年ということもあるので、LispはCommon Lisp以前の方言だと思われる。

第二版は、1987年の書籍。
人工知能プログラミングの本だが、半分位はCommon Lispの解説があり、PAIPと似たような構成。というかLispの人工知能本は、第一部Common Lisp、第二部人工知能という構成が殆どで、この本もそのようになっている。

著者は、Eugene Charniak、Christpher K. Riesebeck、Drew V. McDermott、James R. Meehan各氏でLisp界隈では有名な方々。

制御構文の詳しい解説もされていない2章の時点でマクロとリーダーマクロの解説を始めるというのは、なかなか珍しいと思った。
しかも、リーダーマクロの解説が先。リーダーマクロの解説の題材もバッククォートを展開する関数を定義して、動きを観察したり、動作をいじってみたりする。
set-macro-characterの使い方なども解説される。

リーダーマクロの次に通常のマクロの解説になるが、バッククォートの詳細は理解しているので、そちらの解説はなし。
マクロ学習の題材としては、letの作成が取り上げられる。

次に、repeatというdotimesのようなマクロをお題にして、マクロ変数の捕捉問題と、多重評価問題を解説する。
この解説の中で、変数捕捉回避の方法として、gensymを使った方法とは別に、thunkを使った方法が解説される。

(defmacro repeat (n &body body)
  `(let ((thunk (lambda () ,@body))
         (cnt ,n))
     (loop 
      (when (>= 0 cnt) (return))
      (funcall thunk)
      (decf cnt))))

更に、このrepeatの場合は、下請け関数にthunkを渡すように展開してしまうことも可能であることが示される。

(defmacro repeat (n &body body)
  (repeat* ,n (lambda () ,@body)))

この辺りは類似の本には見られない解説で面白い。
一応、クロージャーで変数捕捉を回避する方は、関数呼び出しが発生するのでベタに展開するマクロより効率は落ちるという説明もされている。

下請け関数に全部任せてしまうのは、最近では、call-withスタイルとも呼ばれ、Google Common Lispスタイルガイドでも紹介されているものだが、結構昔からあるんだなあと感心

なお、thunkもインライン化されたりコンパイラが優秀ならば、ベタなマクロと同じ効率になることもあるので、一概には効率が落ちるともいえないと思う。

といっても、まあ効率重視の局面ではベタに書くのが良いのだろう(二転三転)

そして、let特殊変数スペシャル変数専用の結合束縛構文版を作成する。これは後の章で更に込み入ったものとなる(最終的にはletfのようなものになる)

気になった所

題材は面白いんだけれども、練習問題を解くのは結構難しい気がした。
機転が利く人は前提知識がなくても解けるのかもしれないが、解けなくても、自分なりに考えた上で解答を見て動作を確認すれば、へえと感心するような類の問題が多い気がする。 (なお、回答は巻末に纏まっている)


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: 実用Common Lisp | 3.2章 特殊形式: マクロ etc

Posted 2017-02-13 17:01:50 GMT

今回は、実用Common Lisp、通称PAIP。名著として名高い本のマクロの解説を読んでみる。

3.2章 特殊形式: マクロ

この本では、まず、特殊形式の仲間としてマクロを解説し、後に正確な定義に近付けているらしく、この章でマクロと特殊形式の違いが解説される。

マクロを書く順序として、

  1. 本当に必要かどうか
  2. 構文を書く(書き出す・考える)
  3. 展開形を考える
  4. 実際にdefmacroで記述する

としていて、本当に必要かどうかまず良く考え、書くとしたら既存の文法に合せるように書くべきであることが説明されている。

実際にマクロを書く例として while を作成するが、最初は、バッククォートを利用しないもので書き、次の段落(逆引用符表記法)でバッククォートを利用したテンプレート作成的な書き方を説明。

他、

  • 24.6 シーケンス関数: Once-only: マクロ論のレッスン・マクロ乱用の回避
  • 25.14 マクロに関する問題

でもマクロについての解説あり。

全体的に機能の解説をするついでにコーディングスタイルの話もすることが多いため、余計な解説と情報が多いなという印象。
特殊形式の解説の筈なのに、シーケンス関数の話も混ざっていたり、後の章の話が度々出てきたり、Common Lispの基礎を学ぶのにはこの本はあまり向いていないのではないかと思った。
しかも、コーディングスタイルについては、若干押し付けがましい。
押し付けがましい割には、Norvig先生のコード例も自身のコーディングスタイルに沿っていなかったりする。
また、示されているスタイルは昔からのオーソドックスなものというよりは、Novig先生オリジナルな成分が多目なスタイルに思える。

大著なので、最初に完成した部分と、入門者向けの部分等で統一感がないのかもしれない、と少し思った。

スタイルについては話半分で流しておいて、そこそこ書けるようになって、気になってきたら、Norvig先生がKMP氏と一緒に纏めたスタイル論があるので、別途そちらを参照した方が良いのではないかと思う。

気になった所

局所ローカルマクロ・関数によるシャドウについて

⸨25.14 マクロに関する問題⸩では、3.2章の内容をさらに細かく考察していて、局所ローカルマクロ・関数が展開された関数・マクロをシャドウしてしまう問題についても触れている。
具体的には、下記のような状況だが、

(defun last1 (list)
  (car (last list)))

(defmacro foo (list) `(last1 ,list))

(flet ((last1 (x) x)) (foo '(1 2 3 4)))(1 2 3 4)

これを防ぐには、

(defmacro foo (list)
  `(funcall ,#'last1 ,list))

(flet ((last1 (x) (print x))) (foo '(1 2 3 4))) → 4

と書く必要がある、と解説されている。
実際にはこのように書かれることは殆ど無いが、Common Lispでは、局所ローカルマクロ・関数が多用されないので、スタイル上あまり問題にならないとの説明。

,#'last1というのがそら恐しいが、大域定義を参照するのだから、単に'last1とシンボルで書いてしまえば良いじゃん、と思ってしまった。何か理由があるのだろうか。

  1. #'last1
  2. 'last1
  3. ,#'last1

と書けるが、#'last1と書いてしまっては元の木阿弥。
本書では関数は、#'を付けて書くように統一されているので、'last1ではなく、,#'last1とした、という所だろうか。
もしくは、インライン展開やコンパイラ・マクロへの配慮とか?

局所ローカルマクロ・関数のシャドウ問題への対処は実際には、上記のようにfuncallに分割するようなことはしないで、パッケージ・ロックで防ぐのが一般的だろう。

パッケージ・ロックは、規格外の機能だが大抵の処理系は装備しているので、これを使えばシャドウされたらエラーとすることができる。

たとえば、SBCLであれば、(sb-ext:lock-package "パッケージ")とするだけで良い(もしくはdefpackage:lock Tを指定)

(とはいえLispWorksのように大域的な再定義しかエラーにしてくれない処理系もある……)

記述が古いところがあるので注意

ANSI Common Lispが成立する前に出版された本故に記述が古い所があるdefine-setf-methodspecial-form-p等々)

macroletの解説がない

これだけの大著なのにmacroletの解説がない。
まあ人工知能がメインの本なのでしょうがないのかもしれない。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Common Lisp 入門 | 7章 マクロ

Posted 2017-02-12 15:01:35 GMT

今回は、Common Lisp 入門 (湯淺太一/萩谷昌己著)のマクロの章を読む。

マクロ展開関数は一般のリスト処理関数として定義できる.
すなわち,マクロ展開とはリスト処理の一種である

という明確な解説から始まって、マクロと特殊形式スペシャル・フォームの違い等を説明。実装者視点なのか細かい所の解説が多い。

組み込みマクロの解説としてsetfの解説があるが、普通のマクロよりこちらの方に力が入っている印象。
一般化変数generalized variableの説明も詳しい。

マクロの定義については、まずはバッククォートを使わないで説明が進み、局所ローカルマクロまで解説した後に、バッククォートを使った書き方を解説。
macroletの定義部内でレキシカル変数・関数が参照できないことが解説されているが、このことを解説している本は少ないと思う。

コードにすると、

(let ((x 42))
  (macrolet ((foo () x))
    (foo)))
⊳ Error: The variable x is unbound.

のようなもの。

ANSによると、

The macro-expansion functions defined by macrolet are defined in the
lexical environment in which the macrolet form appears. Declarations
and macrolet and symbol-macrolet definitions affect the local macro
definitions in a macrolet, but the consequences are undefined if the
local macro definitions reference any local variable or function
bindings that are visible in that lexical environment.

とのことで未定義動作になる。

実際試してみるとAllegro CLのインタプリタ動作では動いたりするがコンパイルするとエラー、他の処理系も軒並エラーとなる。

ふと、&environmentを使って中でマクロ展開したら良いのではないかと思い試してみた所、この方法ならどの処理系でもレキシカル変数・関数が参照できた。
しかしこれは合法なのだろうか。シンボルマクロの展開に辺りに解説が書いてありそうだが合法かどうかの記述をみつけることができなかった(乞う詳細)

(defun kou (arg)
  (let ((x arg))
    (macrolet ((x (&environment env)
                 (macroexpand 'x env)))
      (x))))

(kou 42) → 42

(defun ots (arg) (let ((x arg)) (macrolet ((x (&environment env) (symbol-macrolet ((x (macroexpand 'x env))) `(list ,x ,x ,x)))) (x))))

(ots 42)(42 42 42)

(defun hei (arg) (let ((x arg)) (flet ((hei () x)) (macrolet ((x (&environment env) (macroexpand '(hei) env))) (x)))))

(hei 42) → 42

(defun tei (arg) (let ((x arg)) (flet ((tei () x)) (macrolet ((x (&environment env) (macroexpand '(funcall #'tei) env))) (x)))))

(tei 42) → 42

注意点

CLtL1時代の本なので、special-operator-pspecial-form-pだったりする。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Common Lisp & Artificial Intelligence | Chap. 5 Macros

Posted 2017-02-11 18:25:51 GMT

Lisp本を沢山積読してしまっている。折角なのでこの状況のメリットを考えてみたが、一つのトピックについて色々な本を串刺しに読むというのは積読ならではではないかなと思ったので、とりあえずマクロについて串刺しに読んでみようと思う。

この本について

Common Lisp & Artificial Intelligence / Patrick R. Harrison著は、2009年にAmazonで1円で購入。
Common Lispと人工知能というタイトルの本は沢山あるが、その中の一冊という感じ。
240頁程の内容のうち、Common Lispについては150頁程割いている。

Chap. 5 Macros

この本では、マクロの利用形態を5つに大別して解説しており、

  1. ばらつきと詳細を隠蔽し抽象化するため
  2. 文法を単純化するため
  3. 大域的な副作用フォームを作成するため
  4. 引数をクォートするため
  5. 効率的にコンパイルするため

の五つを軸に据え、それぞれに具体的な例を挙げて解説が進む。

解説する例題で、MACLISPで良く使われていたユーティリティをCommon Lispで再現する例が多いので、著者はMACLISPに親しんだ人なのかもしれない。

上記のうち、大域的な副作用フォームを作成する、というのは大域的というより、定義するフォームの外側に影響を及ぼす、という意味らしい。

マクロの解説の後、リーダーマクロの解説となるが、ここでもMACLISPのリード時マクロ展開#%を再現するような例を用いて解説している。

章の最後には問題が九つ程あるので解いてみたが、中々面白い問題かなと思った。

気になった所

マクロを作る際に問題になる変数のキャプチャ問題をFUNARG問題として解説しているところがちょっと気になった。
確かにFUNARGの変種と考えられるようなそうでもないような。
とはいえ、マクロは関数引数になるわけでもないしFUNARGとは無縁のような。

また、コード例の中のassertの構文が間違っている。


HTML generated by 3bmd in LispWorks 7.0.0

Lispのぶら下がり括弧嫌ですね

Posted 2017-02-11 15:30:17 GMT

Lispのぶら下がり括弧嫌ですね。
コッカをぶら下げてしまう人は、何故ぶら下げてしまうのか。

恐らく、括弧の対応が取れないので、行で対応を取っているのでしょう。
Lisp用のエディタを使っていないからだと思いますが、そこで行で対応を担保しつつ括弧も揃える方法を考案しました。

⏜
defun fib (n)
⏜
if (< n 2)
n
(+ (fib (1- n))
   (fib (- n 2)))
⏝
⏝

どうでしょうこれ。

ふざけた感じではありますが、eval-whenだと案外実用できるかも。

⏜
eval-when (:compile-toplevel :load-toplevel :execute)

(defun foo (x) x)

(defun bar (x) x)

(defun baz (x) x)

設定方法(Common Lisp)

(set-macro-character #\⏜
                     (lambda (srm chr)
                       (declare (ignore chr))
                       (read-delimited-list #\⏝
                                            srm
                                            T)))

(set-syntax-from-char #\⏝ #\))


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: LISP 原書第3版(I) | 12章 マクロ

Posted 2017-02-08 18:11:33 GMT

Lisp本積読解消今回はLISP 原書第3版(I)
12章 マクロ を読むことにする

最初にマクロの引数の評価について説明があり、その後にバッククォートを使い、テンプレートを埋める方式で式を組み立てる方法を解説する。

それ程込み入った解説はないが、問題が実践的で面白いのでこの章の問題が解ければ実際に作成するマクロの九割以上はすんなり書けることと思う。

また、13章の構造体の解説にもあったが、マクロもまとめて別ファイルに分けようとのこと。
理由としては、マクロは先に読み込ませたいことが殆どだからとのことだが、マクロ展開に利用する補助関数をどう扱うかについては言及がない。

Lisp Style and Designにはファイル分けについて、評価順でファイル分けをし過ぎると、意味単位のモジュール分割が阻害され、意図が良く分からないものになりがちという指摘があるが、何事も程々が良いだろう。


HTML generated by 3bmd in LispWorks 7.0.0

Lispは関数形言語

Posted 2017-02-07 06:18:52 GMT

JIS X 0007:2001の07.01.20によると、Lispは関数形言語。
「形」に注目。

07.01.20 関数形言語(functional language)

関数呼出しだけを使用して計算機システムを稼働させる
ことによって,達成されるものを記述する手段を提供す
るプログラム言語。

例 FORTH, LISP, ML, Miranda, PostScript

JIS X 0007:2001 の元になったのは、ISO/IEC 2382-7:2000らしいが、ISO/IECでも同様。

functional language:
    A programming language that provides the means to state what is to be achieved by the actions of a data processing system exclusively through the use of function calls. For example, FORTH, LISP, ML, Miranda, Postscript. Synonymous with functional programming language. Contrast with imperative language.

ちなみに、Lisp関係でで良く使われている用語の“binding”は、“束縛”ではなく“結合”らしい。 結合なら、「変数が値に結合される」でも、「変数を値に結合する」でもどっちでも良さそう。


HTML generated by 3bmd in LispWorks 7.0.0

Older entries (2081 remaining)