cut-sea:log4

cut-sea:log4

Kahua

世の中にはセッションステートを利用してるWebアプリってのがある (つーかほとんどそうなのかもしれないけど)。
コレはブツ切れになるリクエスト-レスポンスを一回の関数の呼び出しだと考えると、 言わば大域変数を使って関数間で値を授受してるようなもの。

大域変数だけでプログラムを組みたいなんて方は、イマドキあまり居ないでしょ?

しかも最悪なことに、このセッションステートって奴は、 サーバにとって現在注目中のほげほげであり、 ブラウザの向こうにいる人にとっての注目中のほげほげとは別なのです。 実際ブラウザのバックボタンを押したり、タブやウィンドウを複製するだけで 容易にそういう状況を作り出せます。これに苦しめられた人も多いはず。

一方、関数引数のようなものがクエリストリングだと考えられる。 ところが、クエリストリングでの値の授受は文字列じゃなきゃダメ という厳しい制約がつく。(当たり前だストリングなんだから)
つまりサーバ-クライアント間でシリアライズ可能じゃなきゃダメなわけ。
ハッシュやアレイなどもブラウザへ向かってシリアライズ可能な 文字列にしてやる必要がある。 当然受信した文字列からそれらをサーバサイドに再構成する必要もあるし、 巨大なハッシュテーブルやアレイリストなどシリアライズ可能な形式にできても、 同時に膨大な情報を流すコストが発生する場合だってある。
あるいは手でurlを注入される可能性も考えるとチェックにも熱を入れなきゃなるまい。 たまたま他人のオブジェクトを掴めましたなんて冗談じゃない。
結局巨大でシリアライズ困難なものはサーバサイドでセッションステートに 保持ってのがよくやるパターンだけど、さっきも言ったとおり、 ブラウザの向こうの人が注目してるものと違うものを掴んでる瞬間が いっぱいあることを考えると私のようなマジメな人はゾッとして夜も眠れないわけ。(笑)

さらにクロージャなんぞは送受信すらほぼ絶望的なので高階プログラミングができない。 引数として「今のコンテキストから動的に作成したほげほげをする関数」を 次の処理(リクエスト-レスポンス)に渡したいと思っても手が届かないので、 クロージャだなんだと、今風を気取ってるプログラマでも、そのじつ、 クロージャのないプログラミング言語と変わらないコード吐いてたりして ナサケナイ思いをする。

継続ベースのWebアプリってのは、そこにローカルなスコープで 利用可能なセッションステートを導入するようなもの(と言っておこう)。
シリアライズが困難なオブジェクトはサーバ側だけで保持し、 次のリクエスト-レスポンスに継続を使って授受すればよく(※1)、 だから関数引数だってへっちゃらだ。 じゃんじゃんクロージャを作って渡しまくってもOK。
そのオブジェクトやクロージャは、大域的なスコープには無いから 壊れないし誰にも見えない。(※2)
私の書いたいくつかのちっぽけなKahuaプログラムでもクロージャをすき放題授受してる。
つーか、Schemerにクロージャを使うなって言うのは、手足を縛るようなもの、 あるいは、ほとんど息すんな、死ね!と言ってるようなもの。
こうして私が生きてるってことはKahuaでもいたるところでクロージャを使ってるわけだ。 使ったかどうかなんてイチイチ確認なんかしちゃいねぇ。

何が言いたかったかというと、我らが継続ベースフレームワークの Kahua 1.0が出るらしいというウワサ(笑)なのよ。
そろそろWebの世界でも大域変数だけの大昔風のプログラミングをしてないで、 ちゃんとローカルスコープ(笑)を使いたい人は、 第三回Kahuaセミナー にふるってご参加ください。 ちなみに私はその日は裏磐梯でスノボしてますのでゲレンデで笑い転げてます。(wcut-sea:2006/12/24 18:20:59 PST

(※1): 実際にはKahuaではレキシカルクロージャにおけるフリーバリアブルの参照が 別々のリクエストレスポンス間での授受に相当するので、 プログラマには授受するというつもりすらない。
要は入れたり出したりが好きなら止めないけど(笑)、そんなの不要だってことだ。 当然間違えて入れたり出したりすることも皆無になる。
(※2): 大域的でないというか、継続手続きという名のクロージャに閉じ込められている。 ここにアクセスする手段はページに埋め込まれた継続リンクもしくはアクションからだけ。 そこからだけ、その時点で注目していたオブジェクトをダイレクトに触れる。
そのオブジェクトをプログラマの代りにどっかにつっこんでくれてるんじゃないよ。 そのオブジェクトを触っていた時点の継続を起動するので、 別のリクエストでありながら、時間を飛び越して(その過去の時点の続きとして) 仕事を再開するんだ。 そこでは、当然のようにそのオブジェクトにアクセスできるよね。 だから外からは見えなくても、その時点の評価環境から見えるもの全てに手が届く。


Lispセミナー

11/21-11/22の二日間行ってきた。 個人的には11/21の最後にあった「AllegroCache でつくってみよう、自分の Google」が一番面白かった。

まぁタイトルがキャッチーなので、誤解されそうだけどGoogleは関係ない。(w
Lispを使って仕事した事例紹介の一つとして、論文の検索システムみたいのを構築する話。

最初はNamazuを使ってインデクシングしてやる話なんだけど、 実際にフタを開けてみたら処理しなきゃならない論文の件数が 2桁違う(年間5000件→50万件)という。(w
理屈はしらないけど、件数に対して指数関数的に時間がかかるものらしく、 実験してみたらNamazuじゃ到底ムリポとなったらしい。

そこでACacheを使うんだけど、最初のナイーブな実装ではNamazuよりさらに遅い。 そこから3つ位改善を施してぐんと効率を上げてしまうんだが、 そのうちの一つにMOPを使った改善があり、これがもう素晴らしすぎるくらい面白い。 原理は誰もがしっているテクノロジなのだが、 普通のOOに慣れていると、出来っこないだけに考えすらしなさそう。

結局ね、最近個人的にキレイかどうかの議論に没入しがちなんだけど、 こういう、無理そうなものを可能にする泥臭い処理をやってみせることこそが Lispを認めさせる道なんだろうなぁ。

この事例を解説された数理システムの黒田氏は、懇親会とかでも 「SchemeなんかをCLと一緒にしないで欲しい。」 とSchemeをけちょんけちょんにおっしゃるわけで、とても悔しいわけだが、 それでもとにかく実際の業務を通して実力を示しておられるからこそ、 あそこまで自信を持って主張できるのだろう。

でもSchemeも同じはず。 多分Practicalだということを証明するためには、 どうであれ動作するものを見せるしかないんだなぁと。
11/23は早速同じようなMOPいじり倒しのメタクラスをGaucheで書いてみる。
実際そうしてるんだから当然なんだけど、動くと感動モノ。 やっぱMOP最高だわ~と陶酔しちゃってへろへろになる。 cut-sea:2006/11/23 19:37:13 PST


Amazonさん

早い早いよAmazonさん。cut-sea:2006/11/19 02:57:24 PST


思いついた。

ふと面白そうなアイディアを思い着いたかも。 少し練ってみよう。cut-sea:2006/11/04 19:27:31 PST


Google誕生 ガレージで生まれたサーチ・モンスター

だいぶ以前に発刊されて、本屋で見て面白そうだったので、 隣席の同僚をそそのかしてポチらせた。
その後で回してもらい、長いこと持ちっぱなしだったんだけど先日土日でガッツリ読了。 (ナナメ読み入ってるけど…)

で、やっぱり再確認したのは、彼らもやはり結構行き当たりばったりなのだ。 とにかく走り始めてしまう。 自分達のやっていることを必要としてくれている人たちが出てくれば、 なんとかなるってことかな。 ガソリンはそういう人たちが(直接ではないかもしれないが)供給してくれる。 (勿論ギブミーガソリン!ってSOS発信はしなくちゃならない。)

案外そういうものかもしれない。 大企業にいたから分かるけど、そういうところでは予算を取ったり、 採算が合うかを検討したり、反対意見に対して説明したりということに、 相当な時間と労力を費やす必要があり、話が通った時点ですでに疲弊してたり、 やる気が凹まされたりしてることがある。 そうこうしてる内に走り出してるやつらは、 もうとっくに見えないところに行っちゃってるわけですね。

そうそう、ふと思い出したが、その隣席の友人に以前言われた魔法の言葉

「それってGoogleでも出来ないと思うか?」

エンジニアでありつづけるなら心しておきたい。cut-sea:2006/10/24 20:08:33 PDT


某D大にいってきた

日本最初のハッカーさんにして、現第2版SICPの翻訳者であるW先生が 某D大にて講義をされているという情報をもらい、聞いてきた。 自動車免許の更新にかこつけて、いくつかの私用の後で現場へ。
nobsunに紹介してもらって、 「すみません。講義を聞かせて頂きます。」とご挨拶をしてから最前列へ。
W先生のお弟子さんに当たるI先生にもご挨拶。 こちらは最近私がいじらせてもらっているLazyなScheme処理系の作者さんでもある。 随分深々と頭を下げて挨拶されたので、腰の低い先生だなぁと恐縮してしまった。
講義は今回については実にSICP的なんだけど、もー楽しくって楽しくって。 8進ソロバンの噂は聞いてたけど、さらに桁数にまで秘密があったとは(w
LISP対応だったのかと納得すると同時に、ハッカーのハックというのは、 最初の一発だけでも笑かしてくれるのに、その次の一歩を踏み込むものだなぁと感心。
昨年までこの講義はかのT先生だということらしく、 なんて贅沢なんだD大生!と激しく嫉妬。
あと、大学生ってえらい子供子供してるぞ。 こんな若いんだっけ?

fact/fib of SICP Sec.5 on LLL(not LL)

言語処理系の評価器のコードを触ってると、 アセンブラっぽいコードになってるのに気付きますよね。
自前でヒープに作ったスタックだけど、そいつにpushしたり、popしたり。
Cとかで書いてると、逆に作らなきゃならない分だけ面倒なんじゃないかと思った。 (まぁ実際はそんなこたないわな。Cを甘く見過ぎ。)

さて、問題はcontinueレジスタをどうするか。 call/retモデルではダメなのよ。呼び出し元に戻ってきちゃうと意味をなさない。

とりあえず、その辺を考えてみよう。 と久々にLLL(Low Level Language 要するにAssembler)を(w

SICP Sec.5 fact on LLL

.file   "fact.s"
.data
fmt:    .string "fact(10) = %d\n"

/*
 * %esi = cont
 * %eax = val
 * %ecx = n
 */
.text
.globl main
main:
        pushl   %ebp
        movl    %esp,           %ebp

init:
        movl    $fact_done,     %esi
        movl    $10,            %ecx
        
fact_loop:
        cmp     $1,             %ecx
        je      base_case

        pushl   %esi
        pushl   %ecx
        dec     %ecx
        movl    $after_fact,    %esi
        jmp     fact_loop

after_fact:
        pop     %ecx
        pop     %esi

        mull    %ecx

        jmp     *%esi

base_case:
        movl    $1,             %eax
        jmp     *%esi

fact_done:
        pushl   %eax
        pushl   $fmt
        call    printf
        leave
        ret

ミソはcall/retモデルが使えないので、continueレジスタの役割をどうやるかだけど、 jmp <eax>って命令があるのをGoogleで知ったので、こいつが使えそうだ。 つーか、それ以外に方法が見当たらないので選択の余地なし。 cut-sea:2006/10/16 19:07:14 PDT

SICP Sec.5 fib on LLL

.file   "fib.s"
.data
fmt:    .string "fib(30) = %d\n"

/*
 * %edi = cont
 * %eax = val
 * %ecx = n
 */
.text
.globl main
main:
        pushl   %ebp
        movl    %esp,           %ebp

init:
        movl    $fib_done,      %edi
        movl    $30,            %ecx

fib_loop:
        cmp     $2,             %ecx
        jb      immediate_answer

        pushl   %edi
        movl    $afterfib_n_1,  %edi
        pushl   %ecx
        dec     %ecx
        jmp     fib_loop

afterfib_n_1:
        popl    %ecx
        popl    %edi

        subl    $2,     %ecx
        pushl   %edi
        movl    $afterfib_n_2,  %edi
        pushl   %eax
        jmp     fib_loop

afterfib_n_2:
        movl    %eax,   %ecx
        popl    %eax
        popl    %edi
        addl    %ecx,   %eax
        jmp     *%edi

immediate_answer:
        movl    %ecx,           %eax
        jmp     *%edi
        
fib_done:
        pushl   %eax
        pushl   $fmt
        call    printf
        leave
        ret

同じ調子で、fibも書いた。 コードはSICPを写経してるだけなので、 continueレジスタの使い方さえ押さえてしまえば、 なんら問題なく、一撃で動作確認できる。

重要なのは動作を追うことなので、gdbでじっくり意図した動作か確認。 まぁ、あるイミLLL('LL' ++ 'L')最強ってことで。cut-sea:2006/10/16 18:58:24 PDT


おあそび

foldっぽいものを使う局面では最近自分で専用に作りたくなる…。 以前Gauche:whitespaceでもそれっぽいのを書いたけど、 今回はこれをやってみる。

(use srfi-1)
;; my fold
(define (fld f n s ls)
  (if (null? ls) s
      (fld f n (f (car ls) s) (n ls))))
;; unique
(fld (lambda (x s)
       (append s (list x)))
     (lambda (xs)
       (remove (pa$ equal? (car xs)) (cdr xs)))
     '() ls)
=> (4 2 3 5 1)

こんな感じで、通常のfoldと比べて別にそれほど汎用性がある訳じゃない、 というかむしろ専用になってるから汎用性は下がってるか? いや、基本的には(fold f s ls) == (fld f cdr s ls)と同じようなもんなので、 若干汎用的になってる?

あと、微妙にCPSチックに書いてみる。

(define (uniq ls cont)
  (if (null? ls) ls
      (cont (car ls)
            (uniq (remove (pa$ equal? (car ls)) (cdr ls)) cont))))
(uniq ls cons)

ちょっと書き直した。cut-sea:2006/10/14 08:25:50 PDT

(uniq ls (lambda (x xs) (if (null? xs) x (+ x xs))))
 => 15
(uniq ls (lambda (x xs) (sort (cons x xs) <)))
 => (1 2 3 4 5)

こんな風にuniqな要素に対して色々あそんでみたり。cut-sea:2006/10/14 08:34:43 PDT


takの計測 @ Lazy Scheme

timeオペレータを追加。

lazy> (time (load '/home/cut-sea/script/pfc/prelude.pfc))
Loading script "/home/cut-sea/script/pfc/prelude.pfc"  repeat zipWith fold mapn list-ref .. ... uncurry uncurry3 sieve primes qsort partition Y fact0 tak S K I SKK 
elapsed time[sec]:   0.001335
cpu time[sec]:       0.000269
[]
lazy> (time (tak 12 6 0))
elapsed time[sec]:   0.000737
cpu time[sec]:       0.000182
12
lazy> (time (tak 100 50 0))
elapsed time[sec]:   0.020996
cpu time[sec]:       0.022534
100
lazy> (time (tak 1000 500 0))
elapsed time[sec]:   2.405670
cpu time[sec]:       2.004505
1000
lazy> (time (tak 2000 1000 0))
elapsed time[sec]:  11.293307
cpu time[sec]:       8.118345
2000
lazy> (+ (time (tak 2000 1000 0)) (time (tak 2000 1000 0)))
elapsed time[sec]:   8.331166
cpu time[sec]:       8.044661
elapsed time[sec]:   8.539020
cpu time[sec]:       8.046252
4000
lazy>

こんな感じ。はえぇ。cut-sea:2006/10/13 23:20:46 PDT


Rubyの生産性の高さはどこまで本当か?

以前redditにも上がってたと思う。 読んでなかったんだがさんが書いてたんで読んでみた。
面白いんだけど、私はこの方の論法を聞いててもやっぱり違和感がある。 いや、別の箇所をツツキますけどね。
C#にもこの機能があるとか、 要するに「最近のほにゃららな言語にはこの機能が取り込まれて」という話の部分。
その機能が取り込まれたから云々ってのは不毛じゃん?と言いたい。 そんなこと言ったら、結局取り込んだから、もう差がなくなったよ、 メリットないよね?他にある?あるんなら取り込むよ、そしたら使う理由ないよね? ってなっちゃわない?
そうなると、世の中に「ある斬新な機能ふが」が出てくると、 各言語の実装者がそれを取り込んでくれるかどうか、 あるいはどれだけ早く取り込んでくれるかの勝負になっちゃうわけで、 そんなもんいつまでもアテに出来るわけじゃないっしょ。
Schemeから言わせれば、 むしろ言語自体がわざわざ取り込む必要がないってのが重要なんだと思うよ、きっと。 以前RtS:Shiroでリンクされてたこことか、 Scheme:TinyCLOSとかみたいに、 こーゆー根っ子にあたるモンですら現実的な分量で実装できちゃうってのが、 メタプログラミングってやつの本質なんじゃないかと思うんだが、 違うかなぁ。(あんま偉い人とかに反論されると自信ないけど)cut-sea:2006/10/10 18:39:50 PDT


(fst (pair 1 x)) @ x 未束縛で延々討論

遅くまでご苦労さまでした。
xが未定義の状態で、

lazy> (fst (pair 1 x))
1

であるべきか、

lazy> (fst (pair 1 x))
Error in 0: undefined variable --- x

であるべきか。
未定義を含む式がバグという考え方は分かるつもりではあるんだが、 fstやlengthの類が構造だけで決定して欲しいってのはやっぱりあるんだよなぁ。
どういう局面?って言われたら苦しい。
ともあれ、エラーで弾くとすれば、チェック機構を設けたところであって、 今出ているエラーはちょっと違うと思っているので、色々しつこく追ってみた。

やはり、今のこれはおかしいと思ったので修正。 元コードは、(foo x y)って式があった時に、xやらyやらがシンボルだったら、 fooをcallする前に参照の解決をしているように見える。

lazy> (def (always-true x) #t)
always-true
lazy> (always-true hoge)
Error in 0: undefined variable --- x

見えるだけじゃなく、実際にそうらしい。
なので、envを指すthunkとして束縛し直して、あとは、foo次第ってことです。 (always-true hoge)って未束縛のhogeを与えるのは、確かに妙だな。。。

まぁ、チェック機構を実装したら、評価される以前に、 結局そこでエラーになるのかもしれないけど、 その辺は実際にチェック機構を実装するまでは結論を先延しにしたいなぁ。
しつこいけど、も少しじっくり考えたりしたいので。

ともあれ一旦コミットしてみて色々評価して様子を見てみることにする。cut-sea:2006/10/10 10:18:43 PDT


gtk2+がおかしい?

最近は毎週のようにpkgsrcのupdateをしている。
OSのcurrent追っかけは2weekに一度くらい。 気が向けば前の週にupdateしててもやってる。

で、いつもの様に無事updateしたつもりが、 firefoxもgaimも一切shift+spaceでかな入力できなくなった。 今朝は大丈夫だったよなぁ。

ブラウザで日本語入力できなくなると、ググりにくいし大変。 Sumibiからなんとか調べたら、同じようにktermからはOKで、 firefoxからダメって書いてた人発見。 「gtkが怪しそうなんで戻したけど直らない」って書いてた。

まぁ、確かに今朝updateしたものにgtk2+があるから試してみるかってことで、 過去のbuild分に戻してみると直った。

さて、こういうことも有るんだけど、やっぱりこまめにupdateした方が良いなと。 なんせ50も60も一気にupdateしたら、何が怪しいかなんて分かんないし、 まさか一個updateしては動作確認なんてやってらんないし。cut-sea:2006/10/09 06:32:39 PDT


灰色

地下核実験しやがった。
すでに核を持ってたり、実験した国々がつべこべ言う権利はねぇと思うが、 人間て本当におバカ。
Google Earthを小さい縮尺で見てると、人間の密集している地域が灰色に見える。
住んでるだけでも地球表面がガリガリ引っかかれている様に見えるわけですよ。 なんか痛そうとかも思ってみてた。優しくねーなーって。

それなのに、その上さらに核とかって、地球に対してなんつうことすんのよ。
宇宙船地球号って言葉があったけど、皆で競って船底に穴あけるがいーさ。cut-sea:2006/10/08 20:21:38 PDT


vector constructor on Lazy Scheme

GaucheFestで実装。

lazy> {}
{}
lazy> {1 2 3}
{1 2 3}
pfc> (let ((x 1) (y 2) (z 3)) {(* x y) (+ y z) {z x}}) 
{2 5 {3 1}}
lazy> (vector? {})
#t
lazy> (vector? {1 2 3})
#t
lazy> (vector-ref 2 {1 2 3 4 5})
3
lazy> (size {1 2 3 4 5})
5
lazy> (= {} {})
#t
lazy> (= {1 2 3} (let ((x 1) (y 2) (z 3)) {x y z}))
#t
lazy> (= {1 2} {1 2 3})
#f

こんな感じ。 curly braceを使うのには抵抗あったけど、 土曜のFestで議論して、まぁてっとり早くこれでいくことにした。 最初は<1 2>みたいなアングルを使うのが候補に挙がったんだけど、 残念ながら、<=とか>=とかと区別するとか、そもそも、 シンボルとして'<'とか'>'が使用不可になるんじゃね?ということで諦めることになった。
やっぱりSchemeの#-prefixってのは丁度いい感じなのかなーと思ったよ。ええ。

残件としては、pairとtripleをvectorの特殊形としてマップするってのもやらないとね。 pairもtripleも評価器自体で使ってるので、さすがに無くすのもマズそうなんでしょうがない。

lazy> (vector? {1 2})
#t
lazy> (vector? (pair 1 2))
#t
lazy> (vector? (triple 1 2 3))
#t
lazy> (pair? {1 2})
#t
lazy> (triple? {1 2 3})
#t
lazy> (pair? {1 2 3})
#f
lazy> (triple? {1 2})
#f
lazy> (fst {1 2})
1
lazy> (snd {1 2})
2
lazy> (fst3 {1 2 3})
1
lazy> (snd3 {1 2 3})
2
lazy> (thd3 {1 2 3})
3
lazy> (vector-ref 1 {1 2 3 4 5})
2
lazy> (vector-ref 1 (pair 1 2))
2
lazy> (vector-ref 0 (triple 1 2 3))
1
lazy> (= {1 2} (pair? 1 2))
Error in 0: illegal function --- p = 0x8
#f
lazy> (= {1 2} (pair 1 2))
#t
lazy> (= {1 2} (triple 1 2 3))
{1 2}
{1 2 3}
Error in 0: type mismatch --- = or == or equal?
lazy> (= {1 2 3} (triple 1 2 3))
#t
lazy> (= {1 2 3} {1 2 3})
#t
lazy> (= {1 2} {1 2})
#t
lazy> (= {1 2 3} (pair 1 2))
{1 2 3}
{1 2}
Error in 0: type mismatch --- = or == or equal?

おっけー。object.hの述語関数マクロを変更した。 この辺とかもタグをちゃんとbit立てるように整理した方が高速になるんだろうなー。cut-sea:2006/10/08 18:33:14 PDT

darcsへ移行

あと、今回はdarcsの使い方を覚えた。 ローカル環境にも欲しいということでpkgsrcでbuild中。 CVSからsubversionそしてdarcsと、次々に乗り換え中…。 cut-sea:2006/10/08 02:56:53 PDT


SKK on Lazy Scheme

MIT記法が使えるようになったあたりから、色々な式を書くのが楽になってきた。 で、Y-Combinatorをやったんだから、SKKもやっか?ってことで。

(define (S f g x) (f x (g x)))
(define (K x y) x)
(define (I x) x)
(define (SKK x) (S K K x))

Y-Combinator同様、屁みたいに簡単に動作する。

lazy> (SKK 3)
3
lazy> (SKK 'SKK)
SKK
lazy> (SKK "SKK")
[%S %K %K]

当たり前だけど、普通のSchemeではちと難しい。 自動カリー化が必要になるんじゃないかと思う。

(SKK 3)
=> (S K K 3)
=> (K 3 (K 3))
=> エラー

なんせKの引数の数は2で、自動カリー化されない言語ではしょーがないからね。

と思ったら、Haskellもナゼかアウト。処理系はHugsだ。

S f g x = f x (g x)
K x y = x
I x = x
SKK = S K K x

定義はこんな感じなんだが、間違っているのか、 それとも例によって型チェックか?cut-sea:2006/10/06 12:06:32 PDT

定義が間違ってた。

s f g x = f x (g x)
k x y = x
i x = x
skk x = s k k x

こうしなきゃだめ。 大文字アウトだし、最後のskkの定義も間違ってた。cut-sea:2006/10/06 18:46:54 PDT


"文字列" == (#\文 #\字 #\列) @ Scheme Haskell Lazy Scheme

Gaucheでも(use gauche.collection)とすると 文字列は文字のリスト(正確にはコレクションみたいだけど)として扱える。

gosh> (use gauche.collection)
#<undef>
gosh> (map identity "hoge")
(#\h #\o #\g #\e)

Haskellでは文字列は文字のリストだ。 実際に型の方もStringは[Char]だ。 でも、なぜか

Hugs> "hello"
"hello"

うーん。 Gaucheはもともと文字列は文字列クラスのインスタンスだから、 "hello"と印字されるのはいいんだけど、Haskellの場合は[Char]なんじゃないの? つまり、

Hugs> "hello"
['h','e','l','l','o']

ってのを期待したんだが、

Hugs> "hello"
"hello"

だし、

[Hugs> ['h','e','l','l','o']
"hello"

ですらある。 うーん。"hello"は文字列で持ってて、リストとして扱いたい時にばらしてるんだろうか。 実は

lazy> "hello"
[%h %e %l %l %o]  ;;この処理系では文字は%プレフィクスで表現される

となるので、文字列として印字させたいと思ったので、なんか参考になるかと思った。 (この処理系の場合、ソースを見るとリードした時点で文字のリストにしていた。) 以前さわってた時はHaskellのこの辺は意識してなかったんだけど、 もしかして文字のリストを文字列として印字する専用の関数があったっけ? とか思ってたんだけどなぁ。 ただ、

[Hugs> ['h','e','l','l','o']
"hello"

ってことは、リストを読み込んだところで内部で文字列として持っているか、 それとも、文字のリストを、そのメンバが全部文字なら文字列だとして、 印字の仕方を切り換えているのか。 evalのことを考えるとリストで持ってた方がよさげな気がするのではあるが。 cut-sea:2006/10/06 08:41:40 PDT

ははぁ、なるほど。 Haskellとかだと、型の違うメンバを持つリストはありえないから、 最初のメンバを一個みて文字列だと判断できちゃうのか。 Schemeの場合はそうもいかないから全部チェックする必要が出てきちゃう。 印字の時だけなんでしょうがないと思うしかないかなぁ。

グワーン!!そんなことしたら文字の無限リストが扱えなくなるじゃん!
問題になるのは印字の時だけだけど、印字しはじめてくれなくなっちゃうYo!

-- Hugsとかに見られる文字の無限リストつまり無限文字列の印字における期待される動作
Hugs> repeat 'a'
"aaaaaaaaaaaaaaaaa....

ってことはあれか? 実装したお方もやはりそれを分かってたからこそ今の状態になってんのか?? cut-sea:2006/10/06 08:48:03 PDT

マズいなぁ。lazyになって、lazyであるメリットを亨受しようとすると 型の違うリストは持てないワケかぁ。 むむむ。ってことはさ、Schemeみたく構造体の代わりとしてリストを使えない。 そうすると任意の長さのタプル(ペアとかトリプルとかそういうやつ)を 構築できる必要性がでてこない?
となると、今はpairとtripleしかなくて、

lazy> (pair 1 %a)
(1,%a)
lazy> (triple 1 %a "hoge")
(1,%a,[%h %o %g %e])

ってなってるけど、可変引数にするとリストコンストラクタの例の話と同じことで、

Hugs> (1,'a',"hoge", True)
(1,'a',"hoge",True)

みたいに書けて欲しいぞ。 やばい。タプルのコンストラクタ専用の構文が必要になんの?
なんか段々ヤな流れになってきてる気が…cut-sea:2006/10/06 09:12:50 PDT


concatenative言語?

それは逆ポーランドじゃない のエントリがこれまたなかなか面白そう。cut-sea:2006/10/04 22:04:18 PDT


mapnの定義 on Lazy Scheme

(def (repeat a)
     (cons a (repeat a)))

(def (fold proc seed lst)
     (if (null? lst)
         seed
       (fold proc (proc (car lst) seed) (cdr lst))))

(def (zipWith proc l1 l2)
     (if (or (map null? [l1 l2]))
         []
       (cons (proc (car l1) (car l2))
             (zipWith proc (cdr l1) (cdr l2)))))

(def (map proc lst)
     (fold (lambda (i s) (append s [(proc i)])) [] lst))

(def (mapn proc lsts)
     (fold (lambda (i s) (zipWith id s i)) (repeat proc) lsts))

こんな感じ。 動作としては、

lazy> (def (plus3 x y z) (+ x (+ y z)))
plus3
lazy> (plus3 1 2 3)
6
lazy> (mapn plus3 [[1 2 3][4 5 6][7 8 9]])
[12 15 18]

よさげではある。
下で匿名の方の(map p l1 l2 l3 ...)がmapの本来の意味から言うと 違うんでないか、という意見は確かにそうかと思う。 まぁmapnってより、apply-with-zip-argsみたいな感じの高階関数だってことかな。
でも、あの記法に簡約するのはやっぱり分かりにくいと思う。 悪いがハッキリ言って、悪い意味のPerlのコードと同じだ。

で、今日のナゾだ。
idの挙動がどうにもよく分からん。identityだと思ったんだけどなぁ。

lazy> (zipWith id (repeat +) [1 2 3 4 5])
[<Closure:+:1> <Closure:+:1> <Closure:+:1> <Closure:+:1> <Closure:+:1>]
lazy> (zipWith id (zipWith id (repeat +) [1 2 3 4 5]) [6 7 8 9 10])
[7 9 11 13 15]

こんな具合になるっておかしくない?いや、動作させておいて自分で言うのもアレだけど。

lazy> id
<Closure:id:1>

これは1引数のclosureなのに、

lazy> (id 1)
1
lazy> (id [])
[]
lazy> (id + 1)
<Closure:+:1>
lazy> (id (id + 1) 2)
3
lazy> (id + 1 2)
3
lazy> (id id 1)
1
lazy> (id id +)
<Closure:+:2>
lazy> (id id id)
<Closure:id:1>

どゆことだ?(@_@;;
ソースを追うのは明日以降かな。ネムイし。cut-sea:2006/10/03 09:48:44 PDT

補足

あ、もしかして上のはfoldrか?
よく分からんけど。

(def (zipWith proc l1 l2)
     (if (or (map null? [l1 l2]))
         []
       (cons (proc (car l1) (car l2))
             (zipWith proc (cdr l1) (cdr l2)))))

(def (fold proc seed lst)
     (if (null? lst)
         seed
       (fold proc (proc seed (car lst)) (cdr lst))))

(def (mapn proc lst)
     (fold (lambda (s i) (zipWith id s i) (repeat proc) lst))

なら、ほぼ同型。 最後のが、

(def (mapn proc)
     (fold (zipWith id) (repeat proc)))

ここまでは近づくけど、すでに意味が分からん。。。
考えてみれば当然か? lstってのを脳内補完する必要があるんだけど、 んなもん無理くさいし。cut-sea:2006/10/03 10:15:27 PDT


マクロか?コレ

condを定義してみる。lazyなので関数がSchemeにおける関数とマクロの 両方を兼ねられるハズだと思ってる。

lazy> (def (cond cls)
        (if (null? cls)
            #f
            (if (null? (car cls)) 'error
                (if (car (car cls)) (car (cdr (car cls)))
                    (cond (cdr cls))))))

で、なぜかしばし格闘。なんで、

lazy> (cond ((#f 1)))
Error in 0: illegal function --- p = 0x8
lazy> (cond ((#f 'false) (#f 'false) (#t 'ok)))
Error in 0: illegal function --- p = 0x8

うーんとかなり悩んでしまった。 正解は。。。

lazy> (cond [[#f 1][#f 2][#t 3]])
3
lazy> (cond [[(even? 1) 'false] [(odd? 2) 'false] [#t 'ok]])
ok
lazy> (cond [[#f (display 'false)] [#t (display 'ok)]])
ok  <= displayされたもの
ok  <= display式の値

ムムムー。多分評価される必要のないものは 評価されてないっていう意味ではマクロっぽいんだけど、なんだか違ーーーう!!。 マクロらしいアヤシサがない!なんかキレーにまとまりすぎてねーか???
これって結局、ノーマルなSchemeでlambda使って

[[(lambda () #f) (lambda () (display 'false))] ...

みたいに書いた場合に対応するのか?
つーかlazyってことは、そういうことじゃん。
(でも、LispにM式とか存在してた頃はまさにこんな感じだったのかもしれんなぁ。)cut-sea:2006/09/26 05:22:51 PDT


不便だと感じるのは何故か

even?やodd?あたりを実装しつつ処理系のお勉強をしてて思った。

可変引数が取れないのが不便だと感じる、もう一つの理由は、 せっかく前置記法によって

(+ 1 2 3 4 5 6 7 8 9 10)
(* 1 2 3 4 5 6 7 8 9 10)

と書ける利点が打ち消されてしまうからかもしれない。

つまり、リストを作るのに

(list 1 2 3 4 5 6 7 8 9 10)

と書けないので

(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 (cons 6 ...

と書く必要があったのと同様です。

(+ 1 (+ 2 (+ 3 (+ 4 (+ 5 ...

ってやってられないってことだ。

もちろん、一旦[1 2 3 4 5 6 7 ...]な構文をサポートすれば、 一引数のsumが書けるのでいいんだけど、やはりシンタックスシュガーを導入しただけで、 本質的に解消された訳ではない。(という気がする。まだ。)

(sum [1 2 3])

(sum 1 2 3)

うーむ。

あ、あとmapとかも

map (+ 1) [1,2,3]

これで行けるんだったらば、

map (+) [[1,2,3][4,5,6]]

な気がしたんだが、それはダメなんだよねぇ。

もちろん

map (+) [1,2,3] [4,5,6]

なんぞもっての他だし。

map (\f -> map f [4,5,6]) $ map (+) [1,2,3]

これは意味が違う。 結局zipを使うのか。

map (\t -> (fst t) + (snd t)) (zip [1,2,3] [4,5,6])

こうなるワケだ。 勿論振り返ると最初のがダメなのは当然なんだが、どうも考え方がだいぶ違うなぁと再認識。

この辺を考えていると、quoteとか参照透明性とか、 式と値の区別とか頭の中がグルグルするんだよな。
ふと思い出したので、SICPから引用。 なぜSICPでSchemeを選択したかの理由について書かれた部分だけど、

特徴のもっとも著しいのは プロセスの手続きというLispによる記述自体が Lispデータとして表現できることである。 「受動的」なデータと「能動的」なプロセスを区別する伝統を ぼやかす点に依存した強力なプログラム設計技法が使えるからだ。

cut-sea:2006/09/25 06:15:11 PDT


MIT記法 on lazy Scheme

最初は評価フェーズに入ってるので、カンが掴めなかったが、 基本的には他の式の実装を参考に模倣でなんとか。

lazy> (def (Y f) ((lambda (x) (f (x x))) (lambda (x) (f (x x)))))
Y
lazy> (def (fact0 f n) (if (= 0 n) 1 (* n (f (- n 1)))))
fact0
lazy> (Y fact0)
<Closure:804bd68:1>
lazy> ((Y fact0) 5)
120
lazy>

これで一応書きやすくなった。 Gaucheみたく、(define ((f x) y) (* x y))みたいな再帰的なMIT記法は使えないけど、 自動カリー化があるので実質同じことなんだが。cut-sea:2006/09/22 22:09:45 PDT


リストリテラル on lazy Scheme

今日っていうか、昨日、朝メッセンジャーにnobsunが入ってたので、 捕まえて色々とchatした。 lazyなSchemeでは、やっぱり[x y z]的なリストリテラルっていうか、 コンストラクタっぽいものが欲しい。
まぁその後はなかなか濃ゆいお話に突入したんだけど、まだそこまでは。。。orz
とりあえず、件のlazyな処理系をハック。

lazy> []                                  
[]
lazy> [1 2 3 4 5]
[1,2,3,4,5]
lazy> [+ - * /]
[<Closure:+:2>,<Closure:-:2>,<Closure:*:2>,<Closure:/:2>]
lazy> ["Hello" "World" (map (* 2) [1 2 3 4 5 6 7 8 9 10])]
[[%H,%e,%l,%l,%o],[%W,%o,%r,%l,%d],[2,4,6,8,10,12,14,16,18,20]]
lazy> (map (lambda (f) (f 21 7)) [+ - * /])
[28,14,147,3]

lazy> (def x 10)
x
lazy> (def y 20)
y
lazy> (def z 30)
z
lazy> [x y z]
[10,20,30]

lazy> (map (flip / 2) [x y z])
[5,10,15]

lazy> (def integer (cons 1 (map (+ 1) integer)))
integer
lazy> (take 10 (letzf ((x integer) ($ (even? x))) (pair x (* x x))))
[(2,4),(4,16),(6,36),(8,64),(10,100),(12,144),(14,196),(16,256),(18,324),(20,400)]
lazy> 

スバラシイ。 というワケで、nobsunとペアプロする時にはマジにVMのG-machine化とかか? (この調子だと、もしかしたらMIT記法もできちゃうかもっていう皮算用が入ってる。)cut-sea:2006/09/22 11:38:21 PDT


Weak Head Normal Form

最近どうもHaskellのことが気になる。 というか、遅延評価や自動curry化などが気になっている。 lazyなSchemeが自分の欲求を満たしてくれるか?というと、確かに、

(def integer (cons 1 (map (+ 1) integer)))

とか

(def tuple+ (lambda (p) (+ (fst p) (snd p))))
(def fib (cons 1 (cons 1  (map tuple+ (zip fib (cdr fib))))))

とか、さらにさらに!

;; Y-Combinator
(def ch-Y (lambda (f)
            ((lambda (x) (f (x x))) (lambda (x) (f (x x))))))

;; sample test
(def fact0
     (lambda (f n)
       (if (= 0 n)
           1
         (* n (f (- n 1))))))

((ch-Y fact0) 5)
 => 120
(ch-Y fact0 5)
 => 120

なんぞと書ける辺りはもうコーフンものなのだ(Haskellだとこれは型付き故にムリだし、 Lispではeagarなので定義は出来ても(ch-Y fact0)自体が停止しない)が、 lazyなScheme処理系を触ってると、 色々と相性の悪さが見えてきて、どうにもキビしい。 (そう。停止しない式でも停止できたり、ここにある チャーチ数の定義みたいに順序関係なく記述できたりってのは、 制約を取り除いていると言えるのでとっても羨しい)

ひっかかったのは、zipなんだが、

(def zip (lambda (x y)
             (if (or (map null (cons x (cons y ()))))
                 ()
               (cons (pair (car x) (car y))
                     (zip (cdr x) (cdr y))))))

とりあえず、こいつが、redifinitionされたってワーニング出す。 なるほど、lazyな処理系ではどうしても副作用は認めらんない。 つまり、代入である再定義はアウトだ。1ガビーン
そもそも予約語があるってのは、その時点で結構ストレスがじりじり上がる。

さて、それをクリアしても、orがどうにも気色悪い。 最初ビックラこきましたよ。orとかandが一引数の関数なんですよね。 ようするにリストをよこせっちゅーわけです。 当たり前といえばそうなんだけど、ジミ〜に困るんですよこれが。
Haskellだと、

x = [1,2,3]
y = [4,5,6]
[x,y]
  => [[1,2,3],[4,5,6]]

となるので、単にmap null [x,y]と書けばいいだけ。 当然引数が増えたとしてもmap null [x,y,z,t,s,u,v,w ...]とか書けますわな。
ところが、lazyなSchemeの場合は上記のようになっちゃう。 そもそもlistみたいな任意個数の引数を取る関数が書けないのだからしょうがないんだが、 当然cons, cons, cons…なんてやってられないですね。2ガビーン
つまり、(x y ...)とも書けなきゃ、(list x y ...) とも書けないから、 結局上記の様になっちゃうわけで、とてもじゃないが、不便でしょうがないのだ。
(x y)をWHNFだと看倣せればいいんだが、そうもいかない。

(define x cons)
(define y +)
(define z *)

だとして、

(x y z)
  => (#<closure cons> #<closure +> #<closure *>)  ;; これが解?
  => (#<closure +> . #<closure *>)               :: それともこれか?

とゆーことで、やっぱり判別がつかんのよ。3ガビーン
ここにquoteの存在を再び (以前nobsunに教えられたquoteのもたらすものとかカッコの二つの意味とか) 思いしらされるワケだが、 せめて、`(,x ,y ,z...)と書ければ少しは楽になるようだが、 これって本質じゃないんだよね。

(quasiquote ((unquote x) (unquote y) ...))

のシュガーだからなぁ。 組みこみだからいいのかもしれないけど、 そこまでやってヒゲヒゲが残るってのはどうもなぁとかなんとかブツクサ…

なにがしたいのかっていうと、 要はlazyだったり、自動curryはすごく便利そうなんだけど、 それと引き換えに失うものが大きいってのが悩みな訳ですよ。
オプショナル引数が取れる関数が定義できないってことは、 単にそれだけじゃなくて、コードを書くあらゆる場面で不便なのだ。 実際、ちょっとサンプルデータを作りたい局面で、こんな不便なことはないし、 失うものが自由にほげほげできる的な側面なだけにツライ。 なんとか両立できないもんだろうかと日々悶々としてる。cut-sea:2006/09/21 09:07:25 PDT


Qooqle

QooqleってのがOSS Webの今日の一行で紹介された。 最初は、名称から単なるジョークサイトだと思ったが…
むむむむ。 確かにイマドキな感じで、しかもこりゃ結構クールだ。cut-sea:2006/09/21 04:00:05 PDT


OpenOffice.org @ NetBSD?

死にゆくなどと言われたりしてるわけだけど。。。orz
NetBSDでOOoを使う。

えーっと、pkgsrcから入れればスムーズなんだけど、日本語とかその辺色々不満あり。 NetBSDではCOMPAT_LINUXとか、ようするにデフォルトでkernel作っておけば、 たいてーの場合(たいてーってのが怪しい)Linuxのバイナリが使えます。 まー厳密には色々あるのかもしれないけど、なんとなく動くくさい。

基本的には

  1. rpmを取ってきて
  2. rpm2cpioとかで変換して
  3. cpio -idvで解かして
  4. /emul/linux/usr/bin/lddとかで参照チェックして
  5. linux emuなlibを参照するようにLD_LIBRARY_PATHを適宜調整

でいけるようだ。

cut-sea@nkisi> cd compile  ;; ここで作業する。つーかこの場でのみ使う。公共の場にはインストールはしない。
cut-sea@nkisi> tar zxvf OOo_2.0.3_LinuxIntel_install_ja_wJRE.tar.gz
   :
   :
cut-sea@nkisi> cd  OOC680_m7_native_packed-1_ja.9044
cut-sea@nkisi> cd RPMS
cut-sea@nkisi> rpm2cpio openoffice.org-base-2.0.3-7.i586.rpm | cpio -idv
      :
    以下openoffice.org-***-2.0.3-7.i586.rpmを全部処理

でもって、

cut-sea@nkisi> cd opt/openoffice.org2.0/program
cut-sea@nkisi> setenv LD_LIBRARY_PATH ./:/emul/linux/lib/:/usr/pkg/emul/linux/lib/:/usr/pkg/emul/linux/usr/lib/:/usr/pkg/emul/linux/usr/X11R6/lib:$LD_LIBRARY_PATH
cut-sea@nkisi> ./soffice.bin

これでなにやらsetupから利用まで動作してる。 ちなみにsofficeとかscalcとか呼ぶとgconftoolsだかでセグメンテーションフォールトが出るけど、soffice.binから起動する分には出ない。 ただ追及する時間でもないし、O型なので、その辺は実におーらかなのだ。 まー謎だらけだけど結果オーライか?cut-sea:2006/09/12 11:05:27 PDT


かもすぞ

「龍神丸大吟醸生原酒中取り無濾過」キター!!(^ワ^)/

まんが「もやしもん」で紹介されたために、今年3月に発売と同時に瞬殺で売り切れ、 全国で悲鳴があがり、マジで幻の酒になっちゃった龍神丸。
その中取りが、約半年の低温熟成という沈黙をやぶって今リリース!!

精米歩合40%まで磨いた山田錦を100%使用し、和歌山酵母で仕込んだ最高の大吟醸!
しかも生!しかも原酒!!

そして、以下のパッチがあたってる。

しかも中取り!しかも無濾過!

ちなみに無濾過なので、まだ酵母が生きてるらしい。 かもすぞーコノヤロー!!(笑)って中でやってるから、かすかに発泡してるんだとさ。
やっぱり、こだわりの実装は重要!!
さて、いつ開栓するかなー。わくわく。cut-sea:2006/09/08 18:13:22 PDT


遅れても困る人はいない , リリースは政治パフォーマンスなんだよ。

matzにっき昨日読んで、(後で書く)だったので、じーっとウォッチしてた。 出てくるまでの間にruby-devの件のmputさんとmatzさんのやりとりも。 色々書こうとしたけど、まーいーや。 ただ、これについてはmatzさんも含めてみんなが楽しそうではないですね。 元々好きでやってるはずなのに。
大きく、有名になってくると、そういうものなのでしょうか。

仕様(互換性の問題)

全然話は変わるけど、nil.to_sのやりとりなんかは見てて楽しい。 まーイベントみたいなものなの?
matzさんが大きな仕様変更を試す→皆でぎゃー!!っての。(w
ruby門外漢なので正確なところは知らんけど、 matzさんがto_sに見いだしていた意味はread/write Invarianceの性質なのかしらん?
『to_sは「そのオブジェクトの文字列表現」です。』というのを見てるとどうもそんな気がした。 私の感覚だと、(大物に味方するみたいで恥ずいけど)matzさんの感覚は理解できる。 一方で、反対者の方々が求めているのは、強引に文字列とか数値にキャストした時に、 どうなるのが大抵の場合便利か?というまさに現場での泥臭い話なので、 やっぱり重要なんだろうけど、実はmatzさんが求める様に一貫性と思う根拠を示すのは難しい。 誰かがMLで書いてたけど、そう直感的に思うってのが根拠になっちゃうから。 少なくとも万人がYesと言えるようなもんじゃないだろうし。
でも、その辺をバランスよくデザインすると、大抵の人が大抵のケースで、 記述量が減って幸せになれるのでしょう。
それはさておき、ここで過去のバージョンとの互換性の問題が壁に(当然)なってるんだけど、 まーnil.to_sをユーザが(define nil.to_s "")的に 上書きすりゃいーんでないの?とrubyを知らないので言うだけ言ってみる。
そういや、R6RSでも行儀の悪いリストは排除するか?みたいのが議論に挙がってた。 明らかに現在との互換性は問題が出てくる。
現時点では個人的に入って欲しくないし、入るとは思えないんだけど、 Haskellみたいに自動カリー化して欲しいというのもある。 そうするとオプショナル引数が取れるのはうまくいかない。

みたいに、私みたいな下々の者でもHaskell少しかじると思うところはあるんだが、 RnRSに関しては、過去の互換性はあんまり重要視してほしくないな。 本当にそれが正しいとCommiteeにより判断されるなら、 そうなってもいいのではないかと思う。
多分、その時にはそれで不便だとか書きにくいとは思わないような、 デザインに全体としてはなってくれてるだろうと思う(信じてる)から。
なに?カッコがある時点で書きにくい?
それはもーどーしよーもない。つーかアレがいーんじゃん。(w cut-sea:2006/09/07 20:05:35 PDT


これはすごいインターネットですねー

インターネットウミウシだとさ。cut-sea:2006/09/05 07:44:09 PDT


Manifest

redditで紹介されてて、遊びましたよね?
で、さっきも少しさわってたんですが、面白い発見!!!

えーっと名づけて作った虫が食われちゃう技(技なのか??)。。。 まず、環状になるものを作ると虫じゃなくて変なもの(?)ができるのはご存知ですね? やったことなければ、まずそれを試してみてください。要は閉じる線を描けばいいだけ。

で、その環状のものを作るときに☆を書いてやるんです。 ☆である必要があるかどうかは試して無いけど☆で再現性高いですよ。 その後、ちっちゃい虫をそこら辺に作ってやればOK。 近くに寄ると、まるでブラックホールに吸い寄せられるかのように、 虫さんが吸い込まれて、中央にて消化されて消えていきます。cut-sea:2006/09/04 01:55:31 PDT


aproposで正規表現指定

できるの知らなんだ。

gosh> (apropos #/.*/)

うわーい。cut-sea:2006/09/02 00:47:35 PDT


sumibi.org

リニューアルされてる。 世界各国からアクセスされてますねー。cut-sea:2006/08/31 17:41:46 PDT


spigot

読んでみる。mqueueの方もかな。cut-sea:2006/08/29 15:02:32 PDT

おもしろそう。gauche-packageがあるからMeCabの頃より簡単そう。 で、以前よりずっとGauche:MeCabが分かるようになってる。 今度のGaucheFestネタってことで書き始めてみよう。

こっちね -> Gauche:拡張ライブラリ入門


LL Ring(テスト)

あ、そうそう。
テストについて質問出たとき、あまり深く考えてなかったけど、 よーく考えると、そもそも成果物をイメージして書くこと自体が無いので、 テストファーストは今後もやんない気がしてきた。

そうすると、実装後にテストを作るか。
リファクタリングには強いので、それなら有りだし書いたこともあるけど、 経験からすると、結局テスト書くぞーって、ある程度強い意志がないと書かなそうだ。
あと、ちょっと思いついたことを実装したり、仕様を変えたい時に、 テストも変えることになりそうだなーいやだなーって モチベーションがじりじり下がるのが避けられない。 実は結構その辺がメンドくさがり。

逆に、そもそも成果物を具体的にイメージして書かないのが問題じゃない? という反論を食らいそうなんだが。。。
いや、イメージせずに書く方が圧倒的に楽しいのですよ。ほんと。 自分の頭の中で色々考えてから、それを書き出すだけじゃつまんない。
書いてるうちに、さっきまで自分の頭の中に無かったものがコードに現れるから面白い。
そうしょっちゅう起こることじゃないけど、マジ脊椎に電流流れてキモチいーんです。 ってことで、多分今後もずーっと書かないことになりそうだねぇ。cut-sea:2006/08/27 23:10:42 PDT


お買い物(それと手塚治虫論ちょっとだけ)

だいぶ以前からスニーカーの右足の踵のとこがはがれててパクンパクンなってた。
Air Max '95のyellowを2セット保有してるんだが、これはまだ下ろしたくないしなぁ。

つーわけで、スニーカー買ってきた。
やっぱNIKEはかっこえーわー。
一目惚れした2足をそのままGET。

自分的にはあまり服装とか金かけないんだけど、

ってことで、それだけはゆずれなかったりする。
Breitlingは現在3本所有。腕が足りねー。

帰りに手塚治虫の「アドルフに告ぐ」の文庫を購入。
この作品はいろんなタイプで何度も発刊されてるのを持ってるけど、 大好きな手塚作品の中でもさらに良い。 一番好きか?といわれたら微妙だけど、はっきり言って、芸術作品デス。

読んだことない人は是非是非オススメ。 相当数の登場人物がいて、全員の人間関係、視野、時間の流れがきちんと計算されてる。 つまり、この時点ではAはBに出会ってないし、CはXという事実を知らないとか。 火の鳥なんかも確かにそうだけど、一作品として、より巨大ってところで個人的に評価高い。 (あ、念のため、火の鳥が全集それ自体が一作品としての構成があるのは知ってます。 けど、そう思って交互に読んでもやっぱり一つ一つ別かなーと。)

手塚治虫のスゴいのは、長編でありながら、きちんと細部までコンストラクトされてて、 おそらく緻密な設計をしてから実装しているように見えるんだけど、 手塚氏自体はご存知のようにLight Weightにガンガン実装しまくれるパワーを 持ってらしたってところだろうか。
音楽の世界で例えるとモーツァルトだ。 頭の中に全てあって、それをガーッって描き下してた感がある。 (最近の研究ではモーツァルトは、それなりに手直しがあったようだけど)
さらに、一発屋でもなく、それを生涯並列処理で製作し続けたってところ。
そして大御所になってからも、出版社に自分で作品の持ち込みをしてたってゆーから、 人間のプライドって何?って思っちゃう。

週刊連載で毎週を乗り切るだけにひぃひぃしてて、 打ち切りを恐れて、編集やらアンケートの結果に左右されて、 完結した時にズブズブの、作品ともいえない作品に比べたら、別次元の仕事である。
もちろん毎週作品を継続的に描くことの大変さを否定するつもりはないけどね。

計画性なしにS式を書いてる自分から見ると、まぶしいくらいで、 手塚治虫は才能のバケモノって感じである。 もちろん全部が好きってわけじゃないが、 特に後期作品は完成度がメチャ高でいくら買っても損はない。 しょーもない小説や映画に金つかうくらいなら、これ読めってところだね。cut-sea:2006/08/27 14:29:49 PDT


LL Ring

参加。キミならどう書くに出場した。
思えば、今年は龍神丸が瞬時に売り切れて入手できず(後日別ルートから無濾過中取りを入手)。
LLも油断してたら瞬時に売り切れってことで同じ目に二度もあっちゃった。 つーことで今年も出場側でなんとかいかせてもらう。
キミならどう書くってことで家計簿をつくったけど、 今思ってもやっぱり自分がもし付けるならExcel使うかなーと。

質問については、アプリ自体で分かるところは私が答えて、 フレームワーク周辺については全部さんにおまかせ。 実際あーいう質問は私では答えられないので助かった。 そもそも自分のPCではプロジェクタにうまく出なかったしね。cut-sea:2006/08/27 02:16:19 PDT


地味な努力

アプリの堅牢性を上げる努力って地味だな。 今回学んだのは、regexp-quoteっての。

(string->regexp (string-append (regexp-quote str) "#.*"))

みたいな感じで使用してみた。
でもこれで潰せたのは所詮一箇所だ。 他にも色々潜んでるのを個別の手段で潰して回らなきゃならん。cut-sea:2006/08/24 08:54:10 PDT


四捨五入

あれー?ないの?まさか。 floorやceilingなんかより良く使うはずなのに。

  (define (drop4take5 digit num)
    (let1 time (expt 10 digit)
      (/ (floor (+ 0.5 (* 1.0 num time))) time)))
gosh> (map (lambda (d) (drop4take5 d (/ 123 7654))) (iota 10 1))
(0.0 0.02 0.016 0.0161 0.01607 0.01607 0.01607 0.01607003 0.016070029 0.0160700287)
gosh> (map (lambda (d) (drop4take5 d (/ 678 1452))) (iota 10 1))
(0.5 0.47 0.467 0.4669 0.46694 0.466942 0.4669421 0.46694215 0.466942149 0.4669421488)

こんな感じ。cut-sea:2006/08/20 10:08:54 PDT


ふとつぶやいてみる

On Lisp邦訳はいつ出版?cut-sea:2006/08/14 18:46:07 PDT


うぐぅ?

AAですが、コードになってるってことで、 以前Perlなんかでも本が出てましたね。
ここにはいくつかはSchemeのものもあるようです。 (探し方は簡単。マウスをホバーさせてurlに注目です)
こういう時、HaskellやPython?などのインデントに意味のある言語はさびしいです。
Gauche:whitespaceでも同じことが言える。cut-sea:2006/08/13 20:43:43 PDT


Scheme:generatorとdoとwhile

Shiroさんの解説を受けて私も少しHackしてみる。

(define (make-generator proc)
  (define next #f)
  (define return #f)
  (lambda()
    (call/cc
     ;; break = generatorがcallされた後に相当する継続
     (lambda (break)
       (set! return break) ;; 環境を書き換え
       (if (not next)
           (proc
            ;; yield
            (lambda arg
              ;; cc = yieldがcallされた後に相当する継続
              (call/cc
               (lambda(cc)
                 (set! next cc) ;; 環境を書き換え
                 (apply return arg))))) ;; breakの後に待ち構えている継続をcall
           (next)))))) ;; yieldの後に待ち構えている継続をcall

(define (fib)
  (make-generator
   (lambda(yield)
     (let loop ((i 0)
                (j 1))
       (yield i)
       (loop j (+ j i))))))

こんな感じだろうか。正しいgeneratorの実装ってヤツ。

gosh> (let ((g (fib))
           (i 0))
        (while (< i 10)
               (print (g))
               (inc! i)))
0
1
1
2
3
5
8
13
21
34
#t
gosh>
gosh> (let ((g (fib)))
        (do ((i 0 (+ i 1)))
            ((>= i 10) i)
          (print (g))))
0
1
1
2
3
5
8
13
21
34
10
gosh>

一応はOKそうだ。 breakを必ず取得してreturnを置き換える。 …しまったー!朝だ。orzcut-sea:2006/08/09 13:28:29 PDT


Scheme:generatorとdoとwhileについて

ミソはdo/whileというより、inc!ですかね。

(let ((g (fib))
      (i 0))
  (while (< i 10)
         #?="mae" #?=i
         (print (g))
         (inc! i)
         #?="ato" #?=i))

(let ((g (fib)))
  (do ((i 0 (+ i 1)))
      ((>= i 10) i)
    #?="mae" #?=i
    (print (g))
    #?="ato" #?=i))

こんな風にデバッグプリントを入れてみれば、 なぜ無限ループになるかは判りますよね。
では何故、そうなるのか?

(g)が呼ばれた時にnextつまりcall/ccで切り出した継続が呼ばれますけど、 call/cc式で継続が切り出された時に、その時点でiはどうだったんでしょう。

こんな風にジェネレータに観測用の引数を渡せる様に変更を入れて確認してみましょう。

(define (make-generator proc)
  (define next #f)
  (lambda(a)
    #?="MAE" #?=a
    (if (not next)
        (call/cc
         (lambda(break)
           (proc
            (lambda arg
              #?="ATO" #?=a
              (call/cc
               (lambda(cc)
                 (set! next cc)
                 (apply break arg)))))
           #f))
        (next))))

(define (fib a)
  (make-generator
   (lambda(yield)
     (let loop ((i 0)
                (j 1))
       (yield i)
       (loop j (+ j i))))))

(let ((g (fib 0))
      (i 0))
  (while (< i 10)
         #?="mae"
         (print (g #?=i))
         (inc! i)
         #?="ato" #?=i))

(let ((g (fib 0)))
  (do ((i 0 (+ i 1)))
      ((>= i 10) i)
    #?="mae"
    (print (g #?=i))
    #?="ato" #?=i))

これでどうですかね。:-)
継続というからには、継続を切り出すその直前までに評価済の部分と その後評価される予定のものとがはっきりと違う。

(白状すると私にはそれは分かるけど、件のジェネレータの動作は十分に把握できてない。 もしかしたら"ATO"でaを観測する場所はあまり的確ではないかもしれない。)

だから特に引数の評価順などが絡むような、仕様上未定義なケースで使うのは その実装がどういう順に評価しているかをきっちり把握できてなきゃ使いこなせない。 だから継続ってのはマクロとからむと、とてもやっかい。
そして、構文が複雑だったり、式の評価順に優先度があったりすると 使いこなすのは劇的にむつかしくなると思う。 だから、複雑な構文を用意している言語では 継続がどうもしっくりこないのではないだろうか。 一方でそういう言語にはナマのSchemeより豊富な制御構造や、 ジェネレータやマルチスレッドのような高度な動作がすでに実装されているので、 あらためて必要になるケースが少ないのかもしれない。cut-sea:2006/08/09 08:51:15 PDT


小さな変更だが、私にとっては大いなる飛躍である

Gauche:javacube解析を書いてる時に最初こんな風に書いてた。

(define (analysis quest hint)
  (define (analy p0 q seq)
    (let1 cs (search p0 q)
      (cond ((<= 0 (length q) 1) (sequence-write (reverse seq)))
            ((or (null? cs) (>= (length seq) hint)) #f)
            (else
             (filter (lambda (p1)
                       (let1 new-q (banish p0 p1 q)
                         (filter (lambda (p2)
                                   (analy p2 new-q (cons (cons p0 p1) seq)))
                                 new-q)))
                     cs)))))
  (filter (lambda (p)
            (analy p quest '()))
          quest))

今はこう。

(define (analysis quest hint)
  (define (analy p0 q seq)
    (let1 cs (search p0 q)
      (cond ((<= 0 (length q) 1) (sequence-write (reverse seq)))
            ((or (null? cs) (>= (length seq) hint)) '())
            (else
             (map (lambda (p1)
                    (let1 new-q (banish p0 p1 q)
                      (append-map (lambda (p2)
                                    (analy p2 new-q (cons (cons p0 p1) seq)))
                                  new-q)))
                  cs)))))
  (append-map (lambda (p)
                (analy p quest '()))
              quest))

そう。 小さな変更だが、私にとっては大いなる飛躍の瞬間だ。

実は最初の時にも(そして以前も類似の状況で)#fを使うか()を使うのか悩んで、 そして結局、根拠などなく最初の実装になってた。

そしたら今日はたまたまnobsunがMTGに現れたので、 そこの辺のトコと、この手のquestから複数のquestや 解析中の記録など生成する関数をどういう風デザインするのが良いのかを尋ねた。

いろいろと面白い話もあったんだけど、それらを吹き飛ばすくらいのイキオイで Monoid(モノイド)とかMonadplusとかが意味のある単語として脳内に実装されました。

その結果が上記の変更です。 もちろん関数の引数と返り値をもっとイイ感じにデザインする余地はあるんだろうが、 ここではその辺はちょっと置いておいて、最小限だけキモになる部分の変更を。

継続が分かったー!って時よりも、 最近のリストに関する理解の変化の方が 自分にとってはずっとずっと素晴しく貴重なものになってきてます。

リスト、()とappend、Monadplus、バックトラック不要、そして図形言語とフレームワーク。 まだまだだけど、少しずつ少しずつ自分の中に。cut-sea:2006/08/09 06:44:04 PDT


javacube解析

javacubeは、 うちの職場にインターンで来てたotachiくんが作ったもの。 なかなかむつかしい。

面白そうなので問題を解くプログラムを書いてみたのだが。。。cut-sea:2006/08/09 02:34:10 PDT

こっちね -> Gauche:javacube解析


Shiro:OpenSourceMagazine0606

(*beauty) こういった道具をプログラムに組み込むことは、しばしばプログラムの美観を損ねることがある。コリジョンルーチンの例では、彼の最初のハックはモジュールをまたがって情報を受け渡すのにグローバル変数を利用していた。「ただ動く」プログラムではなく、美しいプログラムを書きたいと思い始めた頃のプログラマは、せっかく綺麗に作られたモジュール構造を壊すことを恐れて、大胆な変更に手が出せないことがある。力のあるプログラマはコードを捨て去ることに抵抗がない。まず汚くても動かしてみて、ダメなら元に戻せばいいし、うまくいってその機能を将来も使いたくなったらその時に設計を考え直せばいいと思っている。

ごめんなさい。脚注のこの部分が一番心に響きました。cut-sea:2006/08/07 18:41:55 PDT


マッチ箱の脳

比較的古い本だけど、えんどうさんに借りて読みました。 2日程で読めちゃいます。

実に判りやすく説明してくれてて、いい本でした。

そんなワケで私も「がんばれ森川くん2号」と「アストロノーカ」をamazonでポチりました。cut-sea:2006/08/06 00:43:46 PDT


URR

matzにっきからURRっての。 なるほど、すごいなぁ。 各桁のビットが決まった重みを持つのではないわけか。 一番下の桁の重みはそれより上の桁のパターンにより決定されるんだね。cut-sea:2006/07/31 06:14:14 PDT


続き

問題があるとする。 その問題をそのまま直接的に解くとすれば、

f::X->Y

なものを作ればよいとしよう。 ここで

g::(X->Y)->(X->Y)

とか、より一般的に

h::[(X->Y)]->(X->Y)

なる型hの集合をデザインするってことか。
ただX,Yがそうなりやすいものとは限らないからX,Yを うまく選び出してやる必要があると思うのだが、どうだろう。 例えばYを(X,Y)みたいな形にするとかってのも、その一つだと思う。cut-sea:2006/07/23 22:10:04 PDT


関数プログラミングの肝とかquoteがもたらすものとかカッコの2つの意味とか

先日nobsunとchatしてたんだけど、やっぱり目からうろこなお話をする。 最初はバカ話をしてただけなんだけど、この辺が良く分らないと投げるとマジメな話に突入。

うーん。今さらquoteとカッコの意味について学ぶ。 竹内先生の「初めての人のためのLISP」とかも思いだす。 なるほど!確かに!って感じですよ。やっぱりカッコは奥が深い。 まだまだ勉強せにゃならんことが多いよ。 これってやっぱり比較対象としてHaskellをかじってみたからか。 確かにSICPにも書いてあるんだけどねぇ、その時には見えないものだ。

それでもやっぱり図形言語はあまりにさらっと書かれてて困っちゃう。 私が知りたいのはどうやって個々の問題に潜む閉包性を抽出するかなんだよな。 つまりどうやってデザインすればそういう形にいきつくのかだ。

あ、そうそうついでにシニフィエとシニフィアンなんて語彙も獲得(大げさ)。cut-sea


ふつはす

しばらく放置プレイしてたけど読了。
パーサコンビネータの実装がようやく分かってきた感じ。 ただ作れって言われたら無理っす。 でも基本はパーサを合成してパーサを返す関数を作るワケだからなんか図形言語のフレームワークを作るのと似てる。
つーか、

f:: a->a
とか
g:: a->a->a

的なものが全部そう見えてくるからねぇ。ビョーキだね。 まぁでも慣れればなんてこと無いのかもしれん。

なんかHaskellを勉強してると、 動作をイメージするという行為を諦めるようになると一人前?って気がしてきた。 つまりね、イメージしようとするとなかなか難解な式を書き出せないけど、 そんなん諦めてこうなってくれーって書き出せば、 自分でもどう動作するか分からないけどいっちょ脱皮できるよん。みたいなの。cut-sea:2006/07/19 18:30:33 PDT


GaucheFest

無線LANの環境は昨年よりよかったけど、それ以外の環境としては水上の方がよかったかな。 とりあえず結構ちまちまとはかどった。cut-sea:2006/07/17 00:01:04 PDT


Last modified : 2012/03/18 11:00:46 UTC