Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-07-26

実践Common Lisp発売!!

| 16:34 | 実践Common Lisp発売!! - わだばLisperになる を含むブックマーク はてなブックマーク - 実践Common Lisp発売!! - わだばLisperになる

このブログでPractical Common Lispのことを取り上げるのは、英語版を読んでいた5章の記録を最後に実に半年ぶりです(笑)

原著は、すべての内容がウェブ上で読めたりPDFで入手可能だったり、かなり太っ腹ですが、当然ながら英語ということで内容は面白いものの英語の苦手な自分にはちょっと敷居が高いものでした。

内容的には、CL入門から中級程度の話題まで一通りのことを網羅してあり、かつタイトルどおり実践的なものなので、

  1. On Lispから入門しようとして挫折した
  2. ANSI Common Lispを読んで次の書物を物色している
  3. 古いCommon Lispの文献で色々調べていたけれど、なんか今どきのCommon Lispと細かいところで書法が違って混乱した
  4. 入門書を読んで大体Common Lispの検討はついたけれど、実践的なコードはどう書くのか迷子になっている
  5. Schemeは知っているけれど、親戚のCommon Lispは知らない

等々の方々には、良い教材になるに違いないと思います。

早速ジュンク堂には(昨日、金曜の時点で)大量入荷しているという情報も。

2008-01-25

Practical Common Lisp (23)

| 15:24 | Practical Common Lisp (23) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (23) - わだばLisperになる

Practical Common Lisp 第5章5. Functionsを読んでメモしてみています。

大分間が開いてしまいました。かなり、まったり進行です。

Mixing Different Parameter Types

  • 各種ラムダパラメータは組み合わせられるが、リストに現われる順番には決まりがある。下記の順番である必要がある。
    1. &optional
    2. &rest
    3. &key
  • これらのパラメータは組み合わせられるものの直感的でない動作をするものもあるので注意が必要
1.
((lambda (x &optional y &key z) (list x y z)) 1 2 :z 3)

2.
((lambda (x &optional y &key z) (list x y z)) 1 :z 3)
;=> error

3.
((lambda (x &optional y &rest z) (list x y z)) 1 3)
;=> (1 3 nil)

4.
((lambda (&rest rest &key x y z) (list rest x y z)) :x 1 :y 2 :z 3)
;=> ((:X 1 :Y 2 :Z 3) 1 2 3) 

個人的には、2のエラーは&keyと&restが同じ場所に格納されると考えると合点が行くんじゃないかと思っています。

2008-01-09

Practical Common Lisp (22)

| 19:12 | Practical Common Lisp (22) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (22) - わだばLisperになる

Practical Common Lisp 第5章5. Functionsを読んでメモしてみています。

半挫折気味です(笑)、気力が…。

Keyword Parameters

  • オプショナル引数は便利だが、キーワード引数を使うとそれだけでは解決できないこともできる。
  • &optional、&restの後だったらどこでも使える。
(foo)                ==> (NIL NIL NIL)
(foo :a 1)           ==> (1 NIL NIL)
(foo :b 1)           ==> (NIL 1 NIL)
(foo :c 1)           ==> (NIL NIL 1)
(foo :a 1 :c 3)      ==> (1 NIL 3)
(foo :a 1 :b 2 :c 3) ==> (1 2 3)
(foo :a 1 :c 3 :b 2) ==> (1 2 3)
  • 上記のように引数定義の順番ではなく、キーワードの名前と指定した値が対になる。キーワードを省略した場合は、&optionalのようにデフォルト値が使われる。また、同様にsupplied-pも使用可能。
  • 外向のインターフェースとしては、指定した名前を使用し、内部では別の名前で処理したい場合、下記のように書ける。
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
  (list a b c c-supplied-p))

;=> (foo :apple 10 :box 20 :charlie 30) ==> (10 20 30 T)

; :appleで値を受けとるが、内部では、aに結び付けられているので、aで参照。

PCLとは無関係な個人的な深追いと混乱メモ

CLtL2を読んでいたら、キーワードについてさらに詳しい(というか自由度の高い)使い方が説明されていたのでメモ

PCLでの例のように(:apple a)という風に記述すれば、内部では違う名前で扱えるわけですが、

(defun foo (&key ((:apple a) 0))
  `(apple -> ,a)

(foo :apple 10)
;=> (apple -> 10)

キーワードで使うシンボルは、キーワードパッケージのシンボルでなくても良いみたいで、

(defun foo (&key ((apple a) 0))
  `(apple -> ,a )

とも書けます。

それで、呼び出しはどうなるのかと言えば、

(foo 'apple 1)
;=> (apple -> 1)

(foo :apple 1)
;=> エラー

となり、定義に使用したキーワードが属するパッケージのシンボルである必要があるようです。

上記のようにパッケージ名を省略すれば、カレントパッケージになるわけですが、見た目で混乱するので、たとえば、cl-userのシンボルを使うとすると、

(defun foo (&key ((cl-user::apple a) 0))
  `(apple -> ,a )

(foo 'cl-user::apple 1)
;=> (apple -> 1)

となります。

(defun foo (&key foo)
  (print foo))

をややこしく書くと、

(defun foo (&key ((keyword::foo foo)))
  (print foo))

ということみたいです。

しかし、キーワードにキーワードパッケージ以外を指定するような状況ってどういうのがあるんでしょう…。

2008-01-06

Practical Common Lisp (21)

| 21:21 | Practical Common Lisp (21) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (21) - わだばLisperになる

Practical Common Lisp 第5章5. Functionsを読んでメモしてみています。

Rest Parameters

  • 引数の扱いには、様々あるが引数の数を可変にしたいことがある。例えば、FORMATは、2つ以上の引数を取るが、2つ目以降は必要に応じて与える引数が増減する。+も同様。0個の場合もあり、(+)は0となる。
(format t "hello, world")
(format t "hello, ~a" name)
(format t "x: ~d y: ~d" x y)
(+)
(+ 1)
(+ 1 2)
(+ 1 2 3)
  • 必須引数や、オプショナル引数等をやりくりして対応することもできるが、対応させるのは困難。。ちなみに取れる最低限50以上は引数が取れることを保証することが規格で定められている。実装により上限は異なるが、CALL-ARGUMENTS-LIMITで処理系の上限を確認できる。(SBCL 1.0.13では、1,152,921,504,606,846,975だった)
  • このような問題を解決するのが、&RESTパラメータで、&RESTに指定された変数がリストとなり、以降の引数が格納される。FORMATや、+は下記のように定義できる。
(defun format (stream string &rest values) ...)
(defun + (&rest numbers) ...) 

2008-01-04

Practical Common Lisp (20)

| 00:16 | Practical Common Lisp (20) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (20) - わだばLisperになる

Practical Common Lisp 第5章5. Functionsを読んでメモしてみています。

Optional Parameters

オプショナルパラメータの話

  • 全部の関数が予め決められた数の引数を扱うなら良いが実際的には、例えば、パラメータに初期値が設定されていて、呼び出し時にもし必要な場合、パラメータを指定したりできると便利。
  • そういうときには、&OPTIONALが使える。
(defun foo (a b &optional c d) (list a b c d))
  • 上記の場合、&optionalより前のaとbは必須パラメータとなり、それより後は、オプショナルになる。
  • 呼び出し時にオプショナル引数に値を渡さないとNILで初期化してくれる。
(foo 1 2)     ==> (1 2 NIL NIL)
(foo 1 2 3)   ==> (1 2 3 NIL)
(foo 1 2 3 4) ==> (1 2 3 4)
  • NILで初期化するのは分かったが、他の値で初期化して欲しい場合、明示的に指定する方法があり、(変数名 初期値)という風にリストにして記述する
(defun foo (a &optional (b 10)) (list a b))

(foo 1 2) ==> (1 2)
(foo 1)   ==> (1 10)
  • 引数の値を他の引数から算出して設定したい場合もある。例えば、矩形を描く関数を定義するとして、基本的にデフォルトは正方形を描くものとし、高さは、明示的に与えられない限り、幅から算出されるとしたい。その場合、先に決定した値を後方で参照して初期値として使用することができる。
(defun make-rectangle (width &optional (height width)) ...)
  • 偶に、引数が明示的に与えられたのか、それとも初期値が使われたのかを判別したい場合がある。その場合には、(変数名 初期値 指定されたかどうかを判別する変数名)という形式で記述する。
(defun foo (a b &optional (c 3 c-supplied-p))
  (list a b c c-supplied-p))

なんとなく演習

(defun オプショナルな気分 (&optional (ans nil))
  (print
   (if ans
       "オプショナルな気分なんですね?"
       "オプショナルな気分ではないのすね?")))

(オプショナルな気分 nil)
"オプショナルな気分ではないのですね?" 

(オプショナルな気分)
"オプショナルな気分ではないのですね?" 
;; いや、そういうわけではないのだが…。

;; 上の定義では、区別が付かないので改善
(defun オプショナルな気分 (&optional (ans nil ans-supplied-p))
  (print 
   (cond ((and ans-supplied-p (not ans)) "オプショナルな気分ではないのですね?")
	 (ans "オプショナルな気分なんですね?")
	 ('T "なんともいえないのですね?"))))

(オプショナルな気分 nil)
"オプショナルな気分ではないのですね?" 

(オプショナルな気分)
"なんともいえないのですね?" 
;; いや、そういうわけでもないのだが…。

--

質問に答えること自体は任意で、否定したい場合は、NILで答えるとした場合、オプショナル引数を省略したためにデフォルト値のNILが渡って来たのか、それとも明示的にNILで答えられたのか区別できないので、supplied-pを利用するという無理矢理なケースを捏造してみる。

2008-01-02

Practical Common Lisp (19)

| 23:30 | Practical Common Lisp (19) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (19) - わだばLisperになる

Practical Common Lisp 第5章5. Functionsを読んでメモしてみています。

Function Parameter Lists

  • パラメータリストの主な役割とはもちろん引数をどう受けとってどのように関数本体の処理に渡すかということ
  • パラメータリストが変数名のみで構成された単純なリストの場合、それぞれの要素は必須パラメータと呼ばれる。
  • この場合、関数が呼ばれる際は、指定したパラメータそれぞれに対応する引数が必ず割り当てられなければならない。対応する引数は、各パラメータに束縛される。関数の呼び出し時に渡される引数が多くても少なくてもエラーとなる。
  • しかしながら、Common Lispのパラメータリストにはより柔軟な引数処理の指定もできる。
  • まず、前述の必須パラメータに加えて、省略可能なオプショナルパラメータがある、また、引数をリストで纏めてリストで受けとることもできる。また、引数の位置の出現位置の順番で対応を指定するのではなく、指定したキーワードに結び付けて取得する方法、というようにCommon Lispには色んな場合に対応できる方法を提供している。

2008-01-01

Practical Common Lisp (18)

| 13:37 | Practical Common Lisp (18) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (18) - わだばLisperになる

やっとこさPractical Common Lisp 第5章5. Functionsです。この章も文字だけみたいなので、読んでメモするという感じで…。

5. Functions

  • 文法と意味論の後は、Lispの3つの基本要素である、関数、変数、マクロをみてゆく。
  • 3章で既にこれら3つは使用していたが、うわべだけでどのように機能しているかの詳細には立ち入らなかった。
  • 今後の2、3章で、これらの話題を扱う。他の言語で対応する諸機能と対象させて考察してみたり等々…。
  • Lispの大半は、関数で成り立っていて、言語規格で定められたものの3/4は関数が占める。
  • すべての組込みデータ型は、関数がどのようにデータを扱うかということにより定められている(?)
  • CLOSでさえ、関数/総称関数の上に成り立っている。これらは、16章で扱う。
  • Lisp流儀にはマクロというものが非常に重要なものになるが、すべての真の機能性は関数によってもたらされているものである。

Defining New Functions

  • 通常関数定義にはDEFUNが用いられ下記のように書かれる
(defun name (parameter*)
  "Optional documentation string."
  body-form*)
  • どんなシンボルでも関数名に使える。通常、アルファベットとハイフンのみが多い。それ以外は特定の命名規則に則って使用されることが多い。
  • 例えば、ある形式のものを他の形式に変換するような関数の場合、"-"で連結されて一つの名前になることがある。文字列をウィジェットに変換する場合は、string-widgetといった風。
  • 重要な事項としては、2章でも述べたが、語句の連結に、"-"を使いアンダースコアや、キャメルケースで書くことはしないということで、frob-widgetは、frob_widgetや、frobWidgetより好ましい。
  • 関数の引数リスト部は変数がどのように関数に渡されるかを定義する。引数を取らない場合は、空リストの()で表記される。
  • 引数の取り方の指定は、色々ある。必須とされるもの、オプション扱いのもの、複数の指定、キーワードの指定。
  • 引数のパラメータリストの次が文字列の場合、その文字列は、その関数を説明に使われるドキュメント文字列となる。関数が定義された場合、ドキュメント文字列は、DOCUMENTATIONで参照できるように定義される。
  • 最後に関数の主となる定義部分である本体になる。式は順に評価され末尾で評価された式が関数の値となるか、RETURN-FROMを用いて指定した値で関数を脱出することもできる。
  • 2章で記のような関数定義をしたが、これを詳細にみると下記のようになる。
    1. 関数の名前:hello-world
    2. 引数:()リストなので、引数はとらないことがわかる
    3. ドキュメント文字列はなし
    4. 一つの式のボディ
(defun hello-world () (format t "hello, world"))
  • もう少し複雑なものをみると、下記のようになる。
    1. 関数の名前は、verbose-sum
    2. 2つの引数を取り、xとyに束縛される。
    3. ドキュメント文字列を持つ。
    4. ボディは、2つの式から成り、返り値は、+が引数を処理した値が、verbose-sum全体の返り値となる。
(defun verbose-sum (x y)
  "Sum any two numbers after printing a message."
  (format t "Summing ~d and ~d.~%" x y)
  (+ x y))

2007-12-31

Practical Common Lisp (17)

| 12:10 | Practical Common Lisp (17) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (17) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。やっと4章が終った。この先が思いやられる。

Practical Common Lisp を読む - mokeheheのScheme日記 - sicpさんもPCL読書記録開始とのことで、xyzzyで取り組まれるとのこと。多分、日本でCommon Lisp系で一番ユーザが多い処理系は、xyzzyじゃないでしょうか。xyzzy周辺は面白いことをやってる人も凄い人も多い気がします。MP3のストリーミングサーバとか後半戦はきつそうですが、xyzzyは結構色々拡張してる人が沢山いて想像以上になんでもできるっぽいので、どういう風になっていくのか楽しみです。

Formatting Lisp Code

  • コードの整形というものは、厳密には文法にも機能にも関係はないものの、コードのスムースな読み書きには重要な事項。
  • Lispコードについていえば、まず重要なのはインデント
  • インデントはLispのコードの機能的構造が良く反映されていて、対応する括弧を勘定したりなどという無駄が発生しないようにすべき。
  • 一般には、コードがネストする場合、インデントは一段深くなる。改行が必要な場合は、同レベルは同じ深さで揃えられる。例えば、下の例ように。

(some-function arg-with-a-long-name
               another-arg-with-an-even-longer-name)

  • マクロとスペシャルフォームは、すこし違っていて、本体部が、2つのスペースで字下げされる。

(defun print-list (list)
  (dolist (i list)
    (format t "item: ~a~%" i)))

  • とはいえ、インデントについてあれこれ悩む必要はなく、SLIMEのような開発環境は適切に字下げの面倒を見てくれる。エディタのようなソフトウェアがインデントを解析する場合、非常に容易に解析できるのがLispの文法のアドバンテージでもある。
  • SLIME(Emacs)についていえば、開き括弧に移動して、C-M-qを押下すると、字下げし直してくれる。関数定義全体は、C-c M-qで整形してくれる。
  • インデントについてのエディタの支援はただ見た目を綺麗にしてくれるという点だけではなく、タイポも発見しやすくしてくれる。たとえば、下記のようなコードがあったとして

(defun foo ()
  (if (test)
    (do-one-thing)
    (do-another-thing)))

  • testの後に括弧を忘れた場合、下記のようにインデントされるため発見が容易になる。

(defun foo ()
  (if (test
       (do-one-thing)
       (do-another-thing))))

  • 他にコードの整形で重要なこととしては、閉括弧はリストの最後にくっつけること。下記のようには書かないこと。

(defun foo ()
  (dotimes (i 10)
    (format t "~d. hello~%" i)
  )
)

  • 最後の)))の連続はそら恐しく感じるが、適切にインデントされたLispコードの括弧は邪魔に意識されることはない筈。
  • 最後にコメントの付け方。1〜4つのセミコロンを使い分け、各々の範囲によって数が変わる。

;;;; Four semicolons are used for a file header comment.

;;; A comment with three semicolons will usually be a paragraph
;;; comment that applies to a large section of code that follows,

(defun foo (x)
  (dotimes (i x)
    ;; Two semicolons indicate this comment applies to the code
    ;; that follows. Note that this comment is indented the same
    ;; as the code that follows.
    (some-function-call)
    (another i)              ; this comment applies to this line only
    (and-another)            ; and this is for this line
    (baz)))

どうでも良いメモ

CLtLでも註釈について書いてて上記と大体同じなんですが、行を継続させる場合は、一文字下げるとか書いてます。それで、一つの;の場合は、スペースなしで直接書き始めてる感じです。最近のコードではあんまり見掛けることは無いんですが、7、80年代のMITのコードではその慣習で書いてることがたまにあります。何れにせよコメントの付け方ってあんまりこだわって書いてる人も多くない気はしますが…。


;;;; Four semicolons are used for a file header comment.

;;; A comment with three semicolons will usually be a paragraph
;;; comment that applies to a large section of code that follows,

(defun foo (x)
  (dotimes (i x)
    ;; Two semicolons indicate this comment applies to the code
    ;; that follows. Note that this comment is indented the same
    ;; as the code that follows.
    (some-function-call)
    (another i)              ;this comment applies to this line only
    (and-another)            ; and this is for this line
    (baz)))

という風です。殆ど間違い探し的にしか違いがありませんが…。

2007-12-28

Practical Common Lisp (16)

| 00:21 | Practical Common Lisp (16) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (16) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

Truth, Falsehood, and Equality

  • Common Lispを修得に関して重要な真偽値の扱いについて
  • シンボルNILは唯一の偽値であり、それ以外は、全部真とみなされる。
  • 非NILの値を返したい場合、正準的にはシンボルTが使われる
  • NILで唯一ややこしい事項として、アトムでもありリストでもある唯一のオブジェクトであるということが挙げられる。
  • NILは、偽を表わすとともに、空リストも表わす。
  • リーダが()を読み込む時それは、NILとして扱われる。これらは完全に相互に交換可能。
  • 前述したように、NILは定数となっているので、'NILも、'()もNILと同等ということになる。クウォートされていない場合は、シンボルNILが定数NILを参照するものとして評価される。しかし、クウォートされている場合QUOTEスペシャルオペレータは、直接シンボルNILを評価することになっている。
  • TについてもNILと同様のことがいえる。'TもTも同じ。
  • 「同じもの」という言葉は「同じものというのは、どういうことか」が定義されていなければいけないが、Common Lispでは、「同じもの」を判定するためにいくつか用意されているものがある。
  • 「=」は数が同値であるかを判定する。「CHAR=」は文字が同一であるかを判定する。
  • このセクションでは、一般的に2つのLispオブジェクトが渡された場合に、同等のものかを判定する4つの機構を紹介する。段階的にEQ、EQL、EQUAL、EQUALPがある。
  • EQは、Lispオブジェクトが同一のものかどうかを判定する。
    • 残念ながら、数と単体の文字の同一性についてどのように扱うかは処理系の判断に委ねられている。
    • (eq 3 3)は真であっても偽であってもどちらでも良いということ。(eq X X)という式で、Xに数字か単一文字かが来る場合、eqを用いて判定するのはまずい。
    • 特定の処理系では意図した通りに動くかもしれないが、全部の処理系で成り立つとは限らない。また、処理系間の違いだけでなく、同一の処理系であっても、eqの仕様が変更されるのは別段問題はないことに注意。
  • ということから、同じクラスの同一の数字、同一の文字であることを判定するには、eqlを使うことになっている。
    • (eql 1 1)は真となり、(eql 1 1.0)は、整数クラスと、浮動小数点数クラスを比較しているので偽となる。
  • EQとEQLの使い分けについての流派は2つある。
  • 「可能な限りEQを使う派」数と文字を主張比較することにはならないことを確信して使う必要がある。
    • (a)しかし、文脈上で数と文字を比較することにはならないことを主張することができる。
    • (b)EQをつかった方がほんのすこし効率的になる(同一のLispオブジェクトかどうかを判定するだけのため)
  • 「EQL常用派」
    • (a)EQが現われた時にEQが正しく使われているかを確認する必要がないので、コードが明解になる
    • (b)EQとEQLの間の「すこしの効率」の違いは、他のボトルネックの要素と比べると誤差のようなもの
  • この本で示されるコードは、「EQL常用派」コードです。
  • より広い範囲のものの同一性を判定するには、EQUALやEQUALPを使う。
  • EQLよりわずかに判定基準が広く、EQLでは偽となり比較できないものを比較するのに使う。
  • これらの基準は、過去のLispプログラマが実用上便利だなと思って定めた以上のものではないので、必要とあらば、自分自身で判定のための述語を作成して使おう。
  • EQUALにリストが与えられた場合、再帰的にEQUALを適用して内容を調べ、構造と内容物が一緒の場合、真となる。
  • また、文字列の場合は、含まれている文字を逐次比較してが全部同じならば、真となる。後述するビットベクタとパスネームストリングについても同様。
  • これら以外は、EQLに判定を投げる。
  • EQUALPはEQUALよりさらに判定基準がゆるい。
    • 文字列中の大文字と小文字を区別の規準としない。
    • 数字の数としての価値が同じならば、同じものとして扱う。つまり、(equalp 1 1.0)は真。
    • 対応要素がそれぞれEQUALP等価なリスト同士は、リストとしてもEQUALP等価
    • 対応要素がそれぞれEQUALP等価な配列同士も同じ。
    • これら以外は、EQLに判定を投げる。

2007-12-26

Practical Common Lisp (15)

| 05:14 | Practical Common Lisp (15) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (15) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

Macros

  • スペシャルオペレータは関数呼び出しの方法を拡張したものではあるが、それは言語の標準で定められたものでもある。
  • 一方、マクロはユーザに文法を拡張する手段を提供する。
  • 3章で見たように、マクロはS式を引数としてとって任意のLispフォームへ展開し、展開された場所で評価される仕組となっている。
  • マクロの評価は2段階に分かれる
    1. まず、マクロのフォームは、中身を評価されることなくマクロ展開機構に渡される
    2. マクロ展開機構から展開された結果のフォームが返されるので、それを通常の評価方法で評価する。
  • この2段階で処理されるということを心にとめておくことは重要。REPLで対話的に操作していると、あっという間に処理が終ってしまうので、このことを見失いがち。
  • しかし、Lispのコードがコンパイルされる時には、完全に別々の2段階を踏んで実行されるので、きちんと把握することが重要。
  • 例えば、COMPILE-FILE関数で、一つのソースコードファイルを丸ごとコンパイルする場合、そのファイルの中のマクロは再帰的にすべて展開され、関数とスペシャルフォームだけが含まれたものまで展開される。
  • マクロ抜きされたものが、LOAD関数でロードできるFASL形式のファイルへとコンパイルされる。これは、コンパイルされはするもののその際に実行はされない。
  • マクロ展開がコンパイル時に行われるということは、コンパイルされたファイルをロードする際には、マクロを展開するという労力を払わなくても良いということを意味する。
  • また、評価器がマクロ展開機構に展開される前のフォームを評価することはないので、マクロのフォームは正規のLispのフォームの形をしている必要もない。
  • 各々のマクロは、させたいことを展開してS式に割り当てる手段を提供する、換言すれば、各々のマクロはローカルな文法を持つことが可能ということでもある。
  • 例えば、3章で例に上げた、さかさフォームのマクロは、正規のLispフォームがさかさまになったものを与えられれば、正規のLispフォームを返すという文法を定めていることになる。
  • マクロが果す役割としては、コンパイラへのフックの提供という目的もある。文法的には関数呼び出しと非常に類似しているのだが、提供するものはまったく違う。

2007-12-24

Practical Common Lisp (14)

| 00:20 | Practical Common Lisp (14) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (14) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

Special Operators

  • 全部が全部、関数の形式で解決できるというわけではない。
    • 関数の引数は、関数に渡される前に評価済みになっていなくてはならないため
  • IFを例にとって詳細を解説 (if x (format t "yes") (format t "no")) というフォームを考える。
  • 仮にIFが関数だとすると、引数は左から右に順に評価されることになる。この場合だと、formatの返り値は、NILとなり、X、NIL、NILが関数に渡され処理されることになる。しかし、それだとIFが真偽を分岐させる前にyesもnoも表示されてしまうことになる。
  • この問題を解決するために、スペシャルオペレータが導入されている。Common Lispの処理系には、そのようなものが25用意されている。
  • もし、リストの最初のシンボルがスペシャルオペレータならば、残りの引数の評価方法はそのスペシャルオペレータが規定する評価順序に従う。
  • IFの場合だと、最初の引数を評価して、それが、NILでないならば、その次を評価し、結果を返す。それ以外は、一つ飛して3番目を評価し、結果を返す。(if test-form then-form [ else-form ])
  • test-formは常に評価され、then-formが評価されるか、else-formが評価されるかする。
  • 同様に単純なスペシャルオペレータとして、QUOTEがある。一つの引数を取り、単純にそれを返す。しかし評価はされない。(+ 1 2)というリストが渡された場合、3ではなく、(+ 1 2)を返す。 (quote (+ 1 2))
  • QUOTEは、広く使われるので、リーダに簡便な記法が組込まれ用意されている
  • (quote (+ 1 2))と書く代わりに、'(+ 1 2)と書ける。
  • この文法は、リーダーによって解釈されるS式のちょっとした拡張になっているので、どちらの記法をしても、結果を渡される評価器からすれば、最初が、QUOTEで残りが(+ 1 2)というリストということになる。
  • 一般的に言えば、スペシャルオペレータは、評価器による特別な解釈を要求する。幾らかのスペシャルオペレータは変数環境等の環境を操作するものがある。例えば、LETは、新しい変数の束縛をつくる。それら一部は、6章で詳細を検討する。
  • (let ((x 10) ) x)は、10が評価値となるが、これは、2番目に出てくるxが評価された値となっている。そのxは、(x 10)として束縛されたxの値となっている。

2007-12-23

Practical Common Lisp (13)

| 16:59 | Practical Common Lisp (13) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (13) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

Function Calls

  • 関数呼び出しのフォームは簡単で、関数名以降の残りのリストを評価して得られた値を関数に渡すだけ。
  • ということは、関数名以降も正規のLispフォームである必要があるということ。
  • 基本形 (関数名 引数*)
  • (+ 1 2)がどう評価させるかを順に辿ると
    1. 1が評価される。
    2. 2が評価される。
    3. これらを、+に渡して処理し、結果として3が返る。
  • もう少し複雑な、(* (+ 1 2) (- 3 4))の場合
    1. (+ 1 2)は、上記より3であることが分かり、(- 3 4)は、-1で、これらが*に渡り、結果として2になる。
  • というように、他の言語では、文法上、個別に特別な形をしているような処理も、Lispでは上記のようになんでも関数処理の流れとして扱い、一貫したものになっていたりする。

2007-12-21

Practical Common Lisp (12)

| 01:14 | Practical Common Lisp (12) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (12) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

S-expressions As Lisp Forms

  • リーダがテキストをS式に変換して初めて評価器の出番となる。
  • リーダが拾って来たS式、全部を全部評価できるというわけではない。
  • 評価器はLispのフォームを決定する役割があるが、これは非常にシンプルなルールに基いている。

4-どんなアトム(リスト以外のものと空リスト)も正しいLispフォームで、また、シンボルが第一番目の要素になっているリストも正しいフォームとして扱われる。

  • アトムは、シンボルと、それ以外の二種類に分類できる。
  • 評価されるLispフォームとしてのシンボルは現在保持している値となる。
    • "変"数という名前(Variable)にも注意しよう。値が"変"わらない"変"数もあり、"Constant Variable"と呼ばれる。(日本語だと、定数っていうし問題なしじゃなかろうか、どうなんだろう) 例としてはPIがCommon Lispには定数として定義されている。
  • それ以外のアトムは、それ自体が評価された値になるものになる。"Hello, World"を評価すると"Hello, World"
  • シンボルでもそういう例は存在する。Tと、NILを評価してもTとNIL、他には、キーワードシンボルが該当する。":"から開始されているシンボル。:FOOを評価すると、:FOO
  • リストの評価方法について
    • シンボルが第一番目の要素のリストは正しい表現として扱われるが、しかし、そういうリストには三種類あり、それぞれ随分と違った方法で評価される
    • 最初のシンボルの名前が、関数であるか、マクロであるか、スペシャルフォームであるか、の三種
    • 以降、関数呼び出しフォーム、マクロフォーム、スペシャルフォームと呼ぶことにする。

うーん、この節は自分の英語力で読むにはちょっと難しいなー。

2007-12-19

Practical Common Lisp (11)

| 22:07 | Practical Common Lisp (11) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (11) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。

S-expressions

  • 基本的なS式は、リストとアトムで構成される
  • リストは、括弧で区切られていて、任意の数の空白で区切られた要素を含むことができる。
  • アトムはそれ以外のすべて
  • セミコロンは、コメント記号で、それ以後改行までコメントとして扱われる
  • ということで、文法的に覚えるべきことは非常に少ない。あとはアトムの種類を覚えることくらいになる。
  • アトムには、数字、文字列、名前(シンボル名)がある。
  • 数字は、そのものずばり、数字で構成されたもの。+と-記号を付けて表記できる。小数点と、有理数の表記のための/、そして、指数表記もつかえる。
123       ; the integer one hundred twenty-three
3/7       ; the ratio three-sevenths
1.0       ; the floating-point number one in default precision
1.0e0     ; another way to write the same floating-point number
1.0d0     ; the floating-point number one in "double" precision
1.0e-4    ; the floating-point equivalent to one-ten-thousandth
+42       ; the integer forty-two
-42       ; the integer negative forty-two
-1/4      ; the ratio negative one-quarter
-2/8      ; another way to write negative one-quarter
246/2     ; another way to write the integer one hundred twenty-three
  • これらの数字は、整数、有理数、浮動小数点数と呼ばれるが、独自の表記方法を持つ、複素数も表現できる。これは10章で解説する。
  • 同じ数字を色々な表現で書くことが可能だということが分かるが、処理系が簡略化するので注意が必要。
  • -2/8や、246/2は、-1/4や、123となる。同様に、1.0と1.0e0は表記の違いということになる。一方、1.0と、1.0d0は違うものとして扱われるが、整数と、浮動小数点数は違うものとして扱われるため。詳細は、10章で。
  • 文字列は以前の章でダブルクオートに囲まれたものとして表現として目にしている。ダブルクオートを中に含めるには、\を直前に付けてエスケープする。エスケープして表現する必要があるのは、ダブルクオート(")と、バックスラッシュ(\)の二つのみ。
"foo"     ; f、o、oを含む文字列
"fo\o"    ; 同上
"fo\\o"   ; f、 o、 \、oで構成された文字列
"fo\"o"   ; f、 o、 "、oで構成された文字列
  • 名前(シンボルの名前という意味?)はLisp内のプログラムの名前として使用している、FORMATや、hello-worldや、*db*など。これらは、シンボルと呼ばれ、処理系は、名前自体が、変数を指しているのか、関数を指しているのか等々は気にしない。
  • 殆どすべての文字がシンボル名に使えるが空白文字は句切りとして使用されているため使えない。数字も使えるが、数字のみで構成されたものは数字自体と区別がつかないので駄目。また、ピリオド(.)も名前につかえるが、ピリオド(.)一文字のみというのは駄目。他に使えない文字というのは10種あり、()'"`,:;\|は使えない。
  • これらの文字を使いたい場合、そのままでは使えないが、\を前に置くか、||で囲むかすれば使える。
  • 重要事項として、リーダは文字の大文字と小文字を区別しないということがあり、全部大文字として扱う。Fooもfoo、FOOも同じ。しかし、\f\o\oと、|foo|は、fooとして扱われ、FOOとは違ったオブジェクトとして扱われる。REPLでfooという名前を打ち込んで、FOOと返ってくるのはこのため。
  • 今日の一般的なコーディングスタイルでは、全部小文字で書くが、結局のところリーダは全部大文字として解釈している。
  • 同じ表記のは常に同じシンボルとして扱われる。
  • リーダは、文字列を全部大文字に直し、シンボルとして取り込もうとする。その際にパッケージの名前一覧に同じ名前がないかをチェックする。見付からなかった場合、新しく一覧に登録される。見付かった場合は、先に登録されていたオブジェクトが使い回される。同じ名前ならばどこでも同一のオブジェクト使われているということでもある。
  • Lispは、Algol系の言語より多くの文字種が使えるということもあるが、ハイフン(-)で区切られた名前(例hello-world)が良く使われているところが特徴的。
  • また、*が前後に付いた変数名は、大域変数を表わすという習慣がある。同じく定数表記は、+を前後に付けるのが慣用表現。内部的なものは%で開始されることが多い、%%から開始というのも同様。
  • Common Lispの標準関数/変数名には、(A-Z)に加え、*、+、-、/、1、2、<、=、>、&、が使われている。
  • 文法に占める比重とは、数字、文字列、シンボルが多いが、他には、ベクタ、単体の文字、アレイ表現がある。これらは、10、11章で扱う。
  • 要点としては、これらをどう組合されてS式が構成されているかということだが、簡単な図を示す。
x             ; Xというシンボル
()            ; 内容が空のリスト
(1 2 3)       ; 3つの数字を含むリスト
("foo" "bar") ; 2つの文字列を含むリスト
(x y z)       ; 3つのシンボルを含むリスト
(x 1 "foo")   ; 数字とシンボルと文字列を含むリスト
(+ (* 2 3) 4) ; シンボルと数字を含むリストと数字を含むリスト
  • もう少し複雑な例としては、全体として4つの要素があるリストで、2つはシンボル、1つは空リスト、1つは、シンボルと文字列を含むリストということになる。
(defun hello-world ()
  (format t "hello, world"))

2007-12-18

Practical Common Lisp (10)

| 23:11 | Practical Common Lisp (10) - わだばLisperになる を含むブックマーク はてなブックマーク - Practical Common Lisp (10) - わだばLisperになる

引き続きPractical Common Lisp 第4章4. Syntax and Semanticsを読んでメモしてみています。文字ばっかりだと、英語が苦手な自分には何だか良く分かりません…。とりあえず、ウェブ翻訳で訳したりして適当に妄想してるメモになっております。

Breaking Open the Black Box

  • Lispの文法と意味論の詳細に立ち入る前に他の言語とはどういったところが違うのかを考察してみる。
  • 殆どの言語の処理系(コンパイラ、インタプリタ共に)はブラックボックスのようなものになっている。ユーザはテキストをそれに読み込ませて結果を受けとる。
  • ブラックボックスの詳細に立ち入るならば、もちろん沢山のパートに分けられる。字句解析や用いられるデータ構造等様々あるが、ブラックボックスたる所以は、その構造や機構が専らブラックボックス内部の処理プロセスで用いられるのみで、それ自体は、処理系製作者向けのものといった感じになっているところ。
  • Common Lispはちょっと違っていて、どう処理系を実装するかと、どう言語が定義されるかの二つが考慮された帰結として、一つのブラックボックスで処理されるのではなくて、二つのブラックボックスで二段階に処理される。テキストをLispのオブジェクトに変換するリーダが最初のもので、それを評価する評価器がそれに続く。
  • リーダは文字列をLispオブジェクトたるS式への変換を担当する。Lisp以外の言語でいうと処理系内部での構文木の生成にあたる。
  • 評価器は構築されたS式からLispの文法を定める。S式が全部正しいLispオブジェクトという訳ではない、例えば、(foo 1 2)、("foo" 1 2)は共にS式としては正しいが、Lispのフォームとしては、最初が文字列のリストはLispフォームとして意味を成していない。
  • ということで、リーダがどのようにS式を組み立てるか、と、評価器がどのように式を評価するのかの二つに焦点を当てて考察したい。