mokeheheのScheme日記

ツッコミ、添削大歓迎です。いろいろ教えてください。

2008-02-03

移転

g000001さんが、ものすごく活発で頼もしく感じ、その活動に参加したいと思うので、cadr部に移行したいと思います。

SICPも読んでないしね…。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080203

2008-01-30

Arc

g000001さんのところでArcという、Paul Graham氏が作ったという新しいLispが紹介されていた。

http://arclanguage.org/

正直、Arcのことはなにもわからないけど、Paul Grahamってことに興味がある。

g000001g0000012008/01/31 20:37Arcは自分も割とPG追っかけなので、触ってみてる感じなのですが、PG節は結構堪能できる気がします。でも、流行るかどうかは微妙だと思いましたw

mokehehemokehehe2008/02/02 23:44PG節とは、なかなかおもしろそうですね。
アナフォリックマクロなどを使われてるようですね。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080130

2008-01-29

Lispにバッククォートとカンマの処理を追加

一ヶ月以上ぶりに俺Lispのソースをいじくって、マクロ中で使えるようにバッククォートとカンマの処理を追加した。Scheme のように、バッククォートが現れたら quasiquote でくくり、カンマが現れたら unquote でくくる。で quasiquote の実行でサブツリーを走査して unquote 以外はそのまま、unquote は eval して返すようにした。

こういう処理はわざわざ C で組込みで書かなくても組込みLispに書けばいいと思ったが、最小簡単組込みLispを目指す身としては C で組み込んでおく。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080129

2008-01-27

Lispでいうところのコンパイルって、マシン語になるの?

Lispでいうところのコンパイルって、ネイティブマシン語に落ちるんじゃなくてバイトコードになるだけだよね?Lispのコードがマシン語に落ちるところを想像できないんだけど…。xyzzyもそうだしno title gauche仮想マシンで実行してるし。

と思ってたらGCLというGNUのCommonLispは、コンパイルするとCのソースを吐き出すらしい。マジで?単にバイトコードを実行するCソースが出るとかじゃなくて、例えば if がちゃんと分岐になったりするのかな?

小っちゃい処理系

俺にも理解できるような、小っちゃい処理系を探す。2ちゃんスレのテンプレから

CAMPUS LIsP
  • 1000行。わかりやすい。
  • マクロなし
TinyScheme
  • コンパイルして動いた
  • ソースファイルはほぼ1つだけど、4500行もあって、おっかない。
  • TinySchemeは、MiniSchemeを元に作られてるとのこと
MiniScheme
  • 1ファイルだけ。2400行。
  • coded by Atsushi Moriwaki (11/5/1989)
  • Revised by Akira KIDA
AMScheme
  • Atsushi Moriwaki Lisp
  • 内容的にはこっちのほうが古いのかな?
  • R4RS
  • ソースが小分けになっているので、場合によってはこっちのほうが理解しやすいかも。
KIScheme
  • 日本語コメントなので安心
bit

バッククォート

同じくR5RSの「4.2.6 準引用」に、「(quasiquote (foo bar))」 で「`(foo bar)」、「(unquote a)」で「,a」、「(unquote-splicing a)」で「,@a」と同じと書いてある。

関数引数

R5RS の「4.1.4 手続き」を読んでいて、

((lambda x x) 3 4 5 6)  ;=> (3 4 5 6)

とできることを知った。関数への引数destructuring-bind されてるってことかな?

gosh> ((lambda (x (y) . z) (list x y z)) 'a '(b) 'c 'd)
*** ERROR: unbound variable: y

違うのか。

また、コロンで残り引数を受け取れる

((lambda (x y . z) z) 3 4 5 6)  ;=> (5 6)

「&rest rest」じゃなくていいんだね。ま、これだと0個以上の引数を受け取る関数は定義できませんが。

たけおかたけおか2008/07/27 20:18通りすがりの者です(もう、ここはチェックしないかも)
&& Optimization of GCL/KCL を書いている者です。

> コンパイラどうなってんだ?
CommonLispならコンパイルそのものは、あんまり難しくありませんよ。直感どおりにコードを生成すればよし。インタープリタからの呼び出しも、難しくはないはず。
問題は、実行中のLispインタープリタに、コンパイルした機械語コードをリンクする、いわゆる「インクリメンタル・リンク」を実現するテクニックです。それは、OSや、リンカを十分に理解していないと、うまく実装できないでしょう。
また、もっと本質的に難しいのは、ガーベジ・コレクタが動いたときに、コンパイルド・コードとうまく共存するかです。すごく単純に言うと、コンパイルド・コードが一時的に、リスト(など)のポインタを機械語レジスタの置いたときに、ガーベジ・コレクタが、そのレジスタをチェックしないと、矛盾が生じる、というような問題が出るので。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080127

2008-01-26

qscheme を Windows で(失敗)

higepon氏の日記にQSchemeというリンクが張ってあった。見てみると

QScheme is a fast and small implementation of Scheme written in C. QScheme is easy to interface and should be easy to use as an extension language.

と書いてあるので、簡単に使えるのかと思ってダウンロードしてみた。

qschemeは

  • gmp-2.0.2.tar.gz
  • ffcall-1.6.tar.gz
  • pcre-3.1.tar.gz

に依存しているとのこと。VC でのビルドは無理そうかな…。cygwinコンパイルしてみる。

ffcall は、avcall, vacall, trampoline, callback の4つを含むライブラリで、qschemeでは avcall を使って任意の引数を持つCの関数を呼び出しているようだ。ffcallの ./configure は通るけどコンパイルがうまくいかない。avcall-i386.S からプリプロセッサで avcall-i386.s (小文字)に変換しているが、cygwin(windows) だとファイル名の大文字小文字の区別がないので、このせいでコンパイルできない。なので適当にファイル名を変える。vacall-i386.S も同様。

gmp は任意精度数演算ライブラリ。configure が cygwin に対応してなく、入れかたわからず。ググルと cygnwin のインストールで入れられるとのこと。cygwin を入れなおしたら qscheme の ./configure に成功。pcre はインストールしなくてもいいのか?

make をしてみる。vm2.c のコンパイル中に prim2.i(8) などでエラーが出る。Prim() マクロがおかしいようで、「l_##name##:」を「l_##name :」に変更。そうしても array.c で「scm_false が未定義」が出る。どこのヘッダにも定義してない。え~、どうなってるのよ~?

ううむ、ここまでか…。

gauche.night

gauche.night これ行きたいなぁ。

2008-03-08 (土) 17:00開場 18:00開演

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080126

2008-01-23

The Scheme Programming Language, 3rd Edition

The Scheme Programming Language, 3rd Edition もウェブで読めるとのこと。Scheme関連の本はウェブで公開されてる太っ腹な本が多くてうれしい限り。

これも読むリストに追加だ。目次の項目を見る限り、すごくオーソドックスな内容そうだな。

The Scheme Programming Language

The Scheme Programming Language

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080123

2008-01-14

Gaucheオンラインマニュアルから検索するフォーム

オンラインのマニュアルにリンクを張る場合、htmlファイルのURLマニュアルの改訂によってずれる可能性があるので、次のURLを使うと便利です。

http://www.shiro.dreamhost.com/scheme/gauche/man/?l=jp&p=トピック名
Gauche-ドキュメント-リファレンス

これを使った検索フォーム

  • 本家ページ内に配置していただけるとありがたいのですが、いかがでしょうか

setqの「q」って?

nreverseの「n」は、「no-cons」の「n」と On Lisp の中かなんかに書いてあったような気がするんだけど、

  • setqの「q」
  • setfの「f」
  • princ

は何の略なんだろう。

  • nconc: no-cons concatenate?
  • rplaca: replace car?
トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080114

2008-01-13

[] Practical Common Lisp第12章 LISPと呼ばれる所以:リスト処理

  • 12章でようやくリスト、ってどんな異端LISP本だよ!(いい意味で)
  • 昔はリストだけで、vectorハッシュテーブル、ユーザ定義構造体などはなかった
  • 映画 Matrix のセリフに見立ててなんか書いてあるけど、意味がわからず…
  • car, cdr はハナモゲラだから、first, rest を使おうか
関数プログラミングとリスト
  • append は最後のリストはコピーしない
破壊的操作


[] Practical Common Lisp第11章 コレクションの続き

http://gigamonkeys.com/book/collections.html

正月休み以来、ずいぶん開いちゃったな…。

ソートとマージ
  • sort や stable-sort は破壊的なのか
  • だけど、
(setf my-sequence (sort my-sequence #'string<))

と書かなきゃイカンとな。渡したシーケンスが破壊されるということであって、元のシーケンスがソートされるというわけではない、のかな?xyzzyでは大丈夫なようだけど。

部分シーケンス操作
  • subseq で部分シーケンスが取れる、ので部分文字列の取り出しもこれでできる
シーケンス述語
  • every, some, notany, notevery
  • Common Lisp だと not.every とか not.some とか関数の合成が短く記述できないから、いちいち用意しないといけないのが辛いね
  • 合成する関数を書くとこんな感じかね、funcall と apply が怪しいんだけども
(defun combine (fn1 fn2)
  (lambda (&rest rest)
    (funcall fn1 (apply fn2 rest))))
  • not.every
(funcall (combine #'not #'every) #'evenp #(1 2 3 4 5))  ;=> t 
  • not.some = notany
(funcall (combine #'not #'some) #'evenp #(1 2 3 4 5))  ;=> nil  '
  • 合成はできるけど、funcall を書かないといけないのがメンドイから、
(defun my-notevery (pred &rest rest)
  (apply (combine #'not #'every) pred rest))

とかして定義を楽にするしかないんだろうなぁ。

シーケンス写像関数
ハッシュテーブル
  • make-hash-table でハッシュを作れるけどキーの比較関数が eql なので、文字列をキーにする場合はうまくいかない、ので (make-hash-table :test 'equal) で作ればよい
  • このサンプルや他のものでも、ハッシュのキーとしてシンボル「'foo」を使ってるけど、キーワードシンボルなら何度評価してもキーワードのままだからキーワードシンボル「:foo」を使うべきではないのかね?この辺の使い分けがよくわからんなぁ
  • ハッシュにキーがないのか値がnilなのかを判定するために、gethash で多値を返して、それを multiple-value-bind で受け取ってあれこれするのってメンドウじゃないかなぁ?それより haskey? みたいなのが楽なのでは?
ハッシュテーブル走査
トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080113

2008-01-06

シンボルとキーワードシンボル

違うのか気になったのテスト:

(defun test (&key a)
  a)

(test :a 1)  ;=>1

(test 'a 1)  ;=>nil

違うのね。

g000001g0000012008/01/09 19:24こんにちは!
>|lisp|
(test 'a 1) ;=>nil
||<
の結果を見てアレ? エラーじゃないのかな?と思って、
Common Lispの処理系と、xyzzyで確認してみました。
普通のCommon Lispの処理系だと、エラーになるんですが、xyzzyだとnilになるんですね。
エディタとしての使い勝手を考えての仕様、とかでしょうか。
詳細は良く分からないのですが、とりあえず、ご報告まで。

mokehehemokehehe2008/01/09 22:32なるほど、本当ならちゃんとキーワードを与えないとエラーが出るものなんですね。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080106

2008-01-05

[] Practical Common Lisp第11章 コレクション

http://gigamonkeys.com/book/collections.html

  • Lispはリストだけじゃなくて、他の言語と同等に配列ハッシュも扱えるよ
  • vectorとlistは、より一般的な抽象化であるsequenceのサブクラスなので多くの共通点がある
vector
  • 固定サイズのvectorとリサイズ可能なvector
  • 固定:vector 関数で作成できる
  • より一般的な:make-array 固定も可変も作成可能、任意次元
  • :adjustable で実際にリサイズ可能に
vectorのサブタイプ
  • 文字列は要素がキャラクタに限定されたベクトルなので、次の章でベクトルを取る関数が文字列も受け取れることについて話す、とのこと。
  • make-array で、:element-type に 'character を与えれば、文字列が作れる
(make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)  ;=> ""
シーケンスとしてのvector
シーケンスをなめる関数
  • いろんなキーワードで動作調整可能
    • たくさんあって複雑という気がせんでもない
高階関数の種類
シーケンス全体の操作


[] Practical Common Lisp第10章 数値、キャラクタ、文字列

[] Practical Common Lisp第9章 実践:ユニットテストフレームワークの作成

  • テスト結果は成功か失敗のどっちか
2つの最初の試み
  • 全部のテストをandでつなぐ
    • これだとどこで失敗したかわからない
  • 個々に成功するか失敗するかを出力させる
    • いちいち書くのがメンドイ、あと数が増えたら困る
リファクタリング
  • 個々の結果出力を1つの関数 report-result に変える
  • 成功したかどうかと式とで2回書かなきゃいけないのを、check マクロを使って手間を省く
戻り値の修正
  • report-result から戻り値を返すようにする
  • and マクロだと失敗した時点で処理を打ち切ってしまうので、打ち切らない combine-results マクロを作る
よりよいレポート結果
  • いくつかテストケースを組み合わて、増えすぎた場合に今のレポートだと多すぎて探すのが大変
  • グローバルにテスト名を保持する変数を作って、テストで設定し、結果表示する
抽象化あらわる
  • テストケースの関数名と、テストケース名で2回設定してるのがムダ
  • テスト関数だけど、それはあなたの心の中だけで、コードが特定のパターンにのっとっていること以外でテスト関数だとはわからない
  • 中途半端な抽象化はソフトウェア構築の安っぽいツールにしかならない
  • なぜなら中途半端な抽象化はパターンのマニフェストによってコードに表現されるので、大量のコードを複製する(メンテに問題が出る)ことで保障しなければならないからである
  • より微妙には、抽象化はプログラマの心の中だけにあるので、別のプログラマ(または未来の自分)に同じ抽象化を理解させる方法がないことである
  • 完璧な抽象化のためには、「これはテスト関数だ」ということを表現する方法と、パターンが必要とするすべてのコードが生成される必要がある
  • すなわちマクロ
  • deftest マクロを作る
テストの階層化
  • *test-name* をリストにすればいい
まとめる
  • 全部で26行!

Lispのリーダー

バッククォートとかカンマとかカンマアットとか、Lispのリーダーでどうやって処理してるんだろう?xyzzyでテスト:

(setf a 1234)
(setf ls '(1 2 3))

としておいて、展開形は

`(hello ,a)          ;=> (hello 1234)
`(hello ,a world)    ;=> (hello 1234 world)
`(hello ,ls world)   ;=> (hello (1 2 3) world)
`(hello ,@ls world)  ;=> (hello 1 2 3 world)

クォートをつけて、展開される前はどんなかをみると

'`(hello ,a)         ;=> (list 'hello a)
'`(hello ,a world)   ;=> (list* 'hello a '(world))
'`(hello ,ls world)  ;=> (list* 'hello ls '(world))
'`(hello ,@ls world) ;=> (cons 'hello (append ls '(world)))

うへぇ、場合分け多いな。

ryos36ryos362008/01/06 10:44えらい勢いで読んでますね。すばらしい。私は CH3, CH23,CH24,ch26 を斜め読み程度。う~ん。

mokehehemokehehe2008/01/06 12:43英文をかなり適当に読み飛ばしてますからね。
Peter Seibel氏の英文がわかりやすく、説明がうまいですね。
ryos36 さんはSchemeやLispをすでにご存知のようですので、
この本では簡単すぎるのではないですか? (^^;;

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080105

2008-01-04

[] Practical Common Lisp第8章 マクロ:自分のを定義する

http://gigamonkeys.com/book/macros-defining-your-own.html

  • マクロ理解への障壁は、皮肉にもそれが言語によくマッチしてるからだ。マクロはちょっと変わった関数にしか見えない。
  • しかしマクロ関数とは異なるレベルであり、異なる種類の抽象化をもたらす
Macの話:単なる話

マクロがないLispを使ってた会社にMac氏が現れて、記述の置き換えの仕事をしていた、そしてそれはコンパイラに組み込まれましたとさ

マクロ展開時 vs 実行時
defmacro

マクロを書くステップ:

  1. マクロのサンプル呼び出し、展開されるべきコードを書く
  2. サンプルコールの引数から手で展開したコードを書く
  3. マクロの抽象化に漏れがないようにする
サンプルマクロ:do-primes
  • 素数でループする、do-primes を書く
  • loop を do で書くと
(defun primep (number)
  (when (> number 1)
    (do ((fac 2 (1+ fac)))
        ((> fac (isqrt number)) t)
      (if (zerop (mod number fac))
          (return-from primep nil)))))

(defun next-prime (number)
  (do ((n number (1+ n)))
      ((primep n) n)
    ))
  • いいかげんloopからdoへの書き換えはメンドイな。loop マクロが書ければいいんだけど。
マクロパラメータ
展開の生成
  • バッククォートを使えば、カンマやカンマアットでクォートを打ち消すことができる
    • カンマアットだとリストをその場に展開する
  • 展開をチェックするには、実行結果を見るか
  • macro-expand-1 を使って展開されたコードを見るか
    • macro-expand-1関数なので、クォートする必要がある
漏れを塞ぐ
  • Joel Spolskyの"The Law of Leaky Abstractions"の中で、抽象化をだいなしにしてしまう、詳細が漏れる抽象化のことを漏れやすい抽象化という用語を作り出した。
  • マクロも抽象化の方法だから、漏れのないようにしないとね
  • 現在の定義は漏れる可能性が3つある
(defmacro do-primes ((var start end) &body body)
  `(do ((ending-value ,end)
        (,var (next-prime ,start) (next-prime (1+ ,var))))
       ((> ,var ending-value))
     ,@body))
      • 残念なことに、この修正がマクロ抽象化に新たに2つの漏れを発生させる
    • 2. start と end の評価順が違う
(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
        (ending-value ,end))
       ((> ,var ending-value))
     ,@body))
    • 3. "ending-value" という変数名を使ってしまっている
      • package を使って解決もできるが、もっといい方法がある
      • getsym 関数は呼び出されるたびにユニークなシンボルを返す、これを使って
(defmacro do-primes ((var start end) &body body)
  (let ((ending-value-name (gensym)))
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
          (,ending-value-name ,end))
         ((> ,var ,ending-value-name))
       ,@body)))

マクロ作成時には以下のルールに従え:

  • マクロ呼び出しのパラメータ順に評価しろ
  • 部分式の評価は一度だけにしろ
  • 展開時に使われる変数にはgetsymを使え

リンク:

マクロを書くマクロ
  • マクロを書くときは、マクロ展開時に使う変数を導入するために、大抵 let で始まる。ならこの定型処理をマクロ化しよう!
(defmacro with-gensyms ((&rest names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
     ,@body))
他の古くからあるマクロを書くマクロ:once-only
(defmacro once-only ((&rest names) &body body)
  (let ((gensyms (loop for n in names collect (gensym))))
    `(let (,@(loop for g in gensyms collect `(,g (gensym))))
      `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
        ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
           ,@body)))))
  • なにをやってるのかよくわからん
    • 一番外側の (let ...) で、once-only に与えられたシンボル用にgensymをして、
    • その内側の `(let ...) で1回だけ評価した値を格納するためのシンボルを作成、
    • その内側で once-only に与えられたシンボルを評価してテンポラリに格納
    • その内側で once-only に与えられたシンボルと同じ名前にバインドしなおす
  • 一番最初の (let ((gensyms ... で、gensyms というシンボルを使ってしまっているため、もしonce-onlyの外側でgensymsを使ってたら動作がおかしくなると思うのだが…。

g000001g0000012008/01/05 02:15はじめまして、自分もPCLを読んでみている者です。
自分はxyzzyはちょっと使ったことがある程度なのですが、
loopマクロはxyzzyにもlispディレクトリ以下に隠れてるらしいというのをどっかでみた記憶があったのを思い出して確認ちょっと確認してみました。
xyzzyだと(require "cmu_loop")で使えるようです。
cmuclから移植したのでこんな名前なのかもしれないですね。

mokehehemokehehe2008/01/05 07:19g000001さん、はじめまして。ブログ読ませてもらってます (^^;;
お書きのとおり、(require "cmu_loop") で本の中の loop が実行できました!ありがとうございます。

PCL を読むまで loop マクロというものを知りませんでした。
マクロだけでこんなムチャクチャできちゃうのはすごいですね。
loop は読みやすくていいですね。自分で書こうとは思いませんが (^^;;

DebraDebra2011/04/11 01:34u9f3XY Touchdown! That's a really cool way of putting it!

lytiwalytiwa2011/04/12 04:50ZECap3 <a href="http://srzjytyhymno.com/">srzjytyhymno</a>

yoshwamtyoshwamt2011/04/13 06:57UsHCYm , [url=http://fpvhnjgcsbyr.com/]fpvhnjgcsbyr[/url], [link=http://dujzatdeswnu.com/]dujzatdeswnu[/link], http://bgdvkybuwzjd.com/

rkmntuurkmntuu2011/04/13 06:57wGmkbz , [url=http://pidabylfpbmq.com/]pidabylfpbmq[/url], [link=http://xekbosghhjxg.com/]xekbosghhjxg[/link], http://hyfwxibcjdmv.com/

ckazhukckazhuk2011/04/23 03:24lcc4uc <a href="http://euvnhygtrqom.com/">euvnhygtrqom</a>

fvqnydtirfvqnydtir2011/04/24 11:46kkgpYy , [url=http://zdkboicpubmh.com/]zdkboicpubmh[/url], [link=http://zeliqhxdzinl.com/]zeliqhxdzinl[/link], http://ahsqzzzpbfdq.com/

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080104

2008-01-03

[] Caesar cipher

TBS | 404 NotFound記念に、anarchy golf - Caesar cipherを解く:

; 1を足す
(define (1+ x) (+ 1 x))

; 文字を回転させる
(define (rotate-char c start-char end-char n)
        (let ((cc (char->integer c))
              (st (char->integer start-char))
              (ed (char->integer end-char)))
          (if (and (<= st cc) (<= cc ed))
              (integer->char (+ (modulo (+ (- cc st)
                                           n)
                                        (1+ (- ed st)))
                                st))
            c)))

; 文字列の文字ごとに関数を適用する
(define (string-map fn str)
        (list->string
         (map fn
              (string->list str))))

; 文字を暗号化
(define (caesar-cipher-char c n)
        (rotate-char (rotate-char c #\A #\Z n)
                     #\a #\z n))

; 文字列を暗号化
(define (caesar-cipher str n)
        (string-map (lambda (c) (caesar-cipher-char c n))
                    str))

(let ((str (read-line (current-input-port) #t)))
  (dotimes (i 26)
    (print (caesar-cipher str i))))
  • エレガントさのかけらもない
  • Schemeで数式を扱うと超わかりにくいな…

はてな記法に悩まされている

スーパーpre記法(シンタックス・ハイライト)が怪しい。バッククォート「`(」やスター「*)」などがあると中身が表示されなかったりする?スペースをつけると動作が変わる。謎。

[] Practical Common Lisp第7章 マクロ

わずか7章でもうマクロキター

  • 条件式やガベージコレクションなど、Lispをオリジナルにする多くのアイディアが他の言語に影響を与えた一方、引き続き Common Lisp を特別なものにしている特徴は、マクロのしくみだ。
  • しかし、違う意味のマクロが広まってるおかげで、Common Lispマクロがいかにすばらしい特徴かを説明するのに苦労する
  • プログラミング言語を、言語コア+標準ライブラリと定義すると、理解しやすく実装しやすくなる
    • しかし、本当の利点は表現力にある
    • 言語をライブラリだけと考えれば、その言語は簡単に拡張できるでしょ?
  • マクロを使えば、新たな文法を作ることができる
when と unless
  • if の then, else には式をひとつしか与えられないので、それ以上の式を与えたい場合 progn を使う必要がある
    • if と progn を一緒にやってくれるのが欲しい
    • それ when でできるよ
(defmacro mywhen (condition &rest body)
  `(if ,condition (progn ,@body))) 
  • 取るに足らないマクロの例だけど、利点はでかい
cond
  • if ... else が複数つながると、インデントも深くなるし醜い
  • cond でスッキリ
and, or, not
  • and は偽が現れた時点で、orなら真が現れた時点で、残りの評価を打ち切る、のでマクロで定義される
ループ
  • Lispの25の特殊オペレータは直接ループ構造をサポートしてはいない、マクロが構築している
  • do: 汎用
    • dolist, dotimes: リスト、回数ループに特化したもの
  • loop: ミニ言語ぽい、Lispらしからぬ、英文ぽいループ用マクロ
    • 賛否両論
dolist と dotimes
do
(do (variable-definition*)
    (end-test-form result-form*)
  statement*) 
  • do を使ってフィボナッチ数の計算
オールマイティなloop
(loop
  body-form*)  
  • 複雑な使い方:
    • (loop for i from 1 to 10 collecting i) ==> (1 2 3 4 5 6 7 8 9 10)
    • (loop for x from 1 to 10 summing (expt x 2)) ==> 385
    • (loop for x across "the quick brown fox jumps over the lazy dog" counting (find x "aeiou")) ==> 11
  • (loop for i below 10 and a = 0 then b and b = 1 then (+ b a) finally (return a))
  • 22章で詳しく説明
  • loopはwhenやifよりちょっと複雑なだけで同じマクロだから、もしloopが標準ライブラリになかったとしても自分で実装できるよ
"Practical Common Lisp"を読んでる方:

[] Practical Common Lisp第6章 変数

http://gigamonkeys.com/book/variables.html

変数の基礎
レキシカル変数クロージャ
(let ((count 0)) #'(lambda () (setf count (1+ count))))
動的変数(特殊変数
  • defvar, または defparameter
  • 慣習的に名前を *...* とする
  • defparameter は常に値のセットを行う、defvar は変数が未定義の場合だけ
  • 実際には、defvar を使うべき
  • defvar や defparameter で定義した変数は特殊となり、動的変数になる。名前で見分けないと酷い目にあう。

よくわからん

定数
  • defconstant
  • 慣習的に名前は +...+
  • すでに定義済みの定数を違う値でもう一度定義しても値は変わらない
代入
  • setfマクロ
    • 格納先が変数の場合:setq 特殊オペレータ
  • ちょっとこれは理解不能だ~
(defun foo (x) (setf x 10))

(let ((y 20))
  (foo y)     ; foo の結果はyには反映されず
  (print y))  ; => 20

setf とか setq の、f とか q ってなんなのさ?

一般化された代入
  • 配列ハッシュテーブル、リスト、ユーザ定義データ構造も値を保持できる
  • どれも setf で値をセット可能
他の方法
  • incf, decf
  • push, pop, pushnew
  • (rotatef a b)
    • (let ( (tmp a)) (setf a b b tmp) nil)
  • (shiftf a b 10)
    • (let ( (tmp a)) (setf a b b 10) tmp)

Write Yourself a Scheme in 48 hours

no title

以前はHaskellSchemeもろくにわからなかったので、さわりだけでちんぷんかんぷんだったけど、今なら読めるかも。読むリストに追加しておこう。

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080103

2008-01-02

[] Practical Common Lisp第5章 関数

新しい関数の定義
関数パラメータリスト
オプションパラメータ
  • 関数引数リストに &optional をつける
  • デフォルト値を nil 以外にしたい場合は (param-name vaue) にする
  • 引数が与えられたかどうか知りたい場合は (param-name value supplied-p) とする
残りのパラメータ
キーワードパラメータ
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
  (list a b c c-supplied-p))
上記のパラメータを混ぜて使うとき
関数戻り値
  • 関数の最後の式の値
  • return-from 特殊オペレータで
    • return-fromは関数に結び付けられるのではなく、block特殊オペレータから抜ける
    • defunは関数全体を同名のブロックで囲むため、関数から抜けることができる
  • return-from の最初の引数(ブロック名)はクォートの必要なし
  • 関数名を指定して return-from だと、関数名を変えたときに一緒に変えなきゃいけなくてメンドイ、けどreturn-fromを陽に使う機械はそんなに多くないので実際はたいして問題にならない
データとしての関数高階関数

xyzzy-lispでは loop が単なる無限ループなので、plotはdoを使って

(defun plot (fn min max step)
  (do ((i min (+ i step)))
      ((> i max))
    (dotimes (j (funcall fn i)) (format t "*"))
    (format t "~%")))
無名関数

[] Practical Common Lisp第4章 シンタックスとセマンティクス (2)

関数呼び出し
特殊オペレータ
  • 関数だと、呼び出される前に引数がすべて評価されてしまうので、ifなどが表現できない
マクロ

特殊オペレータとマクロって何が違うの?いっしょでよくない?

真、偽、そして等値性
  • eq: オブジェクト同値。実装依存なので、数値やキャラクタの比較に使うべきではない。
  • eql: 同じクラスのオブジェクトの2つの値が等しいか。同じクラスで値が等しい場合は真であることが保証される。別のクラスの場合等しくないことが保証される。
  • この本では常にeqlを使う
  • equal: リストや文字列の中身を見て比較してくれる。あとはeqlと一緒
  • equalp: 大文字小文字を同一とみなす。整数と浮動小数も。リストの要素同士がequalpの場合、真。あとはeqlと一緒
Lispコードの整形
  • 文法や意味的にではなく、コードの読み書きのためにインデント重要
  • 閉じ括弧は別の行に書くな
トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080102

2008-01-01

[] Practical Common Lisp第4章 シンタックスとセマンティクス

てきとーに飛ばしつつ

なぜ括弧だらけなのか?
  • 最初は、人間が書くときはM式(meta-expression) で書いてそれをS式に変換するつもりだったけど、結局S式のほうが好まれた
ブラックボックスを開ける
S式
Lispの形としてのS式
  • リーダーがテキストをS式に変換した後、S式Lispコードとして評価される
  • キーワード(:)とクォートつけたシンボル(')の違いわかった。キーワードは評価してもキーワードのままだけど、クォートをつけたシンボルは評価すると変数の値になってしまうんだね
トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20080101

2007-12-31

プログラミングGauche」の本が出る

プログラミングGauche - Ohmsha

Gauche 作者の川合史朗さんが監修、内容は503 Service Unavailableに大幅に加筆修正したもの、Kahua という継続ベースのアプリケーションフレームワークを使った内容、とか。超期待だぁ~!

[] Practical Common Lisp第3章

実践:シンプルなデータベース
  • 関数マクロとスペシャルオペレータ:ここでは説明しないけど、ちょっとずつ違うと思ってくれ
  • plist(プロパティリスト)
  • シンボル:名前だと思ってくれ
    • 「:」始まりでキーワードシンボルという、特殊なシンボルになるとは初めて知った
    • Rubyでのシンボルですね
  • (list :a 1 :b 2 :c 3) でplist作成
  • (getf (list :a 1 :b 2 :c 3) :a) でplistから取り出し
  • プロンプトを出してCD情報の入力補佐関数
    • *query-io* がよくわからず。(setf *query-io* t) 、(read-line) 、と変更
    • y-or-n-p はミニバッファにプロンプトが出て、キーを押した後も消えない
    • with-standard-io-syntax が定義されてない。無視。
データベースクエリ
  • (select :artist "Dixie Chicks") で探せるようにしよう
  • remove-if-not : 二重否定で分かりづらいけど、Haskell でいうところの filter
    • remove といっても、元のリストを変更するわけではなく、変更したものが戻り値として返る
(select (where :artist "Dixie Chicks"))
(select (where :rating 10 :ripped nil))

と書けるようにするのは見事としかいいようがない。

レコードの更新
(コードの)重複を削除して、大きく勝利
  • where 関数内で、要素ごとに下のように書いているのが美しくない!
(if title (equal (getf cd :title) title) t)
  • それに :title とか渡してるところで、渡してないものまで実行時にチェックするのがムダ
  • そこでマクロですよ
  • マクロコンパイル時に実行されるから、実行時にはノーコストだよ
  • (loop while fields ...) というのがエラー
    • (loop ...) は xyzzy では無条件ループ
    • (while fields ...) に変更
  • collecting とは?
    • loop~collecting で、結果をリストとして返す?
  • xyzzy lisp で make-comparisons-list はこんな感じかな?
(defun make-comparisons-list (fields)
  (let ((ls ()))
    (while fields
      (push (make-comparison-expr (pop fields) (pop fields)) ls))
    (reverse ls)))
  • macroexpand-1 で、マクロを展開した結果が見れる
(macroexpand-1 '(where :title "Give Us a Break" :ripped t))
#'(lambda (cd) (and (equal (getf cd :title) "Give Us a Break") (equal (getf cd :ripped) t)))

ここまでほとんど一切細かい文法や仕組みを説明しないのもすごいな。ある程度分かってる人前提?

Wrapping Up


補習
  • update中のコードの重複を取り除いてみる
  • (setf (getf row :title) title) のようなリストを作るマクロを作ればいい
  • where のときに習って:
(defun make-setf-expr (field value)
  (list 'setf (list 'getf 'row field) value))

(defun make-setfs-list (fields)
  (let ((ls ()))
    (while fields
      (push (make-setf-expr (pop fields) (pop fields)) ls))
    (reverse ls)))

(defmacro update (selector-fn &rest clauses)
  `(setf *db*
         (mapcar
          #'(lambda (row)
              (when (funcall ,selector-fn row)
                ,@(make-setfs-list clauses))
              row) *db*)))
  • defmacro 内の、selector-fn にカンマをつけるのがわからなくてハマッた
  • こうなると更に、where と update をまとめたくなるのう

りっぷるりっぷる2008/09/08 22:33>xyzzy lisp で make-comparisons-list はこんな感じかな?

こんなのは、いかがでしょうかー。

(defun make-comparisons-list (fields)
(if fields
(cons (make-comparison-expr (first fields) (second fields))
(make-comparisons-list (rest (rest fields))))
nil))

トラックバック - http://sicp.g.hatena.ne.jp/mokehehe/20071231