2010年1月25日月曜日

Lisp Quote Backquote (3)

Lispには入れたけど、まだ、仕様書を眺めるレベル。
次回は、ソースに入れるかも。

しかし、このブログは学習ブログなので「読者を念頭におかない!」ことが信条なのですが、だんだん、ゴミを撒き散らしているだけなのでは?という懸念が強くなってきました。WebのS/N比を低くしているだけだなぁ、と。まぁそんなこともぼちぼち考えながら、当面は続けていこうと思います。

こつこつ。


;;;; CLtL2におけるbackquoteの定義

;; Steeleの実装を読み解く前に、CLtL2のbackquoteの定
;; 義をおさらいしておこう。まず、

;; 「バッククォートは、テンプレートを用いることによっ
;; て複雑なデータ構造を構築するプログラムを簡単に書
;; けるようにする。」

;; という言葉が、アタリマエなのだが新鮮。そうか、バッ
;; ククォートはデータ構造のテンプレート記法なのだと。

;; さて前回見たとおり、文字apostropheはマクロ文字としては、
;; (quote foo)の略記にすぎない。'fooは、fooが何かに
;; 関わらず、(quote foo)として読み込まれる。

;; うむ。これはわかる。大丈夫。進もう。

;; 次はバッククォート。まず、バッククォートという言
;; 葉の使い方を確認しよう。通常の会話において、バッ
;; ククォートと言うとき、それは単体のマクロ文字を指
;; していることもあるし、バッククォート構文を指して
;; いることもある。大抵の場合、どちらを指しているか
;; は文脈によってあきらかである。なので、この文章で
;; も文脈を利用して「バッククォート」という単語でこ
;; の2つのものを指していくことも可能である。しかし、
;; ちょっと考えてみると、ここではどちらであるか明示
;; した方が書きやすいし、わかりやすそうだ。そこで、
;; この文章では「バッククォート」というように独立し
;; た単語としては使わずに、「マクロ文字バッククォー
;; ト」か「バッククォート構文」のいずれかを使うこと
;; にする。あとひとつ、先頭文字がバッククォートで始
;; まるLispフォームのことを「バッククォートフォーム」
;; と呼ぶことがある。これも必要に応じて利用する。

;; バッククォート構文の説明に移ろう。

;; バッククォート構文は、リーダマクロとして定義され
;; ている。複雑なデータ構造を簡便に構築するためのテ
;; ンプレート記法を目指したものである。

;; バッククォート構文で使われるマクロ文字は次の通り
;; である。

;; ` , # @ .

;; ここで、# は`の直後にあらわれたときのみバック
;; クォート構文のマクロ文字として機能し、@ と .
;; は、, の直後にあらわれたときのみそうである。

;; さて、一般的に、リーダマクロの定義は、マクロ文字
;; を含む印字表現をリーダが読んだときに、リーダの返
;; り値として、どのようなLisp objectを返すのか、と
;; いう変換として記述される。すなわち、定義がリーダ
;; で閉じているのだ。図示すると次のとおり。

;; <印字表現> -(reader)-> <Lispオブジェクト>

;; 例えば、

;; 'foo -(reader)-> (quote foo)を読み込んだときに返すLispオブジェクトとまったく同一。

;; である。すなわち、'fooをreaderが(quote foo)と読
;; み替えると考えてもまったく支障も懸念も無い。すな
;; わち、readerが返すLispオブジェクトの印字表現は、
;; (quote foo)であると言ってしまってよいのだ。

;; しかしバッククォート構文のマクロ文字については、
;; 少々事情が異なる。

;; バッククォート構文では、readerが返却する<Lisp オ
;; ブジェクト>が何であるかは実装依存としてしまって
;; いる。では、バッククォート構文の意味はどう定義す
;; るかというと、その実装依存な<Lisp オブジェクト>
;; をevaluatorが評価した結果得られる<Lispオブジェク
;; ト>が何かという、もう一段先で記述しているのだ。

;; <印字表現> -(reader)-> <実装依存> -(evaluator)-> <Lispオブジェクト>

;; これは後で再度説明する。

;; 準備は整った。ここからは、バッククォート構文の意
;; 味を形式的に定義しよう。

;; 用語1. form は、断りがない限り、任意のLispフォー
;; ムを表すものとする。

;; 用語2. basic は、form からリストと一般のベクタを
;; 除外したものである。

;; 用語3. 2つのLispフォームがあり、それらを評価した
;; 結果がequalにて等しいことを、評価等価と呼ぶ。こ
;; のとき、評価する回数は複数回も考えられる。A と
;; B が一回の評価において評価等価なとき、A = Bと書
;; く。二回の評価において評価等価なときは、A == B
;; と書く。

;; 1. `basic = 'basic.

;; 2. `,form = form.
;; ただし、ここでの form は、@ または . で始まる
;; Lispフォームを除外している。

;; 3. `,@form は誤りである。

;; 4. `(x1 x2 x3 ... xn . atom) = (append [x1] [x2] [x3] ... [xn] (quote atom))

;; ここで角括弧は次のようなものである。(上から順に判定)
;; - xjが,@で始まるとき、xjを,@formと書けば、[xj] = form。
;; - xjが,で始まるとき、xjを,formと書けば、 [xj] = (list form)。
;; - それ以外のとき、xjをformと書けば、 [xj] = (list `form)。
;; ここで、`formはバッククォートフォームである。
;; このフォームも再帰的に解釈をすすめる。

;; 5. `(x1 x2 x3 ... xn) = `(x1 x2 x3 ... xn . nil)

;; 6. `(x1 x2 x3 ... xn . ,form) = (append [x1] [x2] [x3] ... [xn] form)

;; 7. `(x1 x2 x3 ... xn . ,@form) は誤りである。

;; 8. `#(x1 x2 x3 ... xn) = (apply #'vector `(x1 x2 x3 ... xn))

;; 9. ,@ が使われているところはどこでも、,@ の代わ
;; りに ,. を使うことができる。この両者の違いは、,.
;; の場合は、後続のformが生成するリストを破壊しても
;; よいということである。

;; 10. バッククォート構文が入れ子になっている場合、
;; もっとも内側のマクロ文字バッククォートに属する
;; フォームが最初に展開される。

;; さて、先送りした話に戻ろう。

;; `((,a b) ,c ,@d)

;; を上記ルールに基づいて解釈(展開)すると、

;; (append (list (append (list a) (list 'b) 'nil)) (list ) d 'nil)

;; になる。しかし、バッククォート構文のルールが述べ
;; ているのは、readerがまさにこのLispオブジェクトを
;; 返さなければいけないということではなく、これと評
;; 価等価なLispオブジェクトを返せばよいということだ。
;; それは無数に存在する。例は次のとおり。

;; (append (list (cons a '(b))) (list c d)
;; (list* (cons a '(b)) c d)

;; readerが返すものは、これらのうちどれでもよいのだ。

;; さて、これでCLtL2のバッククォートの定義のおさら
;; いができた。次回は、ついにSteeleの実装をみていこ
;; う。

2010年1月24日日曜日

Lisp Quote Backquote (2)

体調が戻ってきたので、Lispいじりを再開。

と思っていたのですが、Lispにたどりつく前の調べもので終わってしまった。

でも、ずっと気になっていたところなので、スッキリ。


;;;; Quote, Unquote and Back quote

;; ところで、クォートとバッククォートって、印刷物で
;; みると、いつもどっちがどっちだかわからなくなって
;; しまう。Infoファイルとかだと、`hoge'のように、バッ
;; ククォートの方が先にくるんですね。だから、前がバッ
;; ククォートで、後がクォート。これ自体、なんかテレ
;; コに命名した方が自然じゃない?という気がする。

;; さらに、印刷のグリフだと後のグリフは、Writing
;; Directionに対して「バック」しているように見える
;; ので、それがバッククォートと思いたくなってしまう。
;; だけど、実はクォートなんですね。少なくとも大抵の
;; Lispの本ではそうです。

;; この違和感がどこから来るのか調べてみよう。

;; 英語の用法をしらべてみると、やっぱり普通は、引用
;; をはじめるところをquoteと言うようだ。そして、引
;; 用のおわりをunquoteと言う。unquoteは日本語の「括
;; 弧閉じ」かな。

;; 例:
;; MacArthur said, quote, I shall return, unquote.

;; quote unquote は日本語の「括弧」、「括弧閉じ」み
;; たいな言葉であり、総称的でもあるので、quote、
;; unquoteと言ったときの具体的な文字はいろいろあり
;; うるわけです。''もあり、""もあり、()もあり、{}も
;; あり、ですね。なので、"Lisp Quote Unquote"は、
;; 「Lisp () 」というのがひとつの解釈ですね。なるほ
;; ど。ついでなので、"Lisp Quote Unquote"の解釈につ
;; いてもう少し探ってみよう。

;; まず、そもそもquote-unquoteというのは、「いわゆ
;; る」という用法があるので、「いわゆるLispについて」
;; というものが別の解釈。

;; もうひとつがBBC-Radioの長寿クイズ番組"Quote
;; ... Unquote"。過去の名言や発言をパネラにお題とし
;; て出して、それが誰が言ったものかを当てたりする番
;; 組のようだ。この番組に因むのが3つ目の解釈。

;; 閑話休題。

;; では、バッククォートって何なのか、どこから来たの
;; か。それはどうやらASCIIコードと印字アプリ(Tex等)
;; がからんでいそうだ。これらを調べる準備として、
;; Unicodeではどうなっているか、確認しておこう。

;; まずUnicodeではQuotationは「同じグリフでも言語に
;; よって用法が違うもの」としている。その結果、ひと
;; つのグリフについて用法をオーバロードさせるという
;; ルールにしている。

;; それを前提として英語ではどうなっているか調べてみ
;; る。ここでは、wide characterは取り扱わない、とい
;; う制限をかけて進めよう。wide characterまで取り扱
;; うと結構なボリュームになるが、こと英語に関して得
;; られる知見は少ないと思うので。

;; まず、英語において推奨、というか、好ましい引用文
;; 字は、次の組み合わせだ。

;; U+2018 引用対象 U+2019

;; フォントがUnicodeに対応していれば、次の行でグリ
;; フを確認できます。

;; ‘ 引用対象 ’

;; 名前はそれぞれ次のよう。

;; ‘ : U+2018 : left single quotation mark
;; ’ : U+2019 : right single quotation mark

;; そうするとLispのquoteで使っているあの文字は何か
;; というと、

;; ' : U+0027 : apostrophe (= apostrophe-quote)

;; なんですね。では、Lispのback quoteで使っているあ
;; の文字は何かというと、

;; ` : U+0060 : grave accent

;; であり、quotation markではなくaccent markなんで
;; すね。ちなみにgrave accentって何ですか、というと、
;; 「抑音アクセント」のことのようです。

;; Unicodeには、どうやらback quoteという概念は無い
;; ようです。さて、ASCIIコードと印字アプリ(Tex等)に
;; 進もう。

;; まず、ASCIIコードにて、'と`は何なのか。

;; まず初版たるASA standard X3.4-1963によると、基本
;; 的には、printable charactersは、コードとグリフの
;; 対応だけがあり、名前は付いていない。ただし、
;; U+0027相当(0x27)については、(APOS)という注釈があ
;; るので、これはapostropheを意図したものなのだろう。
;; そしてなんと、U+0060相当はまだ未定義なんですね。
;; アルファベット小文字も未定義。ASCIIコード自体何
;; 度か改訂されて今のものになったようだ。そのあたり
;; の経緯は、

;; The Evolution of Character Codes, 1874-1968
;; http://www.pobox.com/~enf/ascii/ascii.pdf

;; に詳しい。この文書、とても面白い。一杯やりながら
;; 読みたいたぐい。

;; さて、ANS、ECMA、ISOでごちゃごちゃやって、1967年
;; に今の形にかたまったようだ。それは正式には、ANS
;; (Americal National Standard)ではなく、USAS (USA
;; Standard) として規格化されたらしい。(X3.4-1967)

;; X3.4-1967においては、Lispにおける
;; quoteとbackquoteは、

;; ' : 0x27 : apostrophe
;; ` : 0x60 : (no legend)

;; という状態です。

;; さて、先の歴史文書ではどうだろう。そこでは、
;; 0x27は、apostropheとacute accentがoverloadされて
;; おり、`はgrave accentとのみある。

;; ちなみに、acute accentは、「揚音アクセント」。本
;; 来のグリフは、grave accentと左右対称なものです。

;; これでASCIIコードを確認できた。

;; 次は印字アプリとの関係です。これはWikipediaに詳
;; しい。

;; http://en.wikipedia.org/wiki/Quotation_mark_glyphs

;; 要約すると次のとおり。

;; 昔のPCのフォントは、スクリーン用も印字用も0x27の
;; グリフは垂直ではなく、right single quote markの
;; ように傾いていた。すると引用が、’hoge’ のように
;; なり格好わるい。これをマシにするために、0x60が転
;; 用されたのだ。`hoge’とすると多少見栄えが改善され
;; たということ。

;; これで経緯はわかった。そして、経緯の中では、
;; 0x27をsingle quote markと呼ぶ例をみかけた。(あく
;; までapostropheにoverloadされたものとしてです)
;; 0x60を back quoteと呼ぶ例はみかけなかった。

;; ここからは推測だが、次のような経緯ではないか?

;; ASCIIコードの初版では、0x60 は未定義であり、
;; single quote markとして使えるのは、0x27しかなかっ
;; た。なのでこれを代用した。そしてこれを"Quote"と
;; 呼ぶようになった。しかし、もともと0x27は
;; apostropheなので、垂直ではなく’というように傾い
;; ているフォントが多かった。そんな状況で、0x60が
;; grave accentとして導入された。するとそれをleft
;; single quotation markとして転用するというアイデ
;; アがでた。傾きが逆なので具合がよい。さて、rightの
;; 方を0x27をつかってすでに"Quote"と呼ぶ慣習になっ
;; ていた。そのため新たに加えた0x60は、その逆傾斜である
;; ということで"Back Quote"と呼ぶ慣習となった。

;; ふう。writing directionがleft to rightなのに、な
;; ぜ、leftをback quote、rightをquoteと呼ぶのか?と
;; いうことについては、何となく合点がいった。

;; 違う観点で、もう少しだけ調べよう。

;; Common Lispでは、確か文字コードを指定していない
;; はずだ。その観点で、この話題を調べてみよう。
;; Common LispはCLtL2に限ることにする。

;; まず、CLtL2はASCIIというかISO6937/2というかを意
;; 識して、標準文字を定義している。これを文字レパー
;; トリと言う。しかし文字コードは実装の自由度として
;; おり、指定していない。

;; ところで、ISO6937/2では、全ての文字に文字ラベル
;; と記述(意味または備考と理解すればよさそう)が定義
;; されており、CLtL2もそれに準拠している。前述のと
;; おりCLtL2は文字コードは指定していないので、CLtL2
;; における文字レパートリの定義はグリフ中心となる。
;; ISO6937/2から文字コードを抜いたものと考えればよい。
;; 具体的には次のとおり。

;; SP05 ’ apostrophe
;; SD13 ‘ grave accent

;; せっかくなので、文字ラベルの読み方を確認しよう。

;; S P 0 5
;; | | | |
;; | | | |__ for alphabetic characters:
;; | | | | odd digit = small letter;
;; | | | | even digit = capital letter.
;; | | | |__ If N or S in first position:
;; | | | no special meaning.
;; | | |
;; | | |__ for alphabetic characters:
;; | | | 0 = etter without diacritical mark;
;; | | | 1 to 3 = letter with diacritical mark above it;
;; | | | 4 = letter with diacritical mark below it;
;; | | | 5 and 6 = special form.
;; | | |__ If N or S in first position:
;; | | no special meaning.
;; | |
;; | |__ For alphabetic characters:
;; | | A to Z = the respective letter of the Latin alphabet.
;; | |__ If N in first position:
;; | | D = digit;
;; | | F = fraction;
;; | | S = subscript or superscript.
;; | |__ If S in first position:
;; | A = arithmetic sign;
;; | C = currency sign;
;; | D = diacritical mark;
;; | P = punctuation mark;
;; | M = other symbol (miscellaneous)
;; |
;; |__ For all graphic characters:
;; L = Latin alphabetic character;
;; N = numeric graphic character;
;; S = special graphic character.

;; ちなみに、ISO6937/2では文字コードも定義されてい
;; るので、そのとおりに処理系を実装すれば、

;; SP05 ’ apostrophe => 0x27 '
;; SD13 ‘ grave accent => 0x60 `

;; となる。

;; さて、CLtL2の文字の定義においては、SP05がquoteで
;; あるとか、SD13がback quoteであるという話題はない。

;; それらがquoteであるとかback quoteであると成るの
;; は、「マクロ文字」の定義においてである。

;; ああ、そうなのだ。これは「文字」の定義の話ではなく
;; 「マクロ文字」の定義の話なのだ。
;;「#」を「シャープ」(文字定義)ではなく、「ディスパッチ」
;; と考えて(マクロ文字定義されて)、時にはその
;; ように呼ぶように、「’」を「アポストロフィ」ではなく
;; 「クォート」と考えて(定義されて)、時にはそのよう
;; に呼んだり、「‘」を「グレイブ アクセント」ではなく
;; 「バッククォート」と考えて(定義されて)、時にはそのよう
;; に呼んだりすると理解しておけばよいのだ。

こつこつ、再開。

2010年1月11日月曜日

Schemeコードバトンに参加しました

Schemeコードバトンに参加しました。g000001さんのをテンプレに、そのリポートを。

第1回 Scheme コードバトンのお知らせ - ひげぽん OSとか作っちゃうかMona-
Schemeコードバトンに参加しました - わだばLiperになる -

  • g000001さんからバトンを渡されました。
  • 渡されたバトン http://gist.github.com/273441
  • 次はquek(LETTER)さんへ
  • 渡したバトン http://gist.github.com/273567
  • やったことは、g000001さんがCL化したことを受けて、「とにかくCLらしく!」です。CL臭が結構出せたとしたら嬉しいです。
  • 変更点

    • シェルアプリではなくREPLアプリという流れを助長しました。エントリポイントは、mainではなく、(hige:pon)に変更。
    • 英単語を登録できるTop-level関数をつくりました。(hige:pin)
    • 辞書のCSV一覧表示のTop-level関数をつくりました。(hige:pun)
    • pinとpunは、実践Common Lisp の第三章の技法をそのまま使いました。CL入門者の方が担当されたときにいじりやすいかな、という狙いです。
    • このツールを使う名前空間(主にcl-userかな)と、このツールの名前空間の整理をきっちりしました。英単語はSymbolで表現し、case-sensitive かつ package-awareでread/writeするようにしました。(cl-userが英単語で汚れていかないように、ということ)
    • read-char,clear-inputのシーケンスが、私のTop-levelでは挙動があやしかったので、read-lineに変更しました。
    • ダイナミック変数を導入しました。ツールの利用のライフサイクルを通して固定されている束縛については、引数で渡していかなくてもよいだろうと。先々テストもしやすいかもしれません。
    • CLのイメージ指向を際立たせるためdocumentationストリングをまめに書きました。英語はいいかげんです。。。
    • 処理に無駄がないように、read time evaluationを必要なところで使いました。
    • 制御に関わる部分は命令的に書き、関数の性質がある部分は関数的に書きました。
    • 正答率でソートする部分は、学習効率を高めるためのストラテジの部分なので、関数として外に出して交換しやすくしました。現状は#'sort-dict-standardのみ提供。
    • とにかくCLらしく、ということで、(lambda もいちいち#'(lambdaにかえたり。

    • なんとも気持がいいので、goto、残しました。



やってみて気づいたのは、

Scheme と CL は思ってた以上に別物だ

ということです。自分一人で両方やっているときは、そういう風には思わなかったんです。距離感としては、C言語とJavaぐらいはあるように思います。優劣ではなく距離感として。

あ、もしかしたら今回のものがピュアR6RSだから?かもしれません。

こつこつ。

2010年1月10日日曜日

Lisp Quote Backquote (1)

On Lispの7 Macrosに入ったところで、quote backquoteが出てきました。簡単な説明はあるのですが、そういえば、backquoteについてちゃんと調べたことなかったなぁと思って調べはじました。これはそのメモです。

backquoteは結構難しいみたいなので、その3までいくかもしれません。


;;;;
;;;; Lisp Quote Backquote
;;;;

;; Common Lispのquoteとbackquoteを理解しようという
;; 試みです。

;; 前半は、基本的な動作を確認しています。振舞の確認
;; は、stefilによるテストとして記述しています。ただ
;; し、the reader の内部動作についてはテストとして
;; 書けないものもありました。それらはコメントに振舞
;; を記述しています。

;; 後半はbackquoteをちゃんと理解するために、CLtL2の
;; 付録Cをやっています。ちなみに付録Cにはバグという
;; かタイポがあります。ここに掲載しているのは修正版
;; です。

;; Steeleも書いているとおり、backquoteの理解は簡単
;; ではありません。

;; 特にbackquoteのsemanticsが、本質的には、readerと
;; evaluatorに跨っているところだと思います。すなわ
;; ち、backquoteが無くなるまで評価しきったところで、
;; 意味が定義されているのですが、マクロなどで
;; backquoteが残った状態で使われたりするのですね。

;; より根源的に言うと、入れ子になったbackquoteの仕
;; 様について、ANSI CLはほとんど定義していません。
;; これは大変残念なことです。


;;;; test facility
(asdf:operate 'asdf:load-op :stefil)
(use-package :stefil)
;;(in-root-suite)
;;(defsuite* test-for-understanding)


;;;; quote basics
(is (eq (quote 1)
1))
(is (eq (quote nil)
nil))
(is (eq (quote t)
t))
(is (not (eq (quote #\A)
(quote a))))
(is (not (eq (quote #\A)
(intern "#\A"))))
(is (eql (quote #\A)
#\A))
(is (equal (quote "ab")
"ab"))
(is (eq (quote a)
(intern "A")))
(is (not (eq (quote (quote a))
(quote a))))
(is (equal (quote (quote a))
(list (quote quote) (quote a))))


;;;; quote (standard macro character)
(is (eq 'a
(quote a)))
(is (equal '(a)
(quote (a))))
(is (equal (read-from-string "'a")
''a ))
(is (eq (read-from-string "a")
'a ))
(signals end-of-file (read-from-string "'"))
(signals end-of-file (read-from-string "' "))
(is (equal (read-from-string "' a")
''a))
(is (equal (list ' a 'b)
'(a b)))
(is (eq (car (read-from-string "'a"))
'quote))


;;;; quote and conses
(is (eq 'nil
nil))
(is (eq '()
nil))
(is (equal '(a . b)
(cons 'a 'b)))
(is (equal '(a b . c)
(cons 'a (cons 'b 'c))))

(is (equal '(a)
(cons 'a 'nil)))
(is (equal '(a)
(list 'a)))
(is (equal ''(a)
(cons 'quote
(cons
(cons 'a 'nil)
nil))))
(is (equal ''(a)
(quote (quote (a)))))
(is (equal ''(a)
(list 'quote '(a))))
(is (equal ''(a)
(list 'quote (list 'a))))
(is (equal '('a)
(quote ((quote a)))))
(is (equal '('a)
(list ''a)))
(is (equal '('a)
(list (list 'quote 'a))))


;;;; backquote and quote
(is (eq `nil
'nil))
(is (eq `a
'a))
(is (equal `(a . b)
'(a . b)))
(is (equal `(a)
'(a)))

(signals end-of-file (read-from-string "`"))
(signals end-of-file (read-from-string "` "))
(is (equal (read-from-string "` a")
'`a))
(is (equal (list ` a ' b)
'(a b)))
(is (not (equal ``a
''a)))
(is (not (equal '`a
`'a)))
(is (equal `'a
''a))
(is (eq (eval ``a)
(eval ''a)))
(is (eq (eval '`a)
(eval ''a)))


;;;; backquote and comma
(setf a 1
b 2
c 3
x 'y
(symbol-function 'x) #'identity
y 'z
(symbol-function 'y) #'identity
z 9
(symbol-function 'z) #'identity
m '(p q r)
n '(10 20 30))

(is (eq `,a
a))
(is (eq `,x
x))
(is (eq `,m
m))
(is (equal `,m
'(p q r)))

(is (equal `(a . b)
(append (list `a) 'b)))
(is (equal `(a b . c)
(append (list `a) (list `b) 'c)))
(is (equal `(,a . b)
(append (list `,a) 'b)))
(is (equal `(,a . b)
(append (list a) 'b)))
(is (equal `(,a . ,b)
(append (list a) b)))
(is (equal `(,a ,b)
(append (list a) (list b) 'nil)))
(is (equal `(,@m)
(append m 'nil)))
(is (equal `(,@m ,@n)
(append m n 'nil)))
(is (equal `(a ,b ,m ,@n)
(append (list `a) (list b) (list m) n 'nil)))
(is (equal `((,a b) ,c ,@m)
;; (append (list `(,a b)) (list c) m)
;; (append (list (append (list a) (list `b))) (list c) m)
(list (list a 'b) c 'p 'q 'r)
))

(signals reader-error (read-from-string ","))
(signals end-of-file (read-from-string "`,"))
(signals end-of-file (read-from-string "`, "))
(is (equal (read-from-string "`, a")
'`,a))
(is (eq (eval '`,a)
a))
(is (equal (read-from-string "``,a")
'``,a))
(is (eq (eval '``,a)
'a))
(is (eq (eval (eval '``,a))
a))
(signals reader-error (read-from-string "`,,a"))
(is (equal (read-from-string "``,,a")
'``,,a))
(is (eq (eval '``,,a)
a))
(is (equal (read-from-string "`,`,a")
'`,`,a))
(is (eq (eval '`,`,a)
a))

(is (equal `````,,,,,a
a))
(is (eq ``````,,,,,a
'a))

(is (equal `,`,`,`,`,a
a))
(is (eq `,`,`,`,`,`a
'a))


;; nested backquotes and commas [the reader internal]
;; (The reader's behavior is implementation dependent.
;; Here we use the allegro.)

(is (equal (read-from-string "`a")
'(excl::backquote a)))

(is (equal (read-from-string "``a")
'(excl::backquote (excl::backquote a))))

(is (equal (read-from-string "`,a")
'(excl::backquote (excl::bq-comma a))))

(is (equal (read-from-string "``,,a")
'(excl::backquote
(excl::backquote
(excl::bq-comma
(excl::bq-comma a))))))

(is (equal (read-from-string "`,(+ 1 2)")
'(excl::backquote
(excl::bq-comma
(+ 1 2)))))

(is (equal (eval
(read-from-string "`,(+ 1 2)"))
3))

(is (equal (read-from-string "``,(+ 1 2)")
'(excl::backquote
(excl::backquote
(excl::bq-comma
(+ 1 2))))))

(is (equal (eval
(read-from-string "``,(+ 1 2)"))
(excl::backquote (+ 1 2))))

(is (equal (eval
(eval
(read-from-string "``,(+ 1 2)")))
(+ 1 2)))

(is (equal (read-from-string "``,,(list '+ 1 2)")
'(excl::backquote
(excl::backquote
(excl::bq-comma
(excl::bq-comma
(list (quote +) 1 2)))))))

(is (equal (eval
(read-from-string "``,,(list '+ 1 2)"))
(list (quote +) 1 2)))
(is (equal (eval
(eval
(read-from-string "``,,(list '+ 1 2)")))
(+ 1 2)))

(is (equal (read-from-string "`(,x `(,x `(,x)))")
'(excl::backquote
((excl::bq-comma x)
(excl::backquote
((excl::bq-comma x)
(excl::backquote
((excl::bq-comma x)))))))))
(eval
(read-from-string "`(,x `(,x `(,x)))"))
;; => (Y (EXCL::BQ-CONS X `((EXCL::BQ-LIST X))))

(eval
(eval
(read-from-string "`(,x `(,x `(,x)))")))
;; => (Y (EXCL::BQ-LIST X))

(eval
(eval
(eval
(read-from-string "`(,x `(,x `(,x)))"))))
;; => (Y) ; fully evaluated. implementation independent.

(is (equal
(read-from-string "`(1 `( 2 `( 3,,,x)))")
'(EXCL::BACKQUOTE
(1 (EXCL::BACKQUOTE
(2 (EXCL::BACKQUOTE
(3 (EXCL::BQ-COMMA
(EXCL::BQ-COMMA
(EXCL::BQ-COMMA X)))))))))))

(is (equal
(read-from-string "`(x `(y `(z,,,x)))")
'(EXCL::BACKQUOTE
(x (EXCL::BACKQUOTE
(y (EXCL::BACKQUOTE
(z (EXCL::BQ-COMMA
(EXCL::BQ-COMMA
(EXCL::BQ-COMMA X)))))))))))

(eval
(read-from-string "`(x `(y `(z,,,x)))"))
;; =>
;; (X (EXCL::BQ-LIST
;; (EXCL::BQ-QUOTE Y)
;; (EXCL::BQ-LIST (EXCL::BQ-QUOTE EXCL::BQ-LIST)
;; (EXCL::BQ-QUOTE
;; (EXCL::BQ-QUOTE Z))
;; Y)))

(eval
(eval
(read-from-string "`(x `(y `(z,,,x)))")))
;; => (Y (EXCL::BQ-LIST (EXCL::BQ-QUOTE Z) Z))
(eval
(eval
(eval
(read-from-string "`(x `(y `(z,,,x)))"))))
;; => (Z 9)
(eval
(eval
(eval
(eval
(read-from-string "`(x `(y `(z,,,x)))")))))
;; => 9 ; fully evaluated. implementation independent.

次回は、Steeleの、

;;; Common Lisp backquote implementation, written in Common Lisp.
;;; Author: Guy L. Steele Jr. Date: 27 December 1985

です。まだまだ精査中。。。これは楽しめる!!

こつこつ。

【On Lisp】6 Functions as Representation

なるほど。ネットワークというお題にて、コンパイルを、

  1. (ルール -> データ構造 ) + 辞書 + プログラム
  2. ルール -> 辞書 + ((データ構造 & プログラム) = クロージャ) [インタプリタ]
  3. (ルール -> ((データ構造 & プログラム) = リンクされたクロージャ)) -[コンパイル]-> コンパイル済みプログラム

という文脈で説明するのか。うまい。

お、Lisp復習パートが終わった。ついにマクロだ!

こつこつ。

2010年1月9日土曜日

【On Lisp】5 Returning Functions

ひさしぶりに、のんびりLisp。


** 5 Returning Functions

*** 5.1
- そうか、レキシカルスコープでは関数の呼出し時に新
しいクロージャをつくることができるということは、
ダイナミック変数とは違う意味で動的であるといえ
るな。
- ダイナミック変数でも関数呼出し時に関数を作ること
はできるが、それはどこまでいっても単なるダイナミッ
クバインディングなだけではある。

- 例

CL-USER> (defun make-adder (n)
#'(lambda (x)
(+ x n)))
MAKE-ADDER
CL-USER> (setq add3 (make-adder 3))
#<Interpreted Closure (:INTERNAL MAKE-ADDER) @ #x1000ee3a82>
CL-USER> (funcall add3 2)
5
CL-USER> (proclaim '(special m))
T
CL-USER> (setq m 1)
1
CL-USER> (defun make-adder-dyn (m)
#'(lambda (x)
(+ x m)))
MAKE-ADDER-DYN
CL-USER> (setq add3-dyn (make-adder-dyn 3))
#<Interpreted Closure (:INTERNAL MAKE-ADDER-DYN) @ #x100112ead2>
CL-USER> (funcall add3-dyn 2)
3
CL-USER> (setq m 2)
2
CL-USER> (funcall add3-dyn 2)
4
CL-USER> (setq n 2)
2
CL-USER> (funcall add3 2)
5
CL-USER>

- そうか。関数を引数にとり関数を返す関数が意味をも
つのは、返している関数が単なる関数ではなく、レキ
シカルクロージャであり、引数である関数をクローズ
しているからなんだな。
- クロージャの威力の紹介として、オブジェクトシステ
ムなど、値の保持方法として紹介されることが多い。
それはそれで役に立つけれども、そもそもOOをあまり
やらないという人にはいまいち説得力がないように思
う。でも、汎関数プログラミングのインフラだよ、と
言われれば誰にとっても説得力があるように思う。

*** 5.2
- 直交性という観点にいくなら、それはもうSchemeの
独壇場のような。。。
- おお、Common LispのScheme化的な動きが、、、

*** 5.4
- PGのcomposeはPAIPのものよりもいい。

- PAIPのは、1引数関数しか合成できない。

(defun compose (&rest functions)
#'(lambda (x)
(reduce #'funcall functions :from-end t :initial-value x)))

*** 5.5
- なるほど。再帰のパターン化というのはこんな感じな
んだな。
- やはりlambdaというのは、実行遅延なんだよなぁ。

CL-USER> (setq x 3)
3
CL-USER> x
3
CL-USER> (setq hoge #'(lambda () (setq x 5)))
#<Interpreted Function (unnamed) @ #x1000ff6d62>
CL-USER> x
3
CL-USER> (funcall hoge)
5
CL-USER> x
5
CL-USER>

- thunkも実行遅延なんだけど、より原始的な意味でい
うと、サブルーチンというか手続きというかもそれ自体
実行遅延だよね。
- 関数もそれらの一種とするなら、関数と関数呼出しと
は制御構造の親玉だよね。コードを切り貼りして実行
タイミングを調整する機構。本質的にはgotoやjump
だし。すると、構造化プログラミングとの折り合いは
どうなっているのだろう?
- このあたりについて、プログラミング言語階層での説
明語彙として「継続」を位置づけるのはどうだろう?
継続という用語の導入として、継続を取り出してファー
ストクラスで扱うときが始めてということが多いけど、
それだと逆にわかりにくいのではないか?
- 例えば末尾再帰もある意味継続の構造の問題ですよね。
継続にノイズが入らないのが末尾再帰というか。
- 関数が数学的な意味での関数であるとすると、話はまっ
たく別になるな。そもそも実行順とか制御の考えが入っ
て無いから。
- なんかつれづれなるままに、だな。。。

- さて、遅延させて渡すときはthunkを作る(使う)など
して、 関数でもコードの抽象化(パターン抽出)ができ
るよ、というのがこの章のテーマのようだ。
- ただし、やってみるとわかるが、関数だけで抽象化
させようとすると、関数的抽象化がどうできるのか
にいろいろ腐心することになる。
- もっと手軽に抽象化するなら、関数的抽象化ではな
く表現的抽象化であり、それがこの本の主題たるマ
クロなんですよ〜、ということかなぁ。

こつこつ。

2010年1月7日木曜日

【On Lisp】4 ユーティリティ関数

年始にもかかわらず、業務繁忙。
ちょっとずつ進めて、やっと4章完了。

ユニットテストをたくさん書きながら、理解を深めた。

特にあたらしい知見はない。

こつこつ。

2010年1月2日土曜日

【On Lisp】3 関数プログラミング


** 3 関数プログラミング
*** 3.1
- 副作用について読むとき、いつも違和感がある。
- プログラマまたはユーザから見れば、何が副作用で
何が本作用かは、実現したい課題によるのではない
か。
- それを(関数型)言語の視点から一方的に、値を返却
すること以外の作用を全て副作用と呼んでしまう。
これは乱暴ではないか?
*** 3.3
- なるほど。純粋に関数型であるかどうかよりも、関数
型インターフェイスを提供するかどうかを重要視する
のか。
- 「関数が返すquoteに注意」は知らなかった。

こつこつ。

【On Lisp】2 関数


** 2 関数
*** 前説
- イメージ指向の簡潔な解説あり。
*** 2.2
- PGは、Lisp2だとコードがかっこ悪くなることがある、
という。正気か?
- 'list'を'lst'と書くのはかっこ悪くないか?
*** 2.4
- お、このタイミングでplistと関数の組み合わせを扱
うのか。展開が早くてよろし。
*** 2.7
- count-instancesでlabelsの必然性の導入というのは
違和感ある。そもそもcount-instancesくらいなら、
labelsというか再帰すら使わないんじゃないかなぁ。

CL-USER> (defun count-instances-1 (obj list-of-lists)
(mapcar #'(lambda (list)
(count-if #'(lambda (x) (eq obj x))
list))
list-of-lists))
COUNT-INSTANCES-1
CL-USER> (count-instances-1 'a '((a b c) (d a r p a) (d a r) (a a)))
(1 2 1 2)
CL-USER>
*** 2.9
- On LispはCLtL2なんだな。
- Allegroは「空でないレキシカル環境においてインタ
プリタ的に定義された関数」をコンパイルできる。

CL-USER> (let ((y 2))
(defun foo (x) (+ x y)))
FOO
CL-USER> (compile 'foo)
FOO
NIL
NIL
CL-USER> (compiled-function-p #'foo)
T
CL-USER>

- そうか。コンパイルされた関数が返す関数はコンパ
イル済みなんだな。

CL-USER> (defun make-adder (n)
#'(lambda (x) (+ x n)))
MAKE-ADDER
CL-USER> (interpreted-function-p #'make-adder)
T
CL-USER> (compiled-function-p #'make-adder)
NIL
CL-USER> (setq add2 (make-adder 2))
#<Interpreted Closure (:INTERNAL MAKE-ADDER) @ #x1000d01fb2>
CL-USER> (interpreted-function-p add2)
T
CL-USER> (compiled-function-p add2)
NIL
CL-USER> (compile 'make-adder)
MAKE-ADDER
NIL
NIL
CL-USER> (interpreted-function-p #'make-adder)
NIL
CL-USER> (compiled-function-p #'make-adder)
T
CL-USER> (setq add2 (make-adder 2))
#<Closure (:INTERNAL MAKE-ADDER 0) @ #x1000e55e52>
CL-USER> (compiled-function-p add2)
T
CL-USER> (interpreted-function-p add2)
NIL
CL-USER>

こつこつ。

【On Lisp】1 拡張可能なプログラミング言語


* On Lisp
- 新しい知見や疑問点などをメモする。
** 1 拡張可能なプログラミング言語
- 新しい知見はない。

こつこつ。

On Lisp を読む

なんとなく、気分として、必読図書で読んでいないものを読みたくなりました。というと必読図書をかなりこなしていそうですが、ほとんと読んでいないというのが実情です。まずはLisp系からいこうと思います。

トップバッターとして、今まで手を出さなかったOn Lisp を読んでみます。

On Lisp

On Lispに手を出さなかったのには理由があるのですが、読んでもいいかなぁと思える状態にはなったようです。

さて、On Lispを読み終えることができたら、Let Over Lambdaもいけるかもしれません。お風呂用まで買ったのですが、読書条件がOn Lispにdepends-onしているために読めないでおります。

こつこつ。