2008年7月18日金曜日

さらに、その後

テーマ毎にわかれているのもなんだな、ということで、一箇所に集約しつつあります。

計算機とその周辺

ブログのやり方も、まだいまひとつ飲み込めていないので、そのうち考えてみようと思います。

2008年5月7日水曜日

その後

Common Lisp の習得を目指していますが、ちょっと寄り道というか回り道をしています。

まず、実際にプログラムを組もうとすると、組むための開発環境だとかパーシステンスのあり様だとかで、数理論理の基礎を勉強しなければ、と思いました。飛躍している、とつっこまないでください。一応つながっているのです。

あと業務の関係でRubyを使う必要がでてきたので、これも脇道で、ウサギ本を読んでいます。

Lispは基礎を固める、ということで次の2冊をちょっとずつ読んでいます。

あと、いろいろな本を読んでいると、やはりC言語とJavaは知らないと、例とかでわからないことが多いです。そこで、C言語から勉強しようと思います。

なんか、この読書の連鎖のアルゴリズムは、無停止なような気がしてきましたが、楽しんでやっているのでいいかな、と。
そうだ、本当はアルゴリズムとデータ構造の勉強がしたいのです。それはもう少し片付いたところで。

2008年1月24日木曜日

Chapter 32 Conclusion: What's Next?

前説
Finding Lisp Libraries
Interfacing with Other Laguages
Make It Work, Make It Right, Make It Fast
Delivering Applications
Where to Go Next

特になし。

読了。

Chapter 31 Practical: An HTML Generation Library, the Compiler

前説

FOO言語においては、InterpreterとくらべてCompilerが最適化しているのは、テキストをできるだけまとめて出力するようにすること。


The Compiler


  • The compilerのアーキテクチャ。
  • html-compilerクラス。このオブジェクトにbackendの総称関数へ成されたopsを集積させる。
  • backendの総称関数は、前項へのops書き込み機能を持つ。
  • opsを最適化する関数にわたし、最適化される。
  • 最適化されたops vectorをgenerate-code関数が受け取り、CLのコードを吐く。


  • compilerがhtml-compilerのときのprocessor interface(メソッド)は、html-compilerへのopsの集積処理とする。
  • 関数sexp->opsを定義する。これは、前項を順次S式に適用してopsのvectorを作成する。
  • 関数optimize-static-outputを導入する。これは前項のvectorについて、raw-stringを合成する。
  • 関数generate-codeを導入する。これはopsに対して逐次総称関数op->codeを適用するだけ。
  • 総称関数op->codeを定義する。これが、それぞれのopを対応するLisp codeに変換する。
  • op->codeの定義でポイントは2つ。embed-valueとembed-code。
  • embed-valueは、Lispの変数の埋め込みに対応する。これは(princ-to-string value)が本質である。
  • embed-codeは、Lispのコードの埋め込みに対応する。これは、入力のコードをそのまま使える。



FOO Special Operators


  • Special Operatorは、CL本体と同じように、言語の通常の評価ルールでは実現できないことを実現するためのものである。
  • まず、現在のembed-codeの実装(そのまま)では、(p: (random 10))などで、(random 10)の値はすてられ、出力は

    となる。値が欲しいときもある。
  • そのために、:print、:formatを導入する。
  • :printは、embe-codeで検知され、そのbodyをembed-valueに渡す実装とする。
  • :formatは、それの引数がself-evaluatingであれば、それをemit-htmlに渡せる(Interpreterで使える)。Compilerのときは、引数はどんなLisp expressionsでもよい。


  • Special Operatorの実装方法。
  • まず、関数processの第一分岐としてprocess-special-formを呼ぶようにする。
  • Special formは、'html-special-operator(plist)に格納する。名前とlambdaの組で格納。
  • 個々のSpecial formの定義はマクロdefine-html-special-operatorで簡略化する。
  • 関数process-special-formは、plistからlambdaを取得して、formに適用するだけ。
  • これによって、Special Operatorの拡張をモジュール化できている。



FOO Macros


  • Special Operatorの実装方法と類似。
  • 'html-macro plistにマクロ名と処理lambdaを登録する。
  • ただし、ユーザはそのlambdaを直接書くのではなく、そのlambdaを作るための関数generate-macro-with-attributesなど(define-html-macroから呼ばれる構造とする。
  • generate-macro...は、ユーザから受け取ったマクロ定義(args and body)を、DISTRUCTURING-BINDに渡して、argsの置換をおこなうようにする。
  • 関数processの第二分岐にexpand-macor-form関数を登録する。これがマクロ展開を実行する。



The Public API


  • あり? なぜここで急にXHTML? *xhtml*がnil or t。
  • マクロin-html-styleを定義。
  • マクロhtmlを導入。
  • マクロhtmlはそれ自身ネスト可能であるし、その中身は*pretty*の値によって振舞が違うので、コード分岐を含んでもいる。すなわち単純な実装だとネストによって指数関数的にコードが増大してしまう。
  • それはmacroletで回避可能。


The End of the Line

JavaScriptのS式表現をつくる元気はありません。。。

Chapter 30 Practical: An HTML Generation Library, the Interpreter

気をとりなおして。
30章と31章については、各節のエッセンスをメモしていく形にします。


前説


  • Interpreter : FOOプログラムを受け取って、HTMLを生成する。
  • Compiler : FOOプログラム(CL埋め込みあり)を受け取って、CLのコードを生成する。



Designing a Domain-Specific Language

DSL設計は2段階。

  • DSLの構文というか書きぶりを決める。
  • それの処理系を実装する。


DSLの設計のポイントは、DSLによっていかに表現が「圧縮」されるかということ。


The FOO Language


  • Lisp objectsを基礎におく。
  • self-evaluating lisp objectsは、PRINC-TO-STRINGで変換したものをFOOでは出力する。このとき<>などのエスケープも行う。
  • HTMLの要素名をキーワードシンボルとする。キーワードシンボルを第一要素とするリストを、HTMLの要素に対応させる。
  • 属性は次の2つの表現が可能とする。
    表現1 (:p :id "x" "Foo")
    表現2 ((:p :id "x") "Foo")


Character Escaping


  • 要素と属性でエスケープ対象文字が異なる。
  • 対象文字を、*element-escape*, *attribute-escape*に格納する。
  • *escape*に場所場所でどちらかを割り当てるなどの運用をする。



Indenting Printer


  • indenting-printerというクラスに情報を格納してindentを制御する。
  • ただし、indenting-printerのindentation slotに入っている値に対応してindentするというだけの機構。
  • 文字列をindentして出力するための基本関数を定義。

    (defun emit (ip string) ...)
    (defun emit/no-newlines (ip string &key (start 0) end) ...)
    (defun indent-if-necessary (ip) ...)
    (defun emit-newline (ip) ...)
    (defun emit-freshline (ip) ...)




HTML Processor Interface


  • HTML Processorのインターフェイスは総称関数で定義する。
  • これはInterpreterとCompilerの両方に対応するため。
  • 総称関数は次の8つ。

    (defgeneric raw-string (processor string &optional newlines-p))
    (defgeneric newline (processor))
    (defgeneric freshline (processor))
    (defgeneric indent (processor))
    (defgeneric unindent (processor))
    (defgeneric toggle-indenting (processor))
    (defgeneric embed-value (processor value))
    (defgeneric embed-code (processor code))




The Pretty Printer Backend


  • html-pretty-printerというクラスを導入する。これは、slotにindenting-printerとtab-widthをもつ。
  • 前章の総称関数に対するメソッドの定義をする。そのとき、Indenting-printer節で定義した基本関数が使える。
  • embed-valueとembed-codeとは、Compilerのみで使用する。Interpreterではerrorとなるようにする。



The Basic Evaluation Rule


  • 評価としては、まずself-evaluatingならば、raw-string関数にわたす。
  • そうでなければ、それはFOO言語のcons-sexp形式となる。
  • process-cons-sexp-html関数を導入する。
  • この関数は、HTMLタグ出力、その中身の出力、インデントの管理、改行の管理を担当する。
  • これの振舞の観点から、HTML要素は3つに分類できる。

    (defparameter *block-elements* '(:body :colgroup ...))
    (defparameter *paragraph-elements* '(:area :base :blockquote ...))
    (defparameter *inline-elements* '(:a :abbr ...))

  • この他に2つの補足的分類がある。

    (defparameter *empty-elements* '(:area :base :br ...))
    (defparameter *preserve-whitespace-elements* '(:pre :script ...))

  • この分類を利用して、process-cons-sexp-htmlを実装する。
  • process関数を直に呼ぶのでは手間なので、emit-html関数を導入して省く。
  • *html-ouput*自身をAPIとして公開したくはないので、with-html-ouputマクロを導入して隠蔽する。



What's Next?

特になし。

2008年1月23日水曜日

Chapter 29 Practical: An MP3 Browser

前説

特になし。


Playlists

SHOUTcastの情報がなかなかみつからない。IcecastはSHOUTcastから派生したものらしい。

あとPlaylistの情報。



Playlists As Song Sources

著者がちゃんと説明しないので、私家版整理。

  • MP3をストリーム配信する場合、提供の仕方は2種類ある。ラジオ的なエンドレスなものと、アーカイブス的に指定した曲を提供するものである。
  • いずれにしても、SHOUTcastの仕組みとPlaylistは関係ない。MP3プレーヤに直にラジオ(チャンネル)や曲のURLを直に入力してもよい。
  • PlaylistはSHOUTcastを使ったサービスを便利にするための補完的なものである。
  • MP3 client(の一部)は(Nullsoftの)Playlistを読み込むことができる。
  • ラジオの場合は、Playlistの中のLengthが-1となっている。
  • 曲の場合は、その曲の秒数がLengthに入る。
  • ラジオの場合は、通常Playlistは単にそのチャンネルの聴取する入口の機能のみを担い、Playlistをユーザが編集するなどの機能はない。
  • 曲の場合は、Playlistからユーザが曲を選択して聴取することができるので、サーバ側がPlaylistの編集機能などを提供することによって便利になることもある。ただし、一度ダウンロードしたPlaylistをclient側でカスタマイズするというのもありなので、それしかやりようがないわけではない。
  • この本では、次の設計方針としている。

    • ラジオではなくアーカイブスとするが、曲毎のURLはもたない。
    • ユーザをIPアドレスで識別する。
    • それと相俟って、IPアドレスひとつにつきPlaylistをひとつサーバ上にもてるようにする。このPlaylistはNullsoftのPlaylistではない。
    • サーバ上のPlaylistをユーザがWebブラウザからカスタマイズできるようにする。
    • MP3 clientは単一のURL(ストリーミングフィード)にアクセスし、そのフィードから出る曲をWebブラウザからPlaylistという形で操作する。



Manipulating the Playlist
Query Parameter Types
Boilerplate HTML
The Browse Page
The Playlist
Finding a Playlist
Running the App

特になし。

この章楽しめませんでした。
CLの本ですから、作るアプリのドメイン情報やアーキテクチャについて、詳細に触れる必要はありませんが、説明がいいかげんに思えます。また、題材となっているSHOUTcastやPlaylistはNullsoftの勝手仕様なものであり、その仕様を自助努力で調べようにも入手可能な情報自体が不足しています。著者のCLのコードをみて仕様を想像するのでは、それが正しい実装なのか判断できません。

2008年1月22日火曜日

Chapter 28 Practical: A Shoutcast Server

前説
The Shoutcast Protocol

特になし。


Song Sources

うーん。やっぱりこの著者は、ドメインの説明が下手かな。
参考情報。


Implementing Shoutcast

特になし。

この章も楽しめました。
CLならではかな、と思えるところを振り返ってみると、

  • いろんなtypeのソースに総称関数で備えるところ。
  • 関数名とデータ名とアクセサなどが括弧の中で入り乱れつつ、アルゴリズムや意味を形成しているところ。

    • これっていいのか悪いのか、というのはありました。例えば、

      (index source)
      は、sourceオブジェクトに対するindex slotのリーダーを使っているのですが、CLではこれがそういう行為だという意味をこの表現は運んでいません。意図的なのですが。でも、source.getIndex()だったら、そういう行為だということは一目瞭然ではあるんですよね。


でしょうか。

Chapter 27 Practical: An MP3 Database

前説

特になし。


The Database

おいおい、テーブルから始めるんですか。。。

  • クラスtable は rowsとschemaというslotsを持つ。rowsは関数make-rowsによって初期化される。
  • rowsは a vectorに格納されるようにする。
  • make-rowsはmake-arrayの単純なラッパーになる。
  • クラスcolumnはschemaの構成要素である。
  • クラスcolumnは次のslotsからなる。

    • name
    • equality-predicate
    • comparator
    • default-value 初期値はnil
    • value-normalizer ここのinitformに何故このようなlambdaを置いているのか理解できない。(↓で解決)




Defining a Schema


  • equality-predicateとcomparatorの組は、型を定義することと同義である。
  • column objectsをmake-instanceで直に作るより、型を指定して生成するインターフェイスの方が便利。
  • ここでは総称関数を使う方法をとる。
    (defgeneric make-column (name type &optional default-value))
  • stringとnumberについてeql-specialized methodを定義する。
  • stringのvalue-normalizerは#'not-nullableという関数。nilだとsignalを上げる。
  • numberはvalue-normalizerは指定しない。すなわちinitformのもの。
  • interned-string向けにはまずcolumnのサブクラスを定義する。
    (defclass interned-values-column (column) ...)
  • これにはinterned-valuesというa slotを追加する。これはhash。
  • value-normalizerで、intern処理をする。関数名はintern-for-column。
  • interned-string用のmake-columnも定義。
  • これで、make-columnは、string,number,interned-stringについて定義できた。
  • make-schemaを次のように定義。

    (defun make-schema (spec)
    (mapcar #'(lambda (column-spec) (apply #'make-column column-spec)) spec))

  • specの例は次のとおり。

    (defparameter *mp3-schema*
    (make-schema
    '((:file string)
    (:genre interned-string "Unknown")
    (:artist interned-string "Unknown")
    (:album interned-string "Unknown")
    (:song string)
    (:track number 0)
    (:year number 0)
    (:id3-size number))))




Inserting Values

  • insert-rowはtableにa rowを挿入する関数である。
  • normalize-rowというヘルパー関数を作る。
  • names-and-valuesという引数を用意する。これはa plist。
  • normalize-for-columnというヘルパー関数を用意する。これはcolumn毎のvalue-normalizerを呼ぶ。


  • 関数file-rowは、MP3ファイルからID3情報をread-id3で抽出した上で、それをrowとして*mp3s*に格納するためのplistを作成する。



Querying the Database

うう、別にRDBを作りたくはないのだが。。。どこに連れていく気なんだ。。。


Matching Functions
Getting at the Results
Other Database Operations

特になし。


チュートリアルとしてはいろいろ参考になったのですが、作っているのがRDBなので、それは既存のRDBを使えばいいんじゃないの、とついつい思ってしまいます。バイナリデータのときとかは、あまりそう思わなかったのですが、なんででしょうね。

2008年1月21日月曜日

Chapter 26 Practical: Web Programming with AllegroServe

前説
A 30-Second Intro to Server-Side Web Programming

特になし。


AllegroServe

(defpackage :com.gigamonkyes.web
(:use :cl :net.aserve :com.gigamonkeys.html))

する前に、com.gigamonkeys.htmlのasdfによる読み込みが必要。


Generating Dynamic Content with AllegroServe

特になし。


Generating HTML

"The macro version will be quite a bit more efficient than the emit-html version. Not only do you never have to generate an s-expression representing the whole page, also much of the work that emit-html does at runtime to interpret the s-expression will be done once, when the macro is expanded, rather than every time the code is run."
が大事なことをいっていると思うのだが、理解できない。とりあえずノートしておく。

;; 追記
ああ、なぜ理解できないかはわかった。ここでいっていることの真偽は、結局htmlマクロってどうかかれているの? ということによるからだ。で、それは示されていないと。ここではそういう風にhtmlマクロは作られている、とだけ理解しておけばいいと解釈。


HTML Macro

特になし。


Query Parameters

simple-formが、なぜか出力がREPLに表示されて、WEBブラウザに出ない。
ここ、まず::typeは誤植かな。
あと、let ((*html-output* ...としているところは、(with-html-output ...でしょうか。とりあえず、そうしたら動きました。random-numberも同じです。errataをみると、、、出てない。著者に送っとこうかな。


Cookeis

特になし。


A Small Application Framewok

特になし。


CLを使ったWebアプリ開発の可能性を感じることができました。

Chapter 25 Practical: An ID3 Parser

前説

  • ID3v2.2とID3v2.3を実装対象とする。



Structure of an ID3v2 Tag

うーん。こういうところも、図表も付けてくれた方がわかりやすいと思うのだが。意図あるのかな?
メモ。

  • A tag は、a headerではじまる。
  • そのheaderは、そのtagの概要情報を含む。a headerとは次の整列ルールをもっている。

    • 3B : 文字列"ID3"。(ISO-8859-1 characters, 73,68,51)
    • 2B : ID3のメジャーバージョンとリビジョン。ただしメジャーバージョンは実はマイナーバージョンの格納に使われている。なので、ID3v2.3の場合、メジャーバージョンには3が入る。リビジョンは、現状は0しか運用されていない。
    • 1B : ビットフラグ。このビットフラグの意味はID3のバージョンによって異なる。このビットフラグは、これ以降の構造の切り替えフラグにもなっている。
    • 4B : an integer。ただし、それぞれのByteのうち7bitsしかつかわない。これはタグのサイズを表す(headerは除く)。

  • v2.3では、この後に several extended headerが来ることができる。
  • その後は、framesがくる。framesにはいくつか種類があり様々な情報を格納できる。a frameの構造は、

    • a header から始まる。
    • a header は a string identifierとa sizeを含む。v2.3では、それに続き2Bのフラグがあり、はじめの1Bの値によっては、2つめの1Bは暗号方式の指定であり、the frameの残りの部分の復号に使用される。



あと、framesがいくつ含まれるかは読まないと分からないということ。framesの中身はnull bytesを含むことがあり、搬送している情報のサイズと同じとは限らないこと。などなど。

なるほど。Practicalな題材だ。


Defining a Package

この本、パッケージを利用するときは、エクポートするシンボルを初めから一覧にしちゃってるんですね。そこを足したり引いたりしながら作っていくようなプロセスも見せてもらえれば参考になるんですが。まあ自分でやれってことでしょう。


Integer Types

define-binary-typeの復習。

  • マクロ。
  • read-value methodとwrite-value methodを生成する。
  • それらはCLの組み込みclassesに対するもの。define-binary-classに対するものではない。
  • 次のものを入力として受けとる。

    • the name of the type
    • the &key parameters (the read-valueとthe write-valueメソッドが受け取る情報)
    • the code for reading from a stream
    • the code for writing to a stream

  • これらを受け取ったマクロは、総称関数であるread-value,write-valueにそれらを入力型とするメソッドを登録する。
  • もうひとつ、short formsというのもこのマクロは対応している。
  • それは、すでに定義済みのread-valueメソッドをもとに、より特定化されたread-valueを作るためのもの。
  • 例えば、(define-binary-type u1 () (unsigned-integer :bytes 1))とか。


このまんまで、u1〜u4は定義可能。しかし、ID3タグサイズは4Byteだが、それぞれのByteでは下位7bitsしか使わず、それによって、28bitsをあらわしている。これはLDBのsizeで対応すると。それを&keyで受けとるように調整して、u1〜u4とid3-tag-sizeを統一的に記述できると。なるほど、やっと前章の意味が理解できてきた。


String Types

特になし。


ID3 Tag Header

う、MP3のファイルをまったく持ってない。落とす。
奏サウンド
Foot Loose
日本語のものの曲名がiTunes上で文字化けしている。大丈夫なのかな。

ううむ。show-tag-headerは動くが、show-tag-headersが動かない。とりあえず放置。


ID3 Frames

もんもんとしてるだけで、頭に入らない。。。メモ。


  • id3-frameの定義にdefine-tagged-binary-classを使う。dispatch関数は、find-frame-classとする。

    (define-tagged-binary-class id3-frame ()
    ((id (iso-8859-1-string :length 3))
    (size u3))
    (:dispatch (find-frame-class id)))

  • framesの種類は、24はある。
  • そこでまずgeneric frame classから作る。
  • これで取り敢えずframeを読めるようになる。24種類あるけど実際に使われているのは数種類と予測されるのでそれを調べる。
  • generic frameはid3-frameのサブクラスとする。

    (define-binary-class generic-frame (id3-frame)
    ((data (raw-bytes :size size))))

  • raw-bytesは新しい型。単にan array of bytesとして情報を保持するだけ。定義する。

    (define-binary-type raw-bytes (size)
    (:reader (in)
    (let ((buf (make-array size :element-type '(unsigned-byte 8))))
    (read-sequence buf in)
    buf))
    (:writer (out buf)
    (write-sequence buf out)))

  • find-frame-classはとりあえず'generic-frameのみ返すようにしておく。

    (defun find-frame-class (id)
    (declare (ignore id))
    'generic-frame)

  • ここまで整備できたので、id3-tagにid3-framesというbinary typeを追加したい。
  • これにはframe領域にあらわれるpadding bytesの処理の実装が必要。
  • id3-framesは、paddingは読みとばし、frame objectsをみつけたら生成するという役割を担う。

    (define-binary-type id3-frames (tag-size)
    (:reader (in)
    (loop with to-read = tag-size
    while (plusp to-read)
    for frame = (read-frame in)
    while frame
    do (decf to-read (+ 6 (size frame)))
    collect frame
    finally (loop repeat (1- to-read) do (read-byte in))))
    (:writer (out frames)
    (loop with to-write = tag-size
    for frame in frames
    do (write-value 'id3-frame out frame)
    (decf to-write (+ 6 (size frame)))
    finally (loop repeat to-write do (write-byte 0 out)))))

    ※(size frame)はアクセサーによるアクセス。


Detecting Tag Padding

read-frameを作る。ここで、condition systemを使う。これはunwindさせるので、exceptionと同じですね。


Supporting Multiple Versions of ID3
Versioned Frame Base Classes
Versioned Concrete Frame Classes
What Frames Do You Actually Need?
Text Information Frames
Comment Frames
Extracting Information from an ID3 Tag

後半は、特になし。前半を理解するのに時間がかかりました。前半は、前章を復習しながらナントカでした。
tagged binaryってどの程度使われているのでしょうか。名前からしてTIFFとかはそうかもしれません。

2008年1月19日土曜日

Chapter 24 Practical: Parsing Binary Files

前説

特になし。


Binary Files

バイナリは、コンパクトさと効率が利点とされている。この利点を実現するには、on-disk structuresが簡易にメモリ上のlisp objectsにマッピング可能でなければならない。逆もしかり。


Binary Formats Basics

LDBの説明がわからない。HyperSpecで調べると、byte specifiersは、(byte s p)で、sがbyteのsizeをbitで定義、pがbitポジションの値だそうな。テスト。

CL-USER> (ldb (byte 1 0) #b01)
1
CL-USER> (ldb (byte 1 1) #b01)
0
CL-USER> (ldb (byte 1 2) #b1001)
0
CL-USER> (ldb (byte 1 3) #b1001)
1
CL-USER> (ldb (byte 2 0) #b1001)
1
CL-USER> (ldb (byte 2 1) #b1001)
0
CL-USER> (ldb (byte 2 2) #b1001)
2
CL-USER>

なる。bitポジションに移動して、そこの値をsizeに従って読むのか。


Strings in Binary Files

メモ。

  • character code : positive integers と characters の マッピングを定義。マッピングの中のそれぞれのpositive integerのことをa code point と呼ぶ。
  • character encoding : code pointsをバイト列で表現する方法を定義する。


Composite Structure
Designing the Macros
Making the Dream Reality

特になし。


Reading Binary Objects

そうか、generic functionsはマクロで関数の名前が衝突しないためにも有効なんだ。


Writing Binary Objects

特になし。


Adding Inheritance and Tagged Structures
Keeping Track of Inherited Slots
Tagged Structures
Primitive Binary Types
The Current Object Stack

特になし。

この章も楽しめました。マクロとCLOSがあいまって、汎用コードを構築していく様が圧巻でした。しかし、CLプログラミングがこういうことだとすると、CLOSとマクロについて本当に理解して使いこなしていないと、CLならではのプログラミングはできんですね。。。

Chapter 23 Practical: A Spam Filter

著者が配布しているソースは、asdfでパッケージ管理されている。
asdfの簡単なメモ。



ここにきて、ひとつ疑問。
CLのプログラムの起動方式ってどういう種類があるんだろう。ACLでは、例えばSLIMEで開発をして、"Application"としてまとめるようだ(含むランタイム)。ということは、日頃はSLIMEに住むということなのだな。

著者提供のソースを動かしてみる。

  • ソースDL。展開。
  • /.../systemsをasdf:*central-registry*に登録。
  • 全てのasdのシンボリックリンクを/.../systems/に作成。
  • REPLで、(asdf:operate 'asdf:load-op :spam)
  • うまくいったようだ。
  • REPLで、(in-package :com.gigamonkeys.spam)しておく。



前説

特になし。


The Heart of a Spam Filter

  • internって言葉、アプリ書くときにも、こんな風に使う感じなんだ。



Training the Filter

特になし。


Per-Word Statistics

メモ。この本この部分の説明はかなり雑というか、ヘタクソではないか。ちょっとイライラする。


  • 基本方針は次のとおり。メッセージを、それが含むfeaturesにしたがって分類する。そのとき、個別のfeatureについて、それを含むメッセージがspamである確率を算出し、それら全てを結合したものをそのメッセージのスコアとする。
  • では、あるfeatureを含むメッセージがspamである確率をどう計算するか。
  • 第一案:

    (defun spam-probability (feature)
    (with-slots (spam-count ham-count) feature
    (/ spam-count (+ spam-count ham-count))))

  • 問題あり。これだと、そのfeatureのspam-countとham-countだけに依存している。例えば、spam-countが1で、ham-countが9であっても、次の2つの状況によって、その確率は異なるべきだ。

    状況1
    *total-spams* 100
    *total-hams* 100

    状況2
    *total-spams* 10
    *total-hams* 1000

    spam-countについてくらべると、同じ1であっても、状況1よりも状況2の方がspamである確率は高いはず。また、ham-countでいえば、同じ9であっても状況1よりも状況2の方がhamである確率は低いはず。そこで、、
  • 第二案:

    (defun spam-probability (feature)
    (with-slots (spam-count ham-count) feature
    (let ((spam-frequency (/ spam-count (max 1 *total-spams*)))
    (ham-frequency (/ ham-count (max 1 *total-hams*))))
    (/ spam-frequency (+ spam-frequency ham-frequency)))))

  • 第二案ではfrequency(頻度)という形で総数の影響を組み入れた。
  • しかし、問題あり。2000メッセージを取り扱った結果、spamとhamが半々だったとする。あるfeatureはspam-count = 1000かつham-count = 0だったとする。別のfeatureは、spam-count = 1 かつ ham-count = 0だったとする。するとそれぞれのspam-probabilityは、どちらも1である。後者の方がspamである可能性は低くあるべきだ。そこで、これを補正する方法を考える。第三案ではなく補正。
  • 補正:

    (defun bayesian-spam-probability (feature &optional
    (assumed-probability 1/2)
    (weight 1))
    (let ((basic-probability (spam-probability feature))
    (data-points (+ (spam-count feature) (ham-count feature))))
    (/ (+ (* weight assumed-probability)
    (* data-points basic-probability))
    (+ weight data-points))))



Combining Probabilities
Inverse Chi Square

統計学のお話。読むだけ。


Training the Filter
Testing the Filter
A Couple of Utility Functions
Analyzing the Results
What's Next

特になし。

うーん。平日はほとんど進められなかった。。。

2008年1月15日火曜日

後半戦 Practicalな章に進むにあたって

まず、残念ながら? 平日になってしまったので、仕事優先、よくて一日一章になるかもだが気にしないこと。そこはあせらないようにすること。

次に、Practicalになるとそれぞれが取り扱っているドメインの情報をどれだけ知っているかで、理解のスピードも深さもまったく違ってくると予想される。本当はそれらのドメインを勉強しながらじっくりやりたいのですが、さすがにそれでは時間がかかりすぎるので、CLの書きぶりとして著者がいいたいことが理解できればよしとすることにします。

ID3とかMP3ってまったく知らないので、そこが恐怖です。

Chapter 22 LOOP for Black Belts

LOOPが好きになれるかどうか、が読みどころ。
CLの言語コアよりも上の部分の各種機構って、とどのつまりはミニ言語というかDSLだと思うのですね。ミニ言語やDSL自体を否定していたら、常に言語コアのみで書かないといかんとなってしまう。なので、DOとかはS式っぽいDSLで、拡張LOOPはS式っぽくないということだけだと思うのです。でも何故か、LOOPを見てると不安になるんですよね。それがどこからきてるのか。


前説

特になし。


The Parts of a LOOP

お、さすがに箇条書きをはじめた。この著者が箇条書きにするってことは、LOOPってやっぱりそういうものなんですね。


Iteration Control
Counting Loops
Looping Over Collections and Packages

特になし。


Equals-Then Iteration

この章かなりごつい。。。とにかく例をやってみて理解していくことにする。


CL-USER> (loop repeat 5
for x = 0 then y
for y = 1 then (+ x y)
collect y)
(1 2 4 8 16)
CL-USER> (loop repeat 5
for y = 1 then (+ x y)
for x = 0 then y
collect y)
(1 1 2 4 8)
CL-USER> (loop repeat 5
for x = 0 then y
and y = 1 then (+ x y)
collect y)
(1 1 2 3 5)
CL-USER>

この違いは? ああそうか、どの時点のx,yを参照するかの違いなのか。forの評価はそれぞれ独立して逐次なので、後ろのforの中は前のforの評価結果(今回ループ)を参照可能。andはそれを揃えるときに使うと。


Local Variables

withの例がない。うーむ。
説明を読む限り、補助変数として名前をもった方が記述が楽なときに使用するもの。


Destructing Variables

CL-USER> (defun hoge (list)
(loop for cons on list
do (format t "~a" (car cons))
when (cdr cons) do (format t ", ")))
HOGE
CL-USER> (hoge '(1 2 3 4 5))
1, 2, 3, 4, 5
NIL
CL-USER> (defun piyo (list)
(loop for (item . rest) on list
do (format t "~a" item)
when rest do (format t ", ")))
PIYO
CL-USER> (piyo '(1 2 3 4 5))
1, 2, 3, 4, 5
NIL
CL-USER>

これはたしかにhandy。
読みやすいし、書きやすい。


Value Accumulation

う。なんか便利すぎてくやしい。


Unconditional Execution

-esqueって、〜風とか、〜っぽいって意味なんですね。


Conditional Execution


CL-USER> (loop for i from 1 to 100
if (evenp i)
minimize i into min-even and
maximize i into max-even and
unless (zerop (mod i 4))
sum i into even-not-fours-total
end
and sum i into even-total
else
minimize i into min-odd and
maximize i into max-odd and
when (zerop (mod i 5))
sum i into fives-total
end
and sum i into odd-total
do (format t "~{~a; ~}~%" (list
min-even
max-even
min-odd
max-odd
even-total
odd-total
fives-total
even-not-fours-total)))
0; 0; 1; 1; 0; 1; 0; 0;
2; 2; 1; 1; 2; 1; 0; 2;
2; 2; 1; 3; 2; 4; 0; 2;
2; 4; 1; 3; 6; 4; 0; 2;
2; 4; 1; 5; 6; 9; 5; 2;
2; 6; 1; 5; 12; 9; 5; 8;
2; 6; 1; 7; 12; 16; 5; 8;
2; 8; 1; 7; 20; 16; 5; 8;
...


改造版。さすがにSLIMEの自動インデントは対応してなかった。


Setting Up and Tearing Down

う、Perl難しい。。。


Termination Tests

特になし。


Putting It All Together

特になし。


著者が言っている「繰り返しにはパターンがあり、それはDSL構築に値する」というのはその通りだと思います。あと、通常の英語っぽくすることが、拡張LOOP設計者の狙いだったとのことです。読んでみて、私の整理はこんな感じです。


  • 繰り返しを表現する英語の語彙や構文は、自然言語にしては、なかなかアルゴリズムの表現に適したものであり、
  • だったらそれをそのまま文法にしてDSLをつくるのがいいじゃん。というノリか。それはアリだと思う。
  • これが日本語ではたぶん無理。
  • しかし自然言語の語彙と構文をもってきてるから、そっちの知識も頭にあるので慣れるまではノイズがでる。
  • これが不安感。
  • たぶん、ノイズの度合いは、英語が日常の言語の人と、私のように日本語が日常の言語の人ではずいぶん違うはず。
  • 個人的には繰り返しを再帰で書くのは嫌いではない。
  • しかし、再帰の実戦的なメリットは、繰り返しが書けるということではなくて、関数を宣言的に書けるということの場合が多いと思う。

2008年1月14日月曜日

Chapter 21 Programming in the Large: Packages and Symbols

前説

名前の衝突の問題は、CLでは、the readerの振舞の問題となる。テキスト上の名前をsymbolsに変換するのは、the readerの役目。そのときに、同じ名前を同じsymbolにするか違うsymbolにするか、ということ。


How the Reader Uses Packages

メモ。


  • packagesはそれぞれ名前を持つ。
  • FIND-PACKAGEで、packagesの名前検索ができる。
  • *PACKAGE*はa packageの名前をもち、それをthe current packageと呼ぶ。
  • FIND-SYMBOLは、a name と a package nameを引数にとり、そのnameに対応したsymbolが存在する場合はそれを返し、存在しない場合はNILを返す。
  • INTERNは、FIND-SYMBOLと似たようなものだが、存在しないときには、そのnameに対応したsymbolをそのpackageに生成する。
  • プログラマが使用する名前のほとんどは、unqualified(無資格? 無修飾? 無制限?)である。具体的には、コロンをひとつも含んでいない。
  • その場合、the readerが「テキストの a name を読む」とは、それのエスケープ文字を変換して、全体を大文字にして、INTERNに渡すということだ。
  • コロンを含んでいる名前を a package-qualified name と呼ぶ。(修飾/制限だ)
  • the readerは、a package-qualified name を読むと、それをコロンを区切りとして分解する。それぞれの部分をa package nameやa nameと解釈する。
  • 1コロンは、an external symbolを指すものであり、externalであるということは、元packageがそれをexportsしているということ。存在しなかったり、externalでなかったりすると、the readerがエラーを上げる。
  • 2連続コロンは、おお!強制介入。externalであるかにかかわらず、そのsymbolがそのpackageに存在するなら返却される。
  • keyword symbolsとは、コロンから始まるもののこと。これらは、KEYWORDという名前のpackageにINTERNされて、自動的にexportされる。さらに、同時に、そのsymbolと同じ名前と値のa constant variableを定義する。
  • uninterned symbolsは、頭に#:をつけたもの。どのpakageにもINTERNされない。それを読むたびにthe readerは異なるsymbolを生成する。



A Bit of Package and Symbol Vocabulary

メモ。


  • FIND-SYMBOLを使ってあるpackageについて取得可能なsymbolsを、"accesible in that package"と言う。これは、対象としているpackageが current packageであるときに、unqualified nameで参照可能なものということ。
  • この内訳は2つになる。
  • ひとつは、the readerがそのpackageのname-to-symbolテーブルにinternしたもの。それは、"present in the package"と言う。
  • もうひとつは、そのpackageが他のpackageのsymbolsをinheritsしたもの。inheritsするということは、"using the other package"と言う。ただし、the used package自身がexportsしているsymbolsだけがinheritsされる。このようにexportsには2つの効果がある。inheritsされるということと、package-qurlified names(コロン1つ型)で参照可能になるということ。
  • このinheritanceにおいて名前衝突がおこった場合は、そのうちのひとつを a shdowing symbolにすることによって、name-to-symbolの対応を単一にして解消できる。
  • useとは別にimportがある? (頭がクラクラしてきた。。。)
    だめだ、概念を文章だけで説明されてもなんだかわからない。この後の節の例をみて理解しよう。
  • a present symbolはuninternできる。uninterned symbolのPRINTは#:記法となる。



Three Standard Packages
Defining Your Own Packages
Packaging Reusable Libraries

特になし。


Importing Individual Names

importは個々のsymbolに対してなんですね。


Packaging Mechanics
Package Gotchas

特になし。

この章も楽しめました。

Chapter 20 The Special Operator

前説

特になし。
しかし、この章、ごつそうだ。

Controlling Evaluation

1. QUOTE
2. IF
3. PROGN

特になし。


Manipulating the Lexical Environment

4. LET
5. LET*
6. SETQ
7. FLET
8. LABELS
9. MACROLET
10. SYMBOL-MACROLET
11. FUNCTION

ためしにWITH-SLOTSを展開してみると、、、

CL-USER> (macroexpand-1 '(with-slots (x y z) foo (list x y z)))
(LET ((#:G163 FOO))
(DECLARE (SYSTEM::VARIABLE-REBINDING FOO #:G163))
#:G163
(LIST (SLOT-VALUE #:G163 'X) (SLOT-VALUE #:G163 'Y)
(SLOT-VALUE #:G163 'Z)))
T
CL-USER>

本と違う。SYMBOL-MACROLETまで展開されてるのかな。ま、mightだし、いいや。
SYMBOL-MACROLETの説明がよくわからん。HypeSpecのはわかりやすい。(HyperSpec)


Local Flow of Control

12. BLOCK
13. RETURN-FROM
14. TAGBODY
15. GO

特になし。


Unwinding the Stack

16. CATCH
17. THROW
18. UNWIND-PROTECT

特になし。


Multiple Values

19. MULTIPLE-VALUE-CALL

誰が何する人か整理。

  • VALUES : function : 複数の値を引数に取り、多値として返す。
  • VALUES-LIST : function : 単一のリストを引数に取り、多値として返す。
  • MULTIPLE-VALUE-CALL : special operator : subformをそれぞれ全て評価した上で、多値含めてそれらを全部引数としてfunctionに渡す。
  • MULTIPLE-VALUE-BIND : macro : 多値を変数に格納する。
  • MULTIPLE-VALUE-LIST : macro : 多値をリストにする。



EVAL-WHEN

20. EVAL-WHEN

頭にすっと入らない。思うに、英語力が低いからすっと入らないだけで、この本の記述自体は簡明なのだろう。
いずれにしても、メモを取りながら。。。

  • LOADは、ファイルをloadして、それのtop-level formsを全て評価する。
  • COMPILE-FILEは、a source fileをa FASL fileにコンパイルする。
  • (load "foo.lisp")と(load "foo.fasl")は等価である。
  • LOADは、ファイルの前の方にあるformsは、後ろの方のformsの評価に影響を与える。
  • COMPILE-FILEは、通常は、formsを評価しない。評価が発生するのは、FASLがLOADされるときである。
  • しかし、一部のformsは、COMPILE-FILEの処理のときに評価しないと、(load "foo.lisp")と(load "foo.fasl")の等価性を確立できない。
  • それは、例えば、IN-PACKAGEやDEFMACROである。
  • EVAL-WHENを使うと、specific bits of codeが何時評価されるかを指定できる。



Other Special Operators

21. LOCALLY
22. THE
23. LOAD-TIME-VALUE
24. PROGV

一個足りない。。。と思ったら脚注に。

25. MULTIPLE-VALUE-PROG1

特になし。

Chapter 19 Beyond Exception Handling: Conditions and Restarts

前説

high,medium,lowの対処のところがわからない。メモしながら。


  • low失敗そしてmediumがリカバーできず、だと、ボールはhighに。highはどうする?
  • 作戦1:mediumを使わずにやるべきことをやる。
  • 作戦2:成功するようにmediumの使いかたを変える。
  • 作戦1はクリーン。だけど、コーディングが増える。
  • 作戦2はいかがなものか。それができるということは、highがmediumとlowの内部処理について知っているということであり、callerからみてcalleeはブラックボックスになっているという関数抽象化の基本とバッティングしている。



The Lisp Way

ああ、たぶん、REPLでエラったときに、SLIMEがどうしますか〜と聞いてくるメニューの仕組みなんだな。


Conditions

この、Condition classes と 通常のクラスの間の微妙な違い、嫌だなぁ。覚えることが無用に増える。


Condition Handlers

condition handlerのところメモ。

  • malformed-log-entry-errorが投げられたときに、何が起きるかは、call stack 上で、そのparse-log-entryより上にいるものによる。
  • deguggerに行くのをさけるには、その上にいるもののどれかで、a condition handlerを確立しなければならない。
  • a condition が 発信されたとき、シグナル機構はa list of active condition handlersを調べる。
  • a condition handler は、それが取扱うconditionのspecifierと、そのconditionを唯一の引数とする関数からなる。
  • 確立されたcondition handlersが複数ある場合、シグナル機構は、確立したのが直近の順に選択する。
  • handlersの関数が何もせずにリターンすれば、それは拒否をあらわし、制御はSIGNALに返る。
  • a handlerが処理を担当する場合は、nonlocal exitによって、制御をSIGNALからthe handlerに移す。
  • たいていの場合は、call stackを自分のところまで巻き戻すだけ。
  • HANDLER-CASEマクロはこの類のcondition handlerを作る。

あり? これでは普通のExecption機構とほとんど同じでは??
ああ、"JAVA-STYLE EXCEPTON HANDLING"というところにそう書いてあった。orz
このEXCEPTONは脱字。errataに出てなかったので送っといた。:)


Restarts

ここで看板通りの機構になった。
しかし、よくできてる。他の言語はなんで採用しないんだろう?


Providing Multiple Restarts
Other Uses for Conditions

特になし。

2008年1月13日日曜日

Chapter 18 A Few FORMAT Recipes

あせらず、じっくりやりたいのですが、ちょっとじれてきてもいます。一日一章で一ヶ月。やっぱり長いです。ちょっと加速してみようと思います。加速しても理解の深さは今までと同レベルをキープします。もしかしたら、ペースを崩して墓穴を掘るかもしれません。。。


前説


  • LOOP利用例をFORMAT利用例で焼き直しても。。。嫌われものどうしで比較してどうする。
  • 試す。

    CL-USER> (defun hoge (list)
    (loop for cons on list
    do (format t "~a" (car cons))
    when (cdr cons) do (format t ", ")))
    HOGE
    CL-USER> (hoge '(1 2 3 4))
    1, 2, 3, 4
    NIL
    CL-USER> (defun piyo (list)
    (format t "~{~a~^,~}" list))
    PIYO
    CL-USER> (piyo '(1 2 3 4))
    1,2,3,4
    NIL
    CL-USER> (defun puyo (list)
    (do ((rest list (cdr rest)))
    ((null rest) t)
    (if (cdr rest)
    (format t "~a, " (car rest))
    (format t "~a" (car rest)))))
    PUYO
    CL-USER> (puyo '(1 2 3 4))
    1, 2, 3, 4
    T
    CL-USER>


まあ、出力したいだけなんだから、それをFORMATにまとめちゃうというのは私的にはありです。LOOPは、まだ気持ち悪いのですが。


The FORMAT Function

特になし。


FORMAT Directives

この本、語り部口調が売りだと思うのですが、この際箇条書きで書いてもらった方がわかりやすいと思うところもあるんだよね。。。

メモをとりながら。

  • 全てのdirectivesは次の形となる。
    ~(prefix parameters)(a character)

    • (a character)がdirectivesの本体というか名前。
    • (prefix parameters)は無いこともある。
    • (prefix parameters)は複数のパラメータの場合もある。その場合は、それらをカンマで区切る。
    • (prefix parameters)のパラメータは、具体的な数でもよいし、外から渡してもよいし、#のようにdirectiveの形から算出されるものもあるし、:や@のように出力形式を指定するものもある。
    • :と@の場合は、:@とすると両方の出力形式が採用される。形式としてバッティングする場合は、新しい出力形式が定義されている場合もあるし、何が出力されるかわからん場合もある。



Basic Formatting
Character and Integer Directives
English-Language Directives
Conditional Formatting

特になし。


Iteration

  • おお、~^は脱出なんだ。
  • "~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}"。ウォーー


Hop, Skip, Jump
And More ...

特になし。

2008年1月12日土曜日

Chapter 17 Object Reorientation: Classes

前説

  • built-in classes には、subclassをつくることができない。



DEFCLASS

  • DEFSTRUCTは、backward compatibilityのためにあるといってよい。



Slot Specifiers

  • slotsがだぶっていた場合は、unionになるんだ。その合併ルールはなんだろ。



Object Initialization

特になし。


Accessor Functions

SETFのところが、すっと入らないのでメモ。

  • a SETF function は、SETF(macro)を拡張するひとつの方法。
  • "The name of a SETF function is a two-item list ...)" ここが理解できない。。。
    SETF functionはDEFUNで定義するもんなんだから、その作りをどうしようが作り手の勝手ではないか?
    後段ででてくるDEFCLASSが自動的に作るものの説明とごっちゃになっているような気がする。



WITH-SLOTS and WITH-ACCESSORS
Class-Allocated Slots
Slots and Inheritance

特になし。


Multiple Inheritance

ここもメモをとりながら。


  • 複数のdirect superclassesをもてることは、the effective methodの構築とslot specifiersのマージを複雑にする。
  • DEFCLASSのdirect superclass listにて、左側のsuperclass程、more specificであるというルール。
  • それにしたがって、a linear class precedence listをつくる。class precedence listは、そのclassから見たとき、というものである。classの統一的な系統図ではない。あくまでそのclassから見た優先リストということ。
  • the same generic functionについて、複数のsuperclassで定義されているときにclass precedence listは役に立つ。
  • 例えば、

    (defclass money-market-account (checking-account savings-account) ())
    (money-market-account
    checking-account
    savings-account
    bank-account
    standard-object
    t)

    という状況にて、print-statementというジェネリック関数がcheck...とsaiving...の双方に定義されている場合。
  • 何もしないと、money-market-accountのprint-statementは、check...のものになる。ここれ class precedence listが使われている。
  • money-market-accountのprint-statementの振舞を変更するにはいろいろなやり方がある。
  • ひとつめ。money-markege-account用に、print-statementを書く。
    この方法の場合、check...はCALL-NEXT-METHODで呼べるが、saving...のとかを呼ぶ方法はない。そのコードを使おうと思ったら、saving...の内容を別関数にしておかねばならない。
  • ふたつめ。print-statementは全部CALL-NEXT-METHODにしておく。そうすると、money-market-accoutから見たclass precedence listでは、check...とsaving...が上のように並ぶので、両方呼ばれる。ただし、これはCALL-NEXT-METHODを書いときましょう、というコーディング規約の遵守が必要となる。
  • みっつめ。補助メソッドを使う。bank-account に対して、print-statementをprimaryとして定義する。check...とsaving...については、auxiliary(:after)としてそれぞれの処理を定義しておく。すると、money...に対するとprint-statementの呼び出しは、これら全てを実行する。


Good Object-Oriented Design

そうか、OO設計を学ぶには6ヶ月は覚悟が必要、なのか。
ただ、OOの本ってJavaで書かれているものが多いから、そのまえにJavaを読めるくらいになっとかないといけないかも。それも数ヶ月はかかりそう。無理かも。

Chapter 16 Object Reorientation: Generic Functions

前説

特になし。


Generic Functions and Classes

  • object orientation の根本アイデアは、「まずデータ型を定義すること、それに続きその型に関係する操作を定義することであり、プログラムを系統立てるパワフルな手法である」ということ。(まあ、そうなのですが、このレベルではOOとADTの違いがないですね)
  • CL(OS)の特徴をいくつか。

    • class-based。Classesは、objectsのtaxonomyと位置付けられる。
    • taxonomyの根はひとつ。T。
    • multiple inheritance。
    • message-passingの記法は取らない。CLでやると、(send object 'foo)などとなるが、こうすると高階関数などと相性が悪いから。
    • そのため、(foo object)という記法を採用した。
    • このfooを統合することにした。それは新種の関数で、a generic function と呼ぶ。



Generic Functions and Methods


  • "A generic function is generic in the sense that it can--at least in theory--accept any objects as arguments." それだけなら、呼び名はただの関数で済んじゃうのでは。



DEFGENERIC

預金関係の用語。

  • checking account : 当座預金
  • savings account : 貯蓄預金
  • withdrawal : 引き出し
  • balance : 残高
  • overdraft : 当座貸越し

日本では手形を市民生活の中ではほとんど使わないので、この例は日本の人にはわかりにくいような。当座預金は、日本では、まあ法人しか使っていない。それとも、ITのプロなら業務システムの基礎として簿記は知ってて当然というスタンスか。


DEFMETHOD

  • CALL-NEXT-METHODのnextって、どういうルールで確定するんだろ?
  • the EQL specializerは、the methodが評価されたときに固定され、その後に*account-of-bank-president*を変更しても反映されない。



Method Combination

最近は条件付きパターンマッチングをもった言語が増えてるから、generic functionは著者が心配するほど違和感ないのでは。
effective method の構築にあたって、EQL specializers 同士の比較アルゴリズムについて、特定に使っているparametersが同じ場合どうするんだろ。

そうか、EQL specializersは、オブジェクトが一意に特定される形でしか使えないんだ。ならばそれは常に、most-specificかつunique。でもslotsのこの値がnilだったら、とか書けるのも記述力が高いと思う。代替手段はいろいろあるので、たいしたことじゃないか。

The Standard Method Combination
Other Method Combinations

特になし。

Multimethods

うーん。Javaを知らないので、Javaの例が理解できない。Javaもいつか勉強せねば。

To Be Continued ...

特になし。

この章を読んでみて。
CL(OS)は、OOよりADTに近いと思いました。OOとADTは、双子だけど右利きと左利き、くらいなんで、あんまり意味ないんですが。

2008年1月11日金曜日

Chapter 15 Practical: A Portable Pathname Library

前説
The API

特になし。


*FEATURES* and Read-Time Conditionalization

  • read-time conditionalization とは、処理系毎に異なるコードを走らせることによって、総体としては処理系に渡ってポータブルなコードとなる機構である。
  • *FEATURES*を評価していみると、

    CL-USER> *FEATURES*
    (:ALLEGRO-CL-EXPRESS :ALLEGRO-CL-TRIAL :IPV6 :ACL-SOCKET :HIPER-SOCKET
    :PROFILER :MULTIPROCESSING :FLAVORS :LITTLE-ENDIAN :GSGC :COMPILER
    :USE-STRUCTS-IN-COMPILER :CLOS :DYNLOAD :DLFCN :UNIX :LINUX :REDHAT9
    :LINUX86 :X86 :VERIFY-STACK :VERIFY-CAR-CDR :ENCAPSULATING-EFS
    :RELATIVE-PACKAGE-NAMES :MODULE-VERSIONS :IEEE :IEEE-FLOATING-POINT
    :CONFORMING-IEEE :ICS :COMMON-LISP :ANSI-CL :DRAFT-ANSI-CL-2 :X3J13
    :ALLEGRO :EXCL :FRANZ-INC :ALLEGRO-VERSION>= :ALLEGRO-VERSION=
    :NEW-ENVIRONMENTS :SYMBOL-VALUE-VECTOR :PROCESS7 :USE-THREAD-LIBS
    :DYNLOAD-ACL-LIBRARY :ALLEGRO-V8.1 :TLSVALUES :TLSBNP :SSL-SUPPORT)
    CL-USER>

    UbuntuなのにREDHAT9と出ているところが気になる。
    別のシステムでも取ってみる。

    CL-USER> *FEATURES*
    (:ALLEGRO-CL-ENTERPRISE :IPV6 :ACL-SOCKET :HIPER-SOCKET :PROFILER
    :MULTIPROCESSING :FLAVORS :LITTLE-ENDIAN :GSGC :COMPILER
    :USE-STRUCTS-IN-COMPILER :CLOS :DYNLOAD :DLFCN :UNIX :LINUX :AMD64
    :LINUX86-64 :X86-64 :ENCAPSULATING-EFS :RELATIVE-PACKAGE-NAMES
    :MODULE-VERSIONS :IEEE :IEEE-FLOATING-POINT :CONFORMING-IEEE :ICS
    :COMMON-LISP :ANSI-CL :DRAFT-ANSI-CL-2 :X3J13 :ALLEGRO :EXCL
    :FRANZ-INC :ALLEGRO-VERSION>= :ALLEGRO-VERSION= :NEW-ENVIRONMENTS
    :SYMBOL-VALUE-VECTOR :PROCESS7 :DYNLOAD-ACL-LIBRARY :ALLEGRO-V8.1
    :64BIT :TLSVALUES :TLSBNP :SSL-SUPPORT)
    CL-USER>

    逆にこっちは、CentOSなのだがREDHATはない。。。
    まあ、*BSDと言われているわけではないので深掘りしない。

パッケージのところがわかりにくい。メモをとりながら。

  • どのようなパッケージが存在するかは、処理系毎に異なる。
  • *FEATURES*に含まれるsymbolsは、通常keywordsである。
  • feature expressionsを読んでいる間は、the readerは、KEYWORDパッケージに*PACKAGE*をbinds(する)。
  • "Thus, a name with no package qualification will be read as a keyword symbol." なんじゃこりゃ?

うーん。何がいいたいのか。パッケージの章をやった後、再度考えることにする。


Listing a Directory

この本、その場での説明なしにCLの関数を利用することが増えてきた。
そこで、SLIMEのC-cC-dhでhyperspecを参照することにする。
デフォルトでは、mozillaを立ち上げようとするので、w3mに変更する。

(setq browse-url-browser-function 'w3m-browse-url)
(autoload 'w3m-browse-url "w3m" "Ask a WWW browser to show a URL." t)
(global-set-key "¥C-xm" 'browse-url-at-point)
(defadvice browse-url-at-point
(around change-browse-url-browser-function activate compile)
(let ((browse-url-browser-function
(if (eq major-mode 'w3m-mode)
'browse-url-netscape
'w3m-browse-url)))
ad-do-it))

SLIMEがもっている調査機能はここ。
http://common-lisp.net/project/slime/doc/html/Documentation.html#Documentation

うーん、別frameにw3mが立ち上がるようにしたい。elispをいつか勉強するということで。


Testing a File's Existence

  • labelsはlocal functions and macrosを定義。(HyperSpec)

Chapter 14 Files and File I/O (Continued)

このあたり、いまいちすっとはいってこない。
メモをとりながら読むことにする。

FIlenames


  • pathname objects は representation of filenames である。
  • pathname objects を pathnames と略す。
  • namestrings は システムローカルのシンタックスによるfilenamesのことである。
  • OPENによって返されるstreamsもrepresentaion of filenames である。
  • これら3つを総称して、pathname designators と呼ぶ。'
  • 組み込み関数でfilenamesを引数にとるところは全て、この3つのタイプのどのpathname designatorでも適用可能である。
  • namestrings と pathnames を相互変換する関数がいろいろ用意されている。


How Pathnames Represent Filenames


  • pathnames は a structured ojbectである。
  • 6つのcomponentsから成る。それは、host, device, directory, name, type, version である。
  • プラットフォームによっては、不必要なcomponentもある。
  • PATHNAME関数によって、a namestring を a pathname に変換できる。
  • もちろん、namestringsのみならず、pathname designatorsならなんでも入力として利用できる。
  • the language standardでは、namestrings から pathnamesへの変換は定義されていないが、ほとんどの処理系は実装している。
  • pathnamesのread syntaxは、#p"..."である。
  • pathname designatorsをnamestringsに変換するには、NAMESTRING関数を用いる。


Constructing New Pathnames
Two Representations of Directory Names
Interacting with the File System
Other Kinds of I/O

特になし。
しかし、CLが仕様化された当時の事情はわかるのですが、その後、どこかのタイミングで仕様をアップデートして、これらの混乱を整理することはできなかったのでしょうか。

2008年1月10日木曜日

Chapter 14 Files and File I/O

前説

特になし。

Reading File Data

SLIMEって、openでファイルを指定するとき、補完してくれるんだ。少し幸せな気持になった。

CL-USER> (let ((in (open "/var/log/messages" :if-does-not-exist nil)))
(when in
(format t "~a~%" (read-line in))
(close in)))
Jan 9 07:39:59 ubuntu syslogd 1.4.1#17ubuntu7.1: restart.
T
CL-USER>

の"/var/log/messages"のところ。


CL-USER> (let ((in (open "/var/log/messages" :if-does-not-exist nil)))
(when in
(loop for line = (read-line in nil)
while line do (format t "~a~%" line))
(close in)))
Jan 9 07:39:59 ubuntu syslogd 1.4.1#17ubuntu7.1: restart.
...
Jan 10 21:46:38 ubuntu -- MARK --
T
CL-USER>


doで書いてみる。

CL-USER> (let ((in (open "/var/log/messages" :if-does-not-exist nil)))
(do ((line (read-line in nil) (read-line in nil)))
((null line) (close in))
(format t "~a~%" line)))

READ-LINEが繰り返しているのが嫌ですが、普通に書けます。
LOOPをみて思うのは、読むのは簡単、だけど、書くのは怖い、ということです。
慣れの問題ですが、それ以外にもあるような。
抽象構文木っぽくないものを書くと、どのトークンがどういう意味なのか何なのか、何をしていいのかしちゃいけないのか、ということがわからなくて不安になるように思います。doの場合、構文は、

(do (variable-definition*)
(end-test-form result-form*)
statement*)

さえおさえれば、後は括弧で構文木を書いていけばいいやということでなぜか安心する(DOじゃなくて再帰ならさらに安心) 。しかし、Cライクな構文に慣れた人にとっては、繰り返しを書くのにいちいちDO的や再帰にするのは、ゲンナリ、という気がします。たかだか繰り返し表現でゲンナリされるのはもったいないからLOOP全面出し、という戦略がこの本にはあるのかもしれません。

そういう意味では、Schemeが再帰に繰り返しの基礎を置き、末尾再帰で効率確保というのは、それはそれでよいことですが、それは言語コアとしてということであって、ライブラリとしてLOOPのようなものを早めに前面に出した方が世渡り上手かもしれません。それはマクロのパワーの現れでもあるのですから。

あれ? 上のDO、LETいらないのでは。ああ、そうか。DOの変数定義は、前のイテレーションの値しか引けないから、lineの初期値に困るから、LETがあった方がいいですね。

う、仕事せねば。中断。
再開。

((a b) (c d))をreadすると、((A B) (C D))がprintされる。quoteはいらんのか?
そうか、トップレベルでREADを使うと、REPLじゃなくてRPLになるからいらんのか。(REの結果がREAD)


Reading Binary Data
Bulk Reads
File Output

特になし。


Closing Files

UNWIND-PROTECTの詳細は、Chapter 20で。


続く。

2008年1月8日火曜日

Chapter 13 Beyond Lists: Other Uses for Cons Cells

前説

データ構造を扱う関数群は、複雑なマクロを書くときに役に立つ。まあ、そりゃそうだ。


Trees

おお、a list of lists は list だけれど、それは、"a list" of lists だからなだけなんだ。
この本、読んでると、おお、と感心したことが、単に字句通りなことが多い。。。
認知障害が発生しているのかもしれないが、こうやって理解が深まっていくものと信じたい。

COPY-LISTとCOPY-TREEの違い。

CL-USER> (defparameter *a* '(1 2))
*A*
CL-USER> (defparameter *b* '(3 4))
*B*
CL-USER> (defparameter *c* '(5 6))
*C*
CL-USER> (defparameter *L* (list *a* *b* *c*))
*L*
CL-USER> *L*
((1 2) (3 4) (5 6))
CL-USER> (defparameter *LC* (copy-list *L*))
*LC*
CL-USER> *LC*
((1 2) (3 4) (5 6))
CL-USER> (defparameter *T* (copy-tree *L*))
*T*
CL-USER> *T*
((1 2) (3 4) (5 6))
CL-USER> (setf (car *a*) 10)
10
CL-USER> *L*
((10 2) (3 4) (5 6))
CL-USER> *LC*
((10 2) (3 4) (5 6))
CL-USER> *T*
((1 2) (3 4) (5 6))
CL-USER> (defparameter *b* 3)
*B*
CL-USER> *B*
3
CL-USER> *LC*
((10 2) (3 4) (5 6))
CL-USER> *T*
((1 2) (3 4) (5 6))
CL-USER> (setf *C* 'c)
C
CL-USER> *LC*
((10 2) (3 4) (5 6))
CL-USER> *T*
((1 2) (3 4) (5 6))
CL-USER> (setf (caar *L*) 100)
100
CL-USER> *LC*
((100 2) (3 4) (5 6))
CL-USER> *T*
((1 2) (3 4) (5 6))
CL-USER>

なる。


Sets
Lookup Tables: Alists and Plists
DESTRUCTURING-BIND

特になし。しかし、cons cellsを使ったデータ構造やそれらを対象とした関数の振舞を文章で説明されるのは、綾取りを文章で説明されるような感覚だ。

Chapter 12 They Called It LISP for a Reason: List Processing

前説
"There Is No List"

特になし。


Functional Programming and Lists

appendがシェアしているということ。

CL-USER> (let* ((a (list 1 2))
(b (list 3 4))
(c (append a b)))
(print a)
(print b)
(print c)
(setf (car b) 30)
(print b)
(print c))
(1 2)
(3 4)
(1 2 3 4)
(30 4)
(1 2 30 4)
(1 2 30 4)
CL-USER>

なんと。そういうものなのか。


"Destructive" Operations

同じような例があった。orz


  • destructive operationsには、二種類ある。for-side-effect と recycling である。
  • for-side-effectは、SETFとかVECTOR-PUSHとか。
  • これらはそもそも関数プログラミングしようとするときは使わない。
  • recyclingは、関数プログラミングの中でも使う。recylingは効率を上げるために使われる。
  • 上の例も、SETFを使用しないかぎり、オブジェクトをシェアしていても問題ない。
  • recyclingを利用するときは、それまでに使っていたobjectsはそれ以降使わないときがよい。


うーん。いちいち一つ一つの関数について、どうrecyclingしているかとかを知らねばならんのか。
嫌なら、recyclingな関数は使わない、効率を求めたいなら使え、ということかな。


Combining Recycling with Shared Structure

ああ、それでdestructiveな操作をしたらとにかくSETFしちまえってことなのか。

CL-USER> (defparameter *list* (list 4 3 2 1))
*LIST*
CL-USER> (sort *list* #'<)
(1 2 3 4)
CL-USER> *list*
(4)
CL-USER>

おお。なんだか、すごい。
CLのdestructiveとは、単にもとのobjectsを(代入のように)書き換えちゃいますよ、ということだけではなく、効率がいいようにバラバラにして使ってそのまんまですよってことなんですね。スカーだ。

List-Manipulation Functions
Other Structures

特になし。

2008年1月6日日曜日

Chapter 11 Collections

前説

Ontogeny recapitulates phylogeny.

固体発生は系統発生を繰り返す、例えば受精から出産までの胎児の成長過程(単純な細胞から人間として形成される過程)は、原始の細胞からシンプルな生物を経て人間にいたる系統進化の過程と同じということ。科学的な話ではなかったはずです。

ここでは、Lispの系統発生的過程を、個々のLisp教育過程でくりかえす、ということかな。


Vectors

"the effects of modifying literal objects aren't defined." がわからない。
#(1 2 3)などでベクターObjectを生成したときは、それを変更するとどうなるかは定義されていないのか?

CL-USER> (let ((x #(1 2 3)))
(setf (elt x 0) 10)
x)
#(10 2 3)
CL-USER>

aclでは問題ないようだ。ま、深掘りすることじゃないか。


Subtypes of Vector

fill-pointerを試す。

CL-USER> (make-array 5 :fill-pointer 0)
#()
CL-USER> (make-array 5 :fill-pointer 1)
#(NIL)
CL-USER> (make-array 5 :fill-pointer 2)
#(NIL NIL)
CL-USER> (make-array 2 :fill-pointer 2)
#(NIL NIL)
CL-USER> (make-array 2 :fill-pointer 3)
; Evaluation aborted.
CL-USER> (make-array 0)
#()
CL-USER> (make-array 0 :fill-pointer 0)
#()

なる。"it's the index of the next position"

:adjustableについて。

CL-USER> (defparameter *v* (make-array 2 :fill-pointer 0 :adjustable t))
*V*
CL-USER> (vector-push 1 *v*)
0
CL-USER> (vector-push 2 *v*)
1
CL-USER> (vector-push 3 *v*)
NIL
CL-USER> (length *v*)
2
CL-USER> (vector-push-extend 3 *v*)
2
CL-USER> (length *v*)
3
CL-USER> *v*
#(1 2 3)
CL-USER> (vector-pop *v*)
3
CL-USER> *v*
#(1 2)
CL-USER> (vector-push 3 *v*)
2
CL-USER> *v*
#(1 2 3)
CL-USER> (defparameter *v* (make-array 1 :fill-pointer 0 :adjustable t))
*V*
CL-USER> *v*
#()
CL-USER> (vector-push-extend 1 *v*)
0
CL-USER> *v*
#(1)
CL-USER> (vector-push 2 *v*)
NIL
CL-USER> *v*
#(1)
CL-USER>

なる。


Vectors As Sequences
Sequence Iterating Functions
Higher-Order Function Variants
Whole Sequence Manipulations

特になし。


Sorting and Merging

うーん。なぜdestructiveなのに、

(setf my-sequence (sort my-sequence #'string<))

とsetfする方がよいと言うのだ。納得いかない。


Subsequence Manipulations
Sequence Predicates
Sequence Mapping Functions
Hash Tables
Hash Table Iteration

特になし。

Chapter 10 Numbers, Characters, and Strings

前説
Numbers
Numeric Literals
Basic Math
Numeric Comparisons
Higher Math

ここまで、特になし。


Characters

#\あ

としたら、

Coding system iso-latin-1-unix not suitable for "000050(:emacs-rex (swank:listener-eval \" #\\\\あ
\") \"COMMON-LISP-USER\" :repl-thread 775)
"

だそうな。何か調整が必要なのかな。
SLIME経由じゃなければ、(shell、またはfi:lisp-mode)

CL-USER(3): #\あ
#\あ
CL-USER(4):

なので、SLIMEの設定と思われ。後日調べる。


Character Comparisons
Strings
String Comparisons

特になし。

Chapter 9 Practical: Building a Unit Test Framework

前説
Two First Tries
Refactoring
Fixing the Return Value
Better Result Reporting
An Abstraction Emerges
A Hierarchy of Tests
Wrapping Up

この章、楽しめました。特記事項はありません。
CLによるボトムアッププログラミングがみれて、参考になります。
しかし、トップダウンとボトムアップは共存できるのでしょうか。
大規模プログラミングにおいて、基本設計をトップダウンにして、サブシステムをボトムアップで行く、というのはいけそうです。
ではサブシステムの中で、個々のプログラミングをデザインレシピのようにトップダウンに行く方法とLISPのボトムアップとはどうなのでしょうか。
この章の場合は、トップダウンで個別問題を書き下した後、関数とマクロで抽象化をかけていく際に、その個別問題を解決するところまでがトップダウンの範疇で、そこから汎用的なフレームワークへと進むところがボトムアップになっていると感じました。

Chapter 8 Macros: Defining Your Own

前説

特になし。


The Story of Mac: A Just-So Story

"DEFMACRO"はちょっとウケた。


Macro Expansion Time vs. Runtime

特になし。


DEFMACRO

特になし。


A Sample Macro: do-primes

特になし。


Macro Parameters

特になし。


Generating the Expansion

backquoteがないとマクロ定義は難儀。


Plugging the Leaks

GENSYMが何故安全なのか?
#:がuninterned symbolsに対する通常記法とのことだが。
GENSYMしたときに、G2141はすぐにuniternされ、それを#:記法でアクセスしている、ということか?
Packageの章で確認することにする。


Macro-Writing Macros

once-only の分析はパス。

Chapter 7 Macros: Standard Control Constructs

前説

特になし。


WHEN and UNLESS


CL-USER> (defmacro my-when (condition &rest body)
`(if ,condition (progn ,@body)))
MY-WHEN
CL-USER> (macroexpand-1 '(my-when (> 3 2)
(+ 1 2)
(- 3 1)))
(IF (> 3 2) (PROGN (+ 1 2) (- 3 1)))
T

うーむ。",@"のスライシングは展開時に実施されるが、","による評価は実施されないのか。
いや、conditionを入力されたformに変更するのが","による評価か。


COND

特になし。


AND, OR, and NOT

特になし。


Looping

なんと。Lispの繰り返し構造は、goto 機構の上に全てマクロとして構築されている。(TAGBODY, GO)
DOがその上に構築されて、これが他の繰り返し方法の母。DOLIST、DOTIMESなどがあると。
で、LOOPは鬼子。ミニ言語でLISPっぽくないが、マクロのパワーを示すのにはよい例と。


DOLIST and DOTIMES

特になし。


DO

特になし。


The Mighty LOOP

Seasoned Lisper ではなく、DOの構文もさっき知ったばかりだが、

(do ((nums nil) (i 1 (1+ i)))
((> i 10) (nreverse nums))
(push i nums))

は普通に理解できる。
拡張LOOP自体は便利そう。

2008年1月4日金曜日

Chapter 6 Variables

前説

CLには、lexical な variables と dynamic な variables がある。

Variable Basics


  • 型は、valuesが運ぶ。型チェックはランタイムに実施。(dynamicaly typed)
  • 全ての型エラーは検知される。(strongly typed)
  • 少なくとも概念的には、全てのvaluesは、objectsへの参照である。なので、別の a value を a variable に設定するということは、もとのa value(であるところのa object)に影響を与えない。
  • mutable な objects については、それ自体を変更する方法もある。
  • 新しいvariablesを導入するには、DEFUNが使える。DEFUNはそれへの呼び出し毎に引数とvaluesのbindingsを生成する。
  • arguments = function parameters。
  • function parametersは他のvariablesと同じようにobject referencesを保持している。なので、functionのbodyにてfunction parametersに新しいvaluesを割り当てても、他のbindingsには影響はない。
  • しかし、functionに渡されたobjectsがmutableであるならば、bodyの中でそのobjectsを変更した場合、その変更はthe callerからは見える。the callerとthe calleeは同じojbectを参照しているから。
  • LETもbindingsを作る。
  • DEFUNやLETを the binding form と呼ぶ。(それ自身の構造の中でのみ有効なvariablesを導入可能なものは、すべてbinding formと呼ばれる)
  • binding forms をネストした場合、同じ名前の variablesについては、外側のbindingsは内側のbindingsによってshadowされる。
  • このようにbinding formsのネストの構造が、scopesの区切りになることは、lexical variablesについてもdynamic variablesについても同様である。



Lexical Variables and Closures

lexically scoped variables は、それを導入した binding form の中でしか参照できない。これが基本ルール。しかし、その binding formの評価結果がa function であって、それが、先のlexical scoped variablesを参照していたらどうなるだろう。これもアリである。そのbindingは、評価結果である function object が存在する限りそれにくっついている。これをクロージャという。クロージャが保持しているのはvalues自体ではなくbindingsである。よって、新しいvaluesをクロージャ内で割り当てれば、グローバル変数のように、valuesが変化していきそれを保持できる。
そうか、Schemeを環境モデルで理解するとき 関数定義+環境をクロージャと考えるが、CLではlexically scoped variablesのbindingsを包み込んでいるlambdaのことをクロージャというのか。しかしそうであってもDEFUNでもクロージャは作れるだろ。

CL-USER> (let ((z 10))
(defun foo ()
(setf z (1+ z))
(format t "Z: ~d~%" z)))
FOO
CL-USER> (foo)
Z: 11
NIL
CL-USER> (foo)
Z: 12
NIL
CL-USER>

なんぞ。


複数のクロージャが、ひとつのbindingを捕捉することができる。どれ。

CL-USER> (defparameter *fn2*
(let ((count 0))
(list
#'(lambda () (incf count))
#'(lambda () (decf count))
#'(lambda () count))))
*FN2*
CL-USER> (mapcar #'funcall *fn2*)
(1 0 0)
CL-USER> (funcall (car *fn2*))
1
CL-USER> (funcall (car *fn2*))
2
CL-USER> (funcall (car *fn2*))
3
CL-USER> (funcall (car *fn2*))
4
CL-USER> (mapcar #'funcall *fn2*)
(5 4 4)
CL-USER> (funcall (cadr *fn2*))
3
CL-USER>

なるほど。

ここで小休止。
復帰。

Dynamic, a.k.a. Special, Variables


  • global variables を生成するには、DEFVARとDEFPARAMETERがある。
  • scopingのルールは、これらについても他と同じ。だから、LETで、*standard-output*を*some-other-stream*にbindした場合、そのbindingsはLETのbodyのみで有効。
  • DEFVARとDEFPARAMETERは自動的にspecial宣言していると。
  • 思うのだが、top-levelがまさにlexicalになっているMLと比べると、top-levelで名前定義がglobal specialである環境(例えば今やっているCL)とかでは、top-levelでlexicalだdynamicだといってもなんだかわからないものではないのか。



Constants

特になし。


Assignment


  • 関数呼び出しは値渡し



Generalized Assignment


  • SETF は、user-defined places に使えるように拡張可能。DEFSETF、DEFINE-SETF-EXPANDERなどを使うらしい。



Other Ways to Modify Places

特になし。

Chapter 5 Functions

前説

特になし。


Defining New Functions

特になし。


Function Parameter Lists

特になし。


Optional Parameters

特になし。


Rest Parameters

言語仕様で保証されているarityは50まで。


Keyword Parameters

特になし。


Mixing Different Parameter Types

特になし。


Function Return Values

DEFUN は、bodyを、関数名と同じ名前のblockで自動的に包む。
そこから、RETURN-FROMで脱出できる。


Functions As Data, a.k.a Higher-Order Functions

callbacks や hooks と呼んで、CPSとは言わない。
#'... って、(function ...) の省略形だったのか。
ただし、(#'+ 1 2) はエラー。
ちなみに、('+ 1 2)もエラー。
(funcall #'+ 1 2)はOK。
うーむ。


Anonymous Functions

なんと。
LAMBDA expressions は本来はそれ自体が評価対象となりえるものではなく、LAMBDAではじまるリストは、そのリスト総体で一種の関数名なのだそうな。
ISLISPとの互換性を保つために、LAMBDA macroが定義されて、それが(funcall (LAMBDA ... と展開されるようにした構文糖衣とのこと。
えーと、Schemeを理解する際に頭の中にあったlambdaとは、これは違う。どこかで考えてみる。

LAMBDAはクロージャを作るのに使われるとのこと。この言いっぷりからすると、DEFUNはクロージャを作らないのか?
クロージャは次章でやるとのことなので、そこで確認。

Chapter 4 Syntax and Semantics

What's with All the Parentheses?

Lisp は S-expressions に固執しているわけではなく、M-expressions のニーズが高まればそれもよし、という観点。その上で、S-expressions を好むプログラマがそれなりにいるということ。

RubyがM-expressionsに近いと言われているが、調べたことはないです。


Breaking Open the Black Box

通常のプログラミング言語では、

  • programを表現する a sequence of characters を compiler が実行可能ファイルに翻訳する。

を3つのフェーズにわける。

  • a lexical analyzer が a sequence of characters を tokensにする。
  • a parser が tokens から、言語の文法に従って、an abstract syntax tree を作る。(Syntax)
  • a compiler が an abstract syntax tree を 機械語などの実行可能言語に翻訳する。(Semantics)

とな。Lispでは、

  • the reader が、programを表現する a sequence of characters を Lisp Objectsに変換する。Lisp Objects は S-expression と呼ばれる。S-expressions は abstract syntax trees と同じような木構造を表現できる。
  • the evaluatorが、S-expressionsをLisp formsに変換して評価する。

とな。この節の、最後の文章が理解できない。"Common Lisp: the syntax of s-expressions understood by the reader and the syntax of Lisp forms understood by the evaluator." そうなのか?
the reader は、sequences of characters を S-expressions に変換するものじゃないの? "the syntax of sequences of characters from which the reader can generate S-expressions" なら、わかるんだけど。


S-expressions


  • the reader は symbols が何を表しているかは知らない。それをsymbolsと認識するだけ。
  • the reader は、エスケープされていない文字を全て大文字に変換する。
  • the same textual name を同じsymbolとして認識するために、the reader は intern する。
  • the reader は、文字を全て大文字に変換して、a package というテーブルにすでにそれが存在するかどうかを確認して、それが存在すればそれを指すものとする。存在しなければ、the package に新しい symbolとして追加してそれを指すものとする。



S-expressions As Lisp Forms

self-evaluating な symbol もある。

  • T と NIL。
  • keyword symbols。



Function Calls

特記事項なし。


Special Operators

the special operators は、the evaluator が特殊な処理を必要とするときのものであることが多い、とな。


Macros

special operators と special forms はこの本では同義のようだ。
マクロの引数は、well-formedなLisp formsではなくてもよい。マクロがそれをwell-formedなLisp formsに変換する。すなわち、マクロはSemanticsを定義している。

Truth, Falsehood, and Equality

おお。NILは、atomでありlistでもある。。。


Formatting Lisp Code

うーん。SLIMEで、C-M-q、C-cM-qが機能しない。


4章終了。休憩。

2008年1月3日木曜日

Chapter 3 Practical: A Simple Database (Continued)

Querying the Database

evenpの名前空間の指定に#'が必要なのはわかるが、なんでlambdaにも#'がいるんだろ?
試す。

CL-USER> (remove-if-not #'evenp '(1 2 3 4 5 6 7 8 9))
(2 4 6 8)
CL-USER> (remove-if-not #'(lambda (x) (= 0 (mod x 2))) '(1 2 3 4 5 6 7 8 9))
(2 4 6 8)
CL-USER> (remove-if-not (lambda (x) (= 0 (mod x 2))) '(1 2 3 4 5 6 7 8 9))
(2 4 6 8)
CL-USER> (remove-if-not evenp '(1 2 3 4 5 6 7 8 9))
; Evaluation aborted.
CL-USER>

ふむ。lambda は #'なしでもOKなのか。

keyword parameters のところが頭がごちゃごちゃしたのでまとめる。

まず、

(defun foo (&key a b c) (list a b c))

これ。&keyの後ろとbodyの変数が一致しないとどうなる?

CL-USER> (defun (&key a b d) (list a b c))
; Evaluation aborted.

エラーとなると。それからデフォルト値が指定できると。

CL-USER> (defun foo (&key a (b 20) c) (list a b c))
FOO
CL-USER> (foo :a 1)
(1 20 NIL)
CL-USER>

引数として与えられたかどうかをbodyに渡せる。

CL-USER> (defun foo (&key a b (c 30 c-p)) (list a b c c-p))
FOO
CL-USER> (foo :c 1)
(NIL NIL 1 T)
CL-USER> (foo)
(NIL NIL 30 NIL)

なる。

CL-USER> (defun where (&key title artist rating (ripped nil ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) t)
(if artist (equal (getf cd :artist) artist) t)
(if rating (equal (getf cd :rating) rating) t)
(if ripped-p (equal (getf cd :ripped) ripped) t))))

WHERE

これは何してるんだっけ?

  • selectに渡すselector関数を作るための関数。
  • selector関数は、述語であり、*db*の各レコードに対して、t or nilを返すもの。
  • andの中の構造をみると、このwhere関数は、与えられた全ての条件を満すものをtとする述語。
  • (if hoge (...) t)なんで、条件が与えられてないものはtとする。
  • rippedについては、条件が与えられたときの値がt or nilなので、supplied-pを使用する。

ということか。


Updating Existing Records-- Another Use for WHERE

setf の詳細はChapter 6ということなので、スルー。
mapcar, when, funcallの説明は無し。まあスルーだが、Lisp系の言語をかじったことが無い人はこのあたりで、何がなんだかということで脱落しそうだが。
そういえば、破壊的操作に!をつけるというルールはないんですね。


Removing Duplication and Winning Big

マクロのクラッシュコース 的のもの。

さて、三章までを読んだところでの感想は。

  • CLerの言語の認識がこのような感じだとすると、すでに言語コアとライブラリの区別は認識から消失しているのかもしれない。とすると、CLを使ってCLerがプログラミング初学者に教えるよりも、Schemeで教えた方がよいと思う。少なくとも、R5RSとSRFIとは分離しているので。
  • なにか、昔Rubyを学んだときと同じ感触なような。。。
  • 明示的な再帰が出てこなかった。LOOP多用。
  • Practicalということだから、三章でそぞろ歩きするのはよいとは思う。

Chapter 3 Practical: A Simple Database

前説

この章では、functions, macros and special operators の違いは気にするなと。


CDs and Records

symbolは、まあ名前。
keyword symbolは、symbolのうち、":"から始まるもの。


Filing CDs

特記事項なし。


Looking at the Database Contents

formatって、エラくLoopするな。試す。


CL-USER> (list *db* *db*)
(((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "kathy Mattea" :RATING 7 :RIPPED T))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "kathy Mattea" :RATING 7 :RIPPED T)))
CL-USER> (format t "~{~a:~10t~a~%~}~%" (list *db* *db*))
((TITLE Home ARTIST Dixie Chicks RATING 9 RIPPED T)
(TITLE Fly ARTIST Dixie Chicks RATING 8 RIPPED T)
(TITLE Roses ARTIST kathy Mattea RATING 7 RIPPED T)): ((TITLE
Home
ARTIST
Dixie Chicks
RATING
9
RIPPED
T)
(TITLE
Fly
ARTIST
Dixie Chicks
RATING
8
RIPPED
T)
(TITLE
Roses
ARTIST
kathy Mattea
RATING
7
RIPPED
T))

NIL
CL-USER>

なる。修正。


CL-USER> (format t "~{~{~{~a:~10t~a~%~}~%~}~}" (list *db* *db*))
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T

TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T

TITLE: Roses
ARTIST: kathy Mattea
RATING: 7
RIPPED: T

TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T

TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T

TITLE: Roses
ARTIST: kathy Mattea
RATING: 7
RIPPED: T

NIL


なんぞ。

これ、ミニ言語なんですね。


Improving the User Interaction


CL-USER> (defun prompt-read (prompt)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
PROMPT-READ


*query-io*って?
REPLで調べると、TWO-WAY-STREAM なんですね。


Saving and Loading the Database

read/print equivalenceを保証するには、with-standard-io-syntaxを使う。

疲れがでてきたので、休憩。

2008年1月2日水曜日

Chapter 2 Lather, Rinse, Repeat: A Tour of the REPL

Choosing a Lisp Implementation

Lisp処理系の紹介。aclと決めているので。


Getting Up and Running with Lisp in a Box

Emacsの紹介。
SLIMEの紹介。
Lisp in a Box の紹介。

日頃はAllegroのelispモジュールを使っているが、本に合わせるということでSLIMEを使ってみようと思う。
そこで、Lisp in a Boxのページへ。Win用はモジュール化されているが、Linuxはtar玉。

LinuxはUbuntuで構築しているため、あまり環境を汚したくないからpass。emacsまでtar玉で入れられても困る。
WinでLisp in a Boxを試す。
FranzからWin用のfree express editionを取得。インストール。
しかし、Lisp in a BoxのAllegro用モジュールインストーラがそれを認識できず、モジュールが入らない。
試しにclispモジュールをいれてみると、こちらはそのまま入る。

さて、どうしようか。

Common Lispなので、clispでも大差ないと思うが、今回はAllegroを堪能したいという思いもあるので、Linux上に自前で、acl + emacs + SLIMEの環境を構築することにする。

見るべきは、ここ。
http://www.franz.com/emacs/slime.lhtml

ubuntuのパッケージであるか?
$ aptitude search slime
$
ない。では、ソースから。asdf-installはやめておく。ubuntuのパッケージ管理との兼ね合いを検討の上、別途導入是非を決める。
今はCVSから。って、cvs日頃使っていないのでaptitudeで入れる。後はfranzのガイド通り。

使い方メモ。
M-x slime REPLバッファ起動
M-x slime-mode バッファをslime-modeに。

詳しい使い方はSLIMEホームページ。
http://common-lisp.net/project/slime/


Free Your Mind: Interactive Programming

用語の整理。
REPLにて、処理系は、Lisp expressionsをreadし、それをthe rules of Lispによって、evalする。
evalした結果をprintする。
ここで使うREPL環境は、the top-level, the top-level listener, the Lisp Listenerなどと呼ばれる。


Experimenting in the REPL

REPLをいじる。


CL-USER> 10
10
CL-USER> (+ 2 3)
5
CL-USER>


ひとつめ。the Lisp readerが"10"というテキストを読み込み、数字の10をrepresentingなa Lisp objectを生成する。
このobjectは、a self-evaluating objectなので、それをevaluatorが処理すると、自分自身が返る。
それが、printerに渡されて、printerがテキスト"10"を表示する。

ふたつめ。the Lisp readerが"(+ 2 3)"というテキストを読み込み、Lisp objectsを生成する。どういうobjectsかは、ここでは割愛して、後の章でS expressionsが出てきたところ(出てくるだろう)でやる。ざっくりいうと、+はa symbolであり、2と3はnumbersでり、このS式は、+として定義された関数(obj)に引数2と3(objs)を与えているということになる。
続いて、evaluatorは、2と3を評価して、2と3を得て、評価した結果を+とともに評価して、5を得る。
printerは5を受け取って、表示する。


"Hello, World" Lisp Style


CL-USER> "hello, world"
"hello, world"
CL-USER> (format t "hello, world")
hello, world
NIL
CL-USER>


ひとつめ。Numberと同様にStringもself-evaluatingなLisp objects。readerが読み込むときの形とprinterが書き出す形が同じ。
いわゆるread/print equivalence。

Lisp系をやったことが無い人は、何をごちゃごちゃと、と思うかもしれませんが、Lispではこのあたりの概念と用語をしっかりと確認することが重要(であるとSchemeの経験から思う)。なので、ここから入っているこの本はよいと思う。

ふたつめ。副作用(a side effect)による出力。every expression in Lisp evaluates to some result. であり、formatはNILを返す。

この後、defunを導入してこの節終了。


Saving Your Work

SLIMEを使ってみる。

C-cC-qでslime-close-parens-at-pointが起動しない。というかこの関数自体がないような? obsoleteになったのか? いつか調べる。
C-cC-c、C-cC-zはきく。

quitじゃなくて、sayoonaraでもいいんだ。SLIMEに日本の開発者が入ってるのかなぁ。

Chapter 1 Introduction: Why Lisp?

導入のお話。

特に新しい知見はない。

読む目的

Practical Common Lispを読んでみる。

目的は次のとおり。


  • Schemeはちょっと知っているが、Common Lispはほとんど知らない。両者の差分は何だろう。特に次の3点。
  • 言語仕様がでかい、と言われるが、それはどうなのか。必要にしてなのか、ORをとっただけなのか。
  • 名前空間の違いは煩雑なのか便利なのか。
  • マクロのパワーはCommon LispとSchemeで違いがあるのかどうか。ここでパワーとは、使い手がどれだけ自然にマクロを捉えられるかを含む。


  • AllegroCacheの簡便さはどの程度なのか。
  • Prolog風味な割切り言語Erlangとくらべて、Common Lispの学習カロリーは見合うのか?


読む際の方針は次のとおり。


  • 後段の章で詳細が触れられるものは、その時点で理解に不安があっても先に進む。
  • 後段の章で詳細が触れられず、その章が説明の中心であり、かつその内容が重要であれば、確実に理解するまで前に進まない。
  • 処理系はAllegroCLとする。


書く際の方針は次のとおり。


  • 訳すのではなく自分の言葉で書く。
  • 読書メモである。自分自身が後から振り替えるための特記事項を書く。
  • 原典の著作権については、適切な引用の範囲として配慮する。