2006/08/20

先日、Gauche のストリームと MIT Scheme のストリームがじゃっかん違う?と書いたんだけど、すみません僕が Gauche のドキュメントをちゃんと読んでなかっただけでした。具体的にはこのページ。

stream-delay
http://www.shiro.dreamhost.com/scheme/gauche/man/gauche-refj_158.html#IDX2975
"As a rule of thumb, any stream-producing functions should wrap the resulting expression by stream-delay."

これまで「Streamは遅延評価で実現する」という程度の認識しかなかった。それが SICP の "3.5.4 Streams and Delayed Evaluation" まで読み進んだところ、実際にはもっと込み入ってたってことがわかった。あいかわらずこの本は教科書としてスキがなさすぎ。つい鼻息が荒くなる。

で、そういう認識であらためて Gauche のドキュメントを見返すと、生成した stream を評価する順番を調整する stream-delay がちゃんと用意されてたってわけですよ。いっしゅん Gauche のわなかと思ったけど、ドキュメントを全部読んでないのと理解が甘いのが悪かっただけ。

Ex. 3.56 に当てはめると、
(define (merge s1 s2)
(stream-delay
(cond ((stream-null? s1) s2)
((stream-null? s2) s1)
(else
(let ((s1car (stream-car s1))
(s2car (stream-car s2)))
(cond ((< s1car s2car)
(stream-cons s1car
(merge (stream-cdr s1) s2)))
((> s1car s2car)
(stream-cons s2car
(merge s1 (stream-cdr s2))))
(else
(stream-cons s1car
(merge (stream-cdr s1)
(stream-cdr s2))))))))))

(define S (stream-cons
1
(merge (stream-scale S 2)
(merge (stream-scale S 3)
(stream-scale S 5)))))

gosh> (stream->list (stream-take S 100))
(1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54
60 64 72 75 80 81 90 96 100 108 120 125 128 135 144 150 160 162 180
192 200 216 225 240 243 250 256 270 288 300 320 324 360 375 384 400
405 432 450 480 486 500 512 540 576 600 625 640 648 675 720 729 750
768 800 810 864 900 960 972 1000 1024 1080 1125 1152 1200 1215 1250
1280 1296 1350 1440 1458 1500 1536)
ちゃんと求まる。

ついでに、347ページの微分方程式を解くプロシージャは次のようにすればいい。(そのまま Gauche で試すと 「stream-map に渡す y がstreamじゃねえ」と言われて実行できない)
(define (solve f y0 dt)
(define y (integral (delay dy) y0 dt))
(define dy (stream-map f (stream-delay y)))
y)

2006/08/11

がやがや町に広まった「ハウルは女の子の心臓を食べる」といううわさは、ハウルたち自身が荒野の魔女から身を隠すために衆目を遠ざけるべく意図的に流したものだ。

「Lispはカッコが多いから超むずい」とか「Haskellはモナドとかわかんないと入出力もできないから超むずい」いううわさも、コミュニティが市場原理から身を隠すために衆目を遠ざけるべく意図的に流しているものなのかもしれない。

出版社は市場原理の一部だけど、その内部にはソフィーのように市場原理に呪われてる奴らもいるってことで。

2006/08/10

放置していた Martha Argerich の新作(フランク、ドビュッシー、シューマンのバイオリンソナタ)をようやくプレーヤーにかけたんだけどさあ、なにこれ。あいかわらず聞いてるこっちが汗出てくるよ Argerich ねえさん……フランクの4楽章の3:45付近で誰かがうなってるんですが、あなたですか? びっくりするのでやめてください。

2006/08/07

もう数年前から考えていること。

できることは基本に忠実に対処し、できないことは工夫して対処する

この態度、間違ってますから。少なくとも外国語→母国語の翻訳で望ましい態度は、まったく正反対。

できることは工夫して対処し、できないことは基本に忠実に対処する

仕事で技術文書の英→日翻訳を発注すると、最初の態度をとってくる翻訳者がとても多い。技術翻訳の場合、ほとんどの翻訳者は当該の技術分野について知識や経験がない。たいていは興味もない。そんなわけで、自分が意味のわかる英文は逐語的に訳して済ませてしまい、意味がわからない英文が出てくると意訳しようとする。意味が分からない英文の意訳って定義上不可能なんだけどね。それこそ逐語的に訳してくれればいいのに。自分が原文の意味をとれてないなら、絶対に意訳しようとしないでほしい。技術がわからなくて訳せない箇所は「訳せません」でいいし、文意に自信がないならせめて英文法に忠実な逐語訳をしてくれるとチェックが助かります。

一方、論文でもない限り技術文書にも日常的な英文は相当含まれていて、発注する側としてはそういう箇所は普通の日本語の文章のように訳してほしいんだけど、これがうまくない。例えば英語の文章には日本語の文章より指示代名詞が多いけど、それをそのまま訳してきたりする。

Becky did not hesitate to ask him it in the shop because she had wanted it.
なぜならベッキーはそれが欲しいと思っていたので、その店で彼にそれを求めることを躊躇しませんでした。

勘弁してください。「ベッキーはその店で前から欲しがっていた○○を彼にねだりました」とか、それなりに普通の日本語に意訳してもらいたいからこそ翻訳を依頼してるわけですよ。○○は、たぶん文脈からわかるでしょう? 適切に補ってよ。普通の日本語で「それ」っていうか?

もちろん、そういうのを求めるなら相応のコストを払えという主張に耳を貸さないつもりはないです。個人的には。

もちろん、すべての仕事について「できることは工夫して対処し、できないことは基本に忠実に対処する」べきだとは思ってないです。むしろ、できないなら手を付けるな、というべき仕事もあるし。とはいえ、その「手を付けない」という対応こそが、その分野での「基本」なのかもしれなくない?

もちろん、上記は特定の業務に対する愚痴ではないです。

もちろん、例に使った "Becky..." の英文は僕の作文なので英語としてへんちくりんな可能性があります。

2006/08/02

LaTeXで、ページの残りが少なかったら次に続く要素を改ページしたい。
あくまでも自分用のメモ。
% ページの残りが指定した数値より少なかったら改ページを促す
% \ifnoroomthenpagebreak{20mm} とか
\def\estimate@restpage{%
\dimen@\vsize \advance\dimen@ -\pagetotal%
\advance\dimen@ -\pageshrink%
\advance\dimen@ -\pagedepth%
\advance\dimen@ -\pagestretch
\advance\dimen@ -\pagefilstretch%
\advance\dimen@ -\pagefillstretch%
\advance\dimen@ -\pagefilllstretch}%
\newcommand{\ifnoroomthenpagebreak}[1]{%
\estimate@restpage%
\ifdim\dimen@<#1%
\ifdim\dimen@>7pt \pagebreak\fi\fi}

\estimate@restpage で「残りページのつもりの値」を見積もってるんだけど、なんかどうしても1行分だけ残っちゃうみたいで、次のページの先頭付近が「残り 6pt くらい」だと見なされてしまう(場合がある)。その補正が最後の「> 7pt」。高さが 7pt しかない行はあんまりないので、この \ifnoroomthenpagebreak コマンドで改ページされなくてもどのみちこの位置では改ページされる可能性が高い。だから実用上は意図どおりの結果が得られることになる。とはいえ、このあたりの挙動がいまいち不明なので、そのままどんな場合でも安心して使うことはできないと思う。

にしても、一連の値「\pageほにゃらら」についてのドキュメントはどこ?(そもそもLaTeXのドキュメントが……)
結局参考になったのは、TRALICSというLaTeXからXMLへのコンバータのドキュメントだった。

TRALICS : a LaTeX to XML translator (P)
http://www-sop.inria.fr/miaou/tralics/doc-p.html

どうでもいいけど、こっちはXMLからLaTeXへのコンバータを作るのに精一杯なわけで、LaTeXからXMLを吐き出すなんて気が遠くなる。

2006/07/30

こないだ会社で「集合がわかる本を教えろ」みたいな質問をされたけど、それはたぶんドモルガンとかについてわかりやすく説明してる本ってことですよね。知りません。

まあ、そういう本の必要性は否定しませんが、たぶん読んでも面白くないんじゃないかなあ。っていうのは、ようするに「数式が読めること=数学がわかること」ではないからなんですが。じゃあ「数学がわかること」とは何かってきかれても、自分は必ずしも数学をよくわかっているわけではないので、はっきりとは答えようがないです。だから以下の独り言は音量をさげてこっそり書きます。


なんだかんだいって、数学に対するイメージは、いまだに「数字とか文字式だとかの演算」なのかもしれない。そこまで極端でなくても、「数式が読めること=数学がわかること」だと漠然と思っている人は多い。傍証:おまえ数学科なのになんで計算できないんだよという誹謗中傷。

現代の数学の根っこにあるのは、数式をこねくり回すことじゃない。数学の役割は、人間が恣意的にいじくれないっぽい概念をどう整理(抽象化)すれば人間の把握下におけるか、ってとこにあると思う。具体的には、代数的な構造だとか位相的な構造だとか順序的な構造だとかを持ち出して、超越的にえいって感じで丸め込むのが現代の数学のやり方。

ところで、そんな現代数学のやり口を学ぶ本(あるいは観賞する本)って、数学専攻の学生以外にも役立つような気がするんだけど、どうだろう。読者になり得るのは『ゲーデル・エッシャー・バッハ』を買った人たち。でも、その面白さを潜在的な読者の大多数に書名とパッケージと口コミだけで伝えるのは困難だから、市場での成功は得られないだろうな。『ゲーデル・エッシャー・バッハ』だって、何で売れたのかよくわからないし。仕事でやるとなると、売れるとか売れないとか考えざるを得ない。そもそも誰に書いてもらうかっていうのがネック。数学の言葉で書いちゃダメなわけで。つーか数学の言葉で書いたら、それはただの公理的集合論の教科書なのかも。

あー、もし冒頭の質問が「公理的集合論がわかる本を教えろ」という意味だったのなら、きちんとした内容は別の本で学ぶ必要があるけど、こんな本があります。

『数学の基礎をめぐる論争―21世紀の数学と数学基礎論のあるべき姿を考える』
http://www.amazon.co.jp/gp/product/4431707972

自分が昔読んだときには翻訳がいまいちという印象だった。でも内容は激面白いっす。

2006/07/26

* 「各要素が「0または1をとる乱数」から成る長さnのリストを得よ」
http://oss.timedia.co.jp/index.fcgi/kahua-web/show/ossz/oneline/2006-07-10

「乱数」っていう用語は、とてもあいまい。たとえばこんなのは?
(use math.const)
(use util.stream)

(define so-pseudo-random
(let ((PIchop (let R1 ((x pi))
(stream-cons (floor x) (R1 (* (fmod x 1) 10))))))
(let R ((s PIchop))
(if (odd? (stream-car s))
(stream-cons 1 (R (stream-cdr s)))
(stream-cons 0 (R (stream-cdr s)))))))

gosh>(stream->list (stream-take so-pseudo-random 30))
(1 1 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 0)


ようするに円周率の各桁の偶奇に応じて 0 か 1 を出力しているだけだけど、乱数といえないことはない。ただし当たり前だけど暗号化処理のシードに使ったりするわけにはいかない。予測可能だから。(math.const で提供されている定数を使っているからそのうち 0 が続いてしまう問題には目をつぶったとしても)

以下は宣伝モード。
乱数については、発売したばかりのこちらの書籍もどうぞ。やたらに広範な話題に及んでいるので拾い読みがお勧め。

『Building Secure Software』
http://www.amazon.co.jp/gp/product/427406655X

原書は少し前に出た本なんだけど、翻訳書は適宜監訳注が入ってるので不便はないはず。


ところで出題元の"Open Source WEB"では7月10日の日付になっているけど、Bloglinesにはついさっきようやく入ってきた。どうしてこんなに遅いんだろう。
夏になって女の子たちが塗る日焼け止めの香りが好き。毎年7月ごろはそれが日焼け止めに由来することを忘れてるから、たまに電車に乗ったりすると微かに幸せなきもちになって、ちょっと戸惑う。

いいわけ1:
だいたいこういう基地外っぽいことを書いているときは心が弱っているわけで……

いいわけ2:
「夏服にそわそわ」だけにフォーカスしたIrwin Shawの小説もありますよ、と。

『夏服を着た女たち』
http://www.amazon.co.jp/gp/product/4062748207/

2006/07/25

Gauche のストリームと MIT Scheme のストリームがじゃっかん違う?
SICP の Ex. 3.56 は、「2,3,5以外の素因数を持たない整数全体の集合を以下の stream-scale と merge を使って求めろ」という問題。
(define (stream-scale stream factor)
(stream-map (lambda (x) (* x factor)) stream))

(define (merge s1 s2)
(cond ((stream-null? s1) s2)
((stream-null? s2) s1)
(else
(let ((s1car (stream-car s1))
(s2car (stream-car s2)))
(cond ((< s1car s2car)
(stream-cons s1car
(merge (stream-cdr s1) s2)))
((> s1car s2car)
(stream-cons s2car
(merge s1 (stream-cdr s2))))
(else
(stream-cons s1car
(merge (stream-cdr s1)
(stream-cdr s2)))))))))

素直に解答すると、こうなる。
(define S (stream-cons 
1
(merge (stream-scale S 2)
(merge (stream-scale S 3)
(stream-scale S 5)))))

ところが Gauche(0.8.6)だと、この S を見に行くと止まってしまうようだ。先頭の 1つしか決まっていないストリームを merge にぶちこんで再帰することに問題があるらしい。深さに応じてストリームの先頭付近を具体的にしてやると、意図したストリームに近いものを求めてくれる。
(define S (stream-cons 
1
(merge (stream-scale (stream-cons 0 S) 2)
(merge (stream-scale (stream-cons 0 (stream-cons 0 S)) 3)
(stream-scale (stream-cons 0 (stream-cons 0 S)) 5)))))

gosh> (stream->list (stream-take S 100))
(1 0 0 2 0 0 3 0 0 4 0 0 5 0 0 6 0 0 8 0 0 9 0 0 10 0 0 12 0 0 15 0
0 16 0 0 18 0 0 20 0 0 24 0 0 25 0 0 27 0 0 30 0 0 32 0 0 36 0 0 40
0 0 45 0 0 48 0 0 50 0 0 54 0 0 60 0 0 64 0 0 72 0 0 75 0 0 80 0 0
81 0 0 90 0 0 96 0 0 100)

0を無視すれば正しい集合が求まっている。
しっかし、こういうひっかけがあるときはヒントが書いてありそうなもんだ。つまりたぶんこれはなにかおかしい。そこで MIT Scheme で試してみると、あっさり普通に求まった。
1 ]=> 
(define S (cons-stream
1
(merge (stream-scale S 2)
(merge (stream-scale S 3)
(stream-scale S 5)))))

;Value: s

1 ]=> (stream-ref S 1)

;Value: 2

1 ]=> (stream-ref S 4)

;Value: 5

1 ]=> (stream-ref S 100)

;Value: 1600

なんでだろう。考えられるのは、Gauche と MIT Scheme の stream-cdr が違いそうだってことだろうなあ。また何か勘違いしている可能性もあるので、もっとよく考えること。

2006/07/23

真偽値からなるリストを持っていて、その or を知りたい。つまり、真偽値のリストに #t があるかどうか確かめたい。

最初はブール演算できると信じてた。
gosh> (fold or #f '(#f #f #t #f))
*** ERROR: invalid application: (#<syntax or> #f #f)

この期におよんでプロシージャとシンタックス形式の違いに戸惑うとは。しかし困ったな。シンタックス形式だと apply もできない。
gosh> (apply or '(#f #f #t #f))
*** ERROR: invalid application: (#<syntax or> #f #f #t #f)

常套手段。
(define (or-bool-list ls)
(if (null? ls) #f (or (car ls) (or-bool-list (cdr ls)))))
(or-bool-list '(#f #f #t #f))

いんちき。
(member #t '(#f #f #t #f))

もっとかっこいい方法がいいなあ。

2006/07/22

備忘録。set! の戻り値は R5RS では規定されていない。
Gauche では set! した引数の set! 後の値が返る。
gosh> (let ((x 1)) (set! x 3))
3

MIT Scheme では set! 前の値が返る。
1 ]=> (let ((x 1)) (set! x 3))
;Value: 1

2006/07/21

オフィスの席替えがあった。新しい席はえらい人たちで決めたらしいんだけど、一言でいって現状のベスト(ただし主観)。まあ、主観が大きいとはいえ、全体の雰囲気も先週までとは完全にいい方向へ変わったと思う。人間なんだから苦痛な環境では仕事できないって。
それにしても、ほんとうに上司には恵まれていると思う(会社には恵まれていないけど)。これで仕事をしなければ罰当たりってもんだ。

2006/07/17

キミならどう書く 2.0 - ROUND 2 - Collatz予想
http://ll.jus.or.jp/2006/blog/doukaku2

ROUND 1は素数を求める問題だったので意識的にスルーしてた。今回の問題も、この問題がCollatz予想と呼ばれているなんて知らない頃にpythonで素直に解いていたので、最初はスルーするつもりだった。Schemeで同じように解いてもつまらないし、あれから何か凄い計算の効率化のアイデアを思い付いたわけでもないので。ちなみに、効率化についてはWikipediaにも工夫のしどころが掲載されているみたい(Collatz conjecture)。

むしろ、なんでこんな問題が数学の難問になってるのかが興味深いとこだ。そこでちょっと逆向きに考えて、1に収束するまでのステップ数ごとに数字の分布を見てみようと思った。
(define (one-step-far ls)
(cond ((null? ls)
'())
((and (even? (car ls))
(> (car ls) 4)
(zero? (remainder (- (car ls) 1) 3)))
(cons (/ (- (car ls) 1) 3)
(cons (* 2 (car ls))
(one-step-far (cdr ls)))))
(else
(cons (* 2 (car ls))
(one-step-far (cdr ls))))))

(define (gs step)
(let R ((step step) (collatz '(1)))
(cond ((zero? step) collatz)
(else
(R (- step 1) (one-step-far collatz))))))

収束先である1からの距離が10までの数字たちを並べると、こんな感じ。
gosh> (gs 1)
(2)
gosh> (gs 2)
(4)
gosh> (gs 3)
(8)
gosh> (gs 4)
(16)
gosh> (gs 5)
(5 32)
gosh> (gs 6)
(10 64)
gosh> (gs 7)
(3 20 21 128)
gosh> (gs 8)
(6 40 42 256)
gosh> (gs 9)
(12 13 80 84 85 512)
gosh> (gs 10)
(24 26 160 168 170 1024)
まあ、なんてことない結果なんだけど、やたらに密度が低いことがわかる。つまり、パラパラとしか数字が出現しない。この先、1に収束するまでのステップ数がnである数字の個数は、ほぼ10n/10のスピードで急激に増えていくんだけど、どんなにnを大きくしても、いつまでたっても出現しない比較的小さな数字が残ってしまう(実際、97はn=118で始めて出現する)。
gosh> (length (gs 10))
6
gosh> (length (gs 20))
72
gosh> (length (gs 30))
732
gosh> (length (gs 40))
7628
gosh> (length (gs 50))
79255
こういうパラパラとした自然数の集まりって、素数と同じように、現在の数学(つーか代数)ではうまく扱えない構造なんだろうな。

ところで上で定義したone-step-farを使って「キミならどう書く 2.0」の関数hを書くこともできる。
(use srfi-1)

(define (h n)
(let R ((rest (iota n 1))
(collatz '(1)))
(cond ((null? (lset-difference = rest collatz))
rest)
(else
(R (lset-difference = rest collatz)
(one-step-far collatz))))))
ただしcollatzの長さが爆発するので、これだとh(30)ですら止まらない。h(20)は18と19らしいんだけど……

2006/07/16

10年ぶりに北岳に登ってきた(考えてみたら3000m級に登るのも久しぶりだ)。10年前は北岳から農鳥岳にかけての白根山系を縦歩したんだけど、今回は時間的にも体力的にも無理だろうということで、山頂付近に2つある山小屋のうち「肩の小屋」で一泊して帰ってくるだけのルートを選択。とはいえ、登山口になる広河原の標高が1600mくらいなので、約1500mをひたすら登らなければいけない。ここ数年は奥多摩とか八ヶ岳でお茶を濁してきたので、ワコールの筋力サポート下着を導入することにした。

CW-X
http://www.wacoal.co.jp/products/cw-x/menu.html

まだワコールのサイトには紹介されてないみたいだけど、この7月からトレッキング用の「スタビライクス」という新商品が登山用品店限定で出荷されていたので、それを購入。15500円。どうでもいいけど、なんか山に行くたびに物欲が発動してる気がする。Uさんがいつも「男の子の潜在的な欲望は何かを成長させることだ」と言っていて(鹿野フィルタ済)、自転車趣味のUさんの結論は「自転車趣味は身体を鍛えてスペックアップする喜びと新しいパーツを手にいれてマシンをスペックアップする喜びが同居しているから男の子的に止められない」なんだけど、登山も似たようなものだと思う。しょーじき、実際に山に登るのは辛いことばかりなわけで、それを補う感動が山頂で得られるかというと、そんなこともない。むしろ、下界で「次に登るときはこれを持って行くとうれしいかもしれない」とか「ひざが弱ってきてるから毎日エレベータを使わずに階段を使うようにしよう」みたいなことを考えているのが一番楽しい。登山の醍醐味は、山頂に到達することではなく、出発までの妄想です。

まあ、そうはいっても山頂で晴れると嬉しい。

DSC_0150

今回はちょうど高山植物が一斉に花を咲かせる時期に登ることができた。富士山のシルエットきれいすぎ。

DSC_0183

希少種のキタダケソウ。平年は山頂付近に6月下旬にしか咲かない花なんだけど、今年は雪がけっこう残っていて、そのおかげで3株だけ花を見ることができた。ちなみに、上の写真の前景に咲いているのはキタダケソウではなくハクサンイチゲ。

DSC_0107

なぜか人気の高山植物、オダマキ。

DSC_0124

2006/07/12

夏休み。i.e. 出社しないとはかどる仕事をつぶす週間。とはいえ、平日に自由な時間がとれるんだから、映画のひとつくらいは観にいくし、今晩から明後日にかけては山にいくつもり。

というわけで ミッションインポッシブル3 を観てきた。あー、なんていうか、期待は裏切られませんでした。脇役たちがかっこいいし気が利いているので前作みたくトム・クルーズのオレ映画になってないし、スパイ映画的に分かりやすいきれいなお姉さんたちも単純に美人なだけでなくかわいらしいので共感できるし、飽き飽きするような謎解きに尺を割かず火薬が多めだし、まあ、要するに素直におもしろい。あたまを空っぽにして鑑賞するには最高だと思う。考えちゃだめ。「1作目ではリストラスパイの悲哀で観客の共感を呼び、2作目でアクションオンリーのオレ映画にしちゃって叩かれたから、3作目で新婚スパイの妻への愛を軸にして深みを出そうという意図ね」とか勘ぐっちゃだめ。「新婚スパイの妻への愛を描きましたってわりに、殺される直前に回想してんのが奥さんとのベットシーンばかりかよ」とか突っ込んじゃだめ。しかし、そのほうがスクリーン上では記号として分かりやすいのかもしれないけど、マトリックスも2作目は愛を描きますっていいながら結局はベットシーンだったよなあ。なんだかなあ。それは陳腐だ、ではなく、それは違うと思う。

金曜の夜に友人とビールを飲みながら、「死ぬ前に最後に何を食べるか選択できるとしたら」という話をしていた。うまいものや好物を食べたいというのもあるけど、冷静に100メートルくらい上から考えると、おうちで奥様と普通の食事がしたいです。照れくさいはなしですが。

2006/06/25

そういえば、Ligetiが死んだっていうニュースを最近聞いた。何枚かCDを持ってるけど、ふだん聞いているのはピアノ練習曲集ばかりだし、散漫にいくつかの作品を聞いているだけだったせいか一貫した「らしさ」みたいなものをさっぱり感じられず、あまり思い入れがなかった。思い入れがないと持っているCDのライナーノーツさえ読まないので、どんな背景を持った作曲家なのかもよく知らない。まあ、それでもピアノ練習曲集だけは面白かったので、たまに思い出したように聴いていた。多少の調声が感じられる曲があるのも聴きやすかった。

死んだっていうニュースをきっかけに、ピアノ練習曲集(Naxos 8.555777)のライナーノーツとWikipedia(日本語)だけ読んでみた。けっこう面白いオヤジだったらしい。

改めてこういう記事を読むと、いろいろ府に落ちるところがある。どうやら作風はころころ変わってて、ピアノ練習曲はごく最近のライフワークだったのね。Conlon Nancarrowからの影響についてはライナーノーツでも触れられてたので、確かなんだろう。っていうか、ぼく自身、NancarrowのCDはLigetiよりもはるかによく聴いてる。つまり、ぼくはそういうの(自分では勝手に「パラパラした感じ」って言ってますが)が好きってことで、だからLigetiでもピアノ練習曲だけはよく聴いてたんだろう。

たぶん、前期の楽曲のCDはこれからも滅多に聴かないね。音楽には、作者や演奏者と時代を共有している聴者にとっては極端に有意味なものがあると思うんだけど(Free Jazzとか、Zeppelinの初期とか。それがつまらないという意味ではなく、当時のムーブメントとか気分とかいうやつを共有していなければ正直よく理解できないほどの評価をされているんじゃないか、という意味)、Ligetiの初期の楽曲にもそういうのがありそう。晩年のピアノ練習曲も同じなのかもしれないけど、少なくともぼくには共有できる何かがあるっぽい。だから聴く。ようするに趣味の問題といってしまえばそれまでなわけで。

2006/06/23

どんな分野にも売り上げランキングというのがあって、なにかしらモノを作って販売している人間にとっては気になってしかたがない情報なわけですよ。とくに競合が多い場合は。書籍もその例に漏れないんだけど、レコード業界におけるオリコンのような存在がないから、信用できるランキングというものがない(ここで「信用できる」というのは、少なくともエンドユーザやマスコミや同僚や上司や同業他社といった他者に対するアピールに活用する場合を想定している)。その結果、みんな、主に次のようないろんな意味で偏りがあるランキング情報に頼っている。
  1. 社内で発行した本の販売冊数はだいたい分かるので、そのなかでのランキング
  2. 書店によってはランキング(もしくは実売情報)を公開しているところがあるので、そこから得られる(もしくは推測される)ランキング
  3. Amazon.co.jpのランキング。各書目のページに表示される数字や、特定のキーワードで検索したときに掲載される順番
どれも母集団が限定的すぎるのが偏りの原因だと思う。そして、その偏りを無視してランキングという数字を持ち出されるとき、イラってくる。
もうどうしょうもないのは、1番目のランキングで高水準にある書目を見て、それが日本中でベストセラーになっているかのごとく勘違いしている場合。バカだろ。まあ、そんなケースは出版社の中にいない限りお目にかかれないけど、中にいるとしばしばお目にかかる。
2番目のランキングは、もう少し現実を反映している。でも、書店っていうのは、読者として意識している以上に得意分野と不得意分野の売り上げ差があるものだと思う。だから、特定の書店から報告されるランキングだけ目にして「○○の本は売れている」と言い放つ人は苦手だ。
いちばんイラってくるのは、3番目のランキングだけをもってあーだこーだ言われるケースである。つーか、あの「Amazonランキング」って何さ。1時間ごとに更新されるようだけど、Amazonランキングが1000だったら、少なくともその時間帯には日本で1000番目に売れている本なのか? とまあ、そういう感想が(出版社にいる以上)当然だと思うんだけど、これがぜんぜん当然じゃない。瞬間値にすぎないAmazonランキングをやたらに気にしたり、なんか適当なキーワードで検索して一番先頭に表示されることに意味を見出そうとする。そのような場当たり的な検証からは、その書籍が市場で評価されているかどうか判断できませんから! まあ、百歩譲って、それで個人的な満足を得るだけならいい。でも、たとえば恣意的なキーワードで検索して先頭付近に表示されることに満足するゲームを続けてたりすると、そのうち気分が麻痺してきて、まるでそれが真の市場の評価であるかのような錯覚に陥るものなので、ちゅういしてください。

で、何年か前にもAmazonランキングが取りざたされる状況のあいまいさにむかついて、それなら多少なりとも実際のところを検証できるようにしてやれと息巻き、Amazonランキングの推移を長期にわたってグラフにする実験をした。Amazonの書籍ページからAmazonランキングの数字を一時間に一回引っ張ってきて、それをMRTGに流し込んでるだけだけど、何年も続けてると下図のような結果が得られて興味ぶかい。基本的には会社で動かしているものなので、例としてちょっとだけ公開。グラフは、下に行くほどランキングが高くなっていることを表す。

flat-year
rightup-year

上側のグラフは、ときどき急峻な山(ランキングの落ち込み)があるけど、全体としては地を這うような傾向にある(特に今年の2月以降)。これは、ランキングとしては高値安定なので、そこそこ定番として市場に受け入れられていると思ってよさそうである。ちなみにこの本は、なにをかくそう『プログラミングのための線形代数』です。
一方、下側のグラフ(書名は控えさせていただきます)は、たまに売れてランキングを戻す谷間があるけど、その間はほぼ右肩上がりの傾向にあって、だんだんランキングが下がっていることを示す。つまり、発売後しばらくは売れたかもしれないけど、定番にはならず市場から忘れられちゃって、今ではぽつぽつとだけ売れている本だと思っていい。

まあ、ようするに、バカは使えないけどデータは使いようっていう話でした。

2006/06/18

パズル「数独」をSchemeによる制約プログラミングで解く

SICPは3.4節の手前で足踏み中。3.3節まで練習問題はひととおり終えたけど、ここで実装しているconstraint programingの例に釈然としないものが残る。
わかんないときは自分で例を作ってみるのがいちばん。要するに
  1. 要素間の関係を定義し、
  2. ある要素の値を更新すると、
  3. 他の要素の値も定義した関係にしたがって更新される
という具合に「関係」ドリブンなプログラムを作ってみましょう、という話なんだよね。関係ドリブンで解にたどり着くといえば数理パズルなわけで、数理パズルといえばsudokuでしょう。constraint programingでsudokuソルバとか書けないだろうか。ふつうのsudokuソルバをどのように実装するかきちんと知らないので、もしかしたら当り前すぎてバカバカしい話かもしれないし、あるいは愚かな話なのかもしれない(ちゃんと調べること→自分)。まあ、ここはあくまでもconstraint programingの練習というスタンスで。

まず決めなければならないのは、「要素を何にするか」。ここではsudokuの各セルを要素として、そのセルが「取りうる数字」を考えることにする。最初は各セルとも、1〜9の数字をどれでも取りうる。
次に関係を定義する。ここでは、「各行」「各列」「各ブロック」を関係とする。「各行」「各列」「各ブロック」などをスロットと呼ぶことにすると、どのスロットも9個のセルを含み、それぞれのセルに1〜9の要素が一つずつ入らなければならない。

例えば4x4のsudokuの場合、セルは16個、スロットは12個になる。セル1〜16とスロットA〜Lの関係はこんな感じ。

sudoku-grid4x4

この関係についてSICPちっくなconstraint network図を描くのはやっかいだけど、無理に一部分を描けばこんな感じになると思う。

sudoku-constraint4x4

上図の関係を見ながらプロシージャを書いていく。肝心の関係の定義は、とりあえず
  • 各スロットに、まったく同じ可能性を持つセルがあったら、ほかのセルからその可能性を取り除く


この関係だけでは解を求めるには不十分で、実際、これから示すコードで解けるsudokuの問題はほとんどない。
(define (make-cell init-possibilities)
(let ((possibility init-possibilities)
(slots '()))
(define (set-my-possibility new-possibility)
(set! possibility new-possibility)
(for-each-slot possibility slots))
(define (connect slot)
(set! slots
(cons slot slots)))
(define (me request)
(cond ((eq? request 'possibility) possibility)
((eq? request 'set-possibility) set-my-possibility)
((eq? request 'connect) connect)))
me))

(define (for-each-slot possibility slots)
(cond ((null? slots) 'done)
(else
((car slots) possibility)
(for-each-slot possibility (cdr slots)))))

(define (set-possibility! cell possibility)
((cell 'set-possibility) possibility))
(define (connect cell slot)
((cell 'connect) slot))
(define (get-possibility cell)
(cell 'possibility))


(define (slot . cells)
(define (one-of-cell-has-new-possibility possibility)
(cond ((= (num-of-same-possibility possibility cells)
(length possibility))
(update-cells! cells possibility))))
(define (me possibility)
(one-of-cell-has-new-possibility possibility))
(define (connect-all-cells-to-me cells me)
(cond ((null? cells)
'done)
((connect (car cells) me)
(connect-all-cells-to-me (cdr cells) me))))
(connect-all-cells-to-me cells me)
me)

(define (update-cells! cells possibility)
(cond ((null? cells)
'slot-updated)
((or (equal? (get-possibility (car cells)) possibility)
(<= (length (get-possibility (car cells)))
(length possibility)))
(update-cells! (cdr cells) possibility))
(else
(set-possibility! (car cells)
(complement (get-possibility (car cells))
possibility))
(update-cells! (cdr cells) possibility))))

(define (complement l1 l2)
(define (include? a l)
(cond ((null? l) #f)
((equal? a (car l)) #t)
(else (include? a (cdr l)))))
(cond ((null? l1) '())
((include? (car l1) l2)
(complement (cdr l1) l2))
(else
(cons (car l1) (complement (cdr l1) l2)))))

(define (num-of-same-possibility possibility cells)
(cond ((null? cells)
0)
((equal? possibility (get-possibility (car cells)))
(+ 1 (num-of-same-possibility possibility (cdr cells))))
(else
(num-of-same-possibility possibility (cdr cells)))))

実際に問題を解いてみる。まずは初期化。トップレベルでdefineを繰り返す方法がわからない……。しかたないので、各スロットのセル一覧をリストとして出力するプロシージャで我慢して、それをトップレベルに張り付けてごまかす。
(define-syntax make9x9cells
(syntax-rules ()
((_ e)
(define e (make-cell '(1 2 3 4 5 6 7 8 9))))
((_ e1 e2 ...)
(begin
(define e1 (make-cell '(1 2 3 4 5 6 7 8 9)))
(define e2 (make-cell '(1 2 3 4 5 6 7 8 9)))
...))))
(make9x9cells
c11 c12 c13 c14 c15 c16 c17 c18 c19
c21 c22 c23 c24 c25 c26 c27 c28 c29
c31 c32 c33 c34 c35 c36 c37 c38 c39
c41 c42 c43 c44 c45 c46 c47 c48 c49
c51 c52 c53 c54 c55 c56 c57 c58 c59
c61 c62 c63 c64 c65 c66 c67 c68 c69
c71 c72 c73 c74 c75 c76 c77 c78 c79
c81 c82 c83 c84 c85 c86 c87 c88 c89
c91 c92 c93 c94 c95 c96 c97 c98 c99
)

(define s1 (slot c11 c12 c13 c14 c15 c16 c17 c18 c19))
(define s2 (slot c21 c22 c23 c24 c25 c26 c27 c28 c29))
(define s3 (slot c31 c32 c33 c34 c35 c36 c37 c38 c39))
(define s4 (slot c41 c42 c43 c44 c45 c46 c47 c48 c49))
(define s5 (slot c51 c52 c53 c54 c55 c56 c57 c58 c59))
(define s6 (slot c61 c62 c63 c64 c65 c66 c67 c68 c69))
(define s7 (slot c71 c72 c73 c74 c75 c76 c77 c78 c79))
(define s8 (slot c81 c82 c83 c84 c85 c86 c87 c88 c89))
(define s9 (slot c91 c92 c93 c94 c95 c96 c97 c98 c99))
(define s10 (slot c11 c21 c31 c41 c51 c61 c71 c81 c91))
(define s11 (slot c12 c22 c32 c42 c52 c62 c72 c82 c92))
(define s12 (slot c13 c23 c33 c43 c53 c63 c73 c83 c93))
(define s13 (slot c14 c24 c34 c44 c54 c64 c74 c84 c94))
(define s14 (slot c15 c25 c35 c45 c55 c65 c75 c85 c95))
(define s15 (slot c16 c26 c36 c46 c56 c66 c76 c86 c96))
(define s16 (slot c17 c27 c37 c47 c57 c67 c77 c87 c97))
(define s17 (slot c18 c28 c38 c48 c58 c68 c78 c88 c98))
(define s18 (slot c19 c29 c39 c49 c59 c69 c79 c89 c99))
(define s19 (slot c11 c12 c13 c21 c22 c23 c31 c32 c33))
(define s20 (slot c14 c15 c16 c24 c25 c26 c34 c35 c36))
(define s21 (slot c17 c18 c19 c27 c28 c29 c37 c38 c39))
(define s22 (slot c41 c42 c43 c51 c52 c53 c61 c62 c63))
(define s23 (slot c44 c45 c46 c54 c55 c56 c64 c65 c66))
(define s24 (slot c47 c48 c49 c57 c58 c59 c67 c68 c69))
(define s25 (slot c71 c72 c73 c81 c82 c83 c91 c92 c93))
(define s26 (slot c74 c75 c76 c84 c85 c86 c94 c95 c96))
(define s27 (slot c77 c78 c79 c87 c88 c89 c97 c98 c99))
ためしに解いてみる問題としては、「数学セミナー」の2006年5月号の西川さんの記事41ページに掲載されているものを使うことにした。ちょっとみにくいけど、こんな問題。
(5)(3)( )( )(7)( )( )( )( )
(6)( )( )(1)(9)(5)( )( )( )
( )(9)(8)( )( )( )( )(6)( )
(8)( )( )( )(6)( )( )( )(3)
(4)( )( )(8)( )(3)( )( )( )
(7)( )( )( )(2)( )( )( )(6)
( )(6)( )( )( )( )(2)(8)( )
( )( )( )(4)(1)(9)( )( )(5)
( )( )( )( )(8)( )(1)(7)(9)
これらの初期値を次のように各セルに設定する。
(set-possibility! c11 '(5))
(set-possibility! c12 '(3))
(set-possibility! c15 '(7))
(set-possibility! c21 '(6))
(set-possibility! c24 '(1))
(set-possibility! c25 '(9))
(set-possibility! c26 '(5))
(set-possibility! c32 '(9))
(set-possibility! c33 '(8))
(set-possibility! c38 '(6))
(set-possibility! c41 '(8))
(set-possibility! c45 '(6))
(set-possibility! c49 '(3))
(set-possibility! c51 '(4))
(set-possibility! c54 '(8))
(set-possibility! c56 '(3))
(set-possibility! c61 '(7))
(set-possibility! c65 '(2))
(set-possibility! c69 '(6))
(set-possibility! c72 '(6))
(set-possibility! c77 '(2))
(set-possibility! c78 '(8))
(set-possibility! c84 '(4))
(set-possibility! c85 '(1))
(set-possibility! c86 '(9))
(set-possibility! c89 '(5))
(set-possibility! c95 '(8))
(set-possibility! c97 '(1))
(set-possibility! c98 '(7))
(set-possibility! c99 '(9))
この時点ですべてのセルの値が更新されちゃっているのがconstraint progamingのおもしろいところ。あとは出力だけしてやればいい。
ただし、上記に書いたように最初に与えている関係が不十分なので、解は求まりきってない。
(define (print-possibilities size . cells)
(let R ((ls cells) (cnt 1))
(cond ((null? ls)
'done)
((= 1 (remainder cnt size))
(newline)
(display (get-possibility (car ls)))
(R (cdr ls) (+ cnt 1)))
(else
(display (get-possibility (car ls)))
(R (cdr ls) (+ cnt 1))))))

(print-possibilities 9
c11 c12 c13 c14 c15 c16 c17 c18 c19
c21 c22 c23 c24 c25 c26 c27 c28 c29
c31 c32 c33 c34 c35 c36 c37 c38 c39
c41 c42 c43 c44 c45 c46 c47 c48 c49
c51 c52 c53 c54 c55 c56 c57 c58 c59
c61 c62 c63 c64 c65 c66 c67 c68 c69
c71 c72 c73 c74 c75 c76 c77 c78 c79
c81 c82 c83 c84 c85 c86 c87 c88 c89
c91 c92 c93 c94 c95 c96 c97 c98 c99
)
実行結果
gosh> print-possibilities
gosh>
(5)(3)(2 4)(6)(7)(8)(9)(1 2 4)(1 2)
(6)(7)(2 4)(1)(9)(5)(3)(2 4)(8)
(1)(9)(8)(3)(4)(2)(5)(6)(7)
(8)(2 5)(9)(7)(6)(1)(4)(2 5)(3)
(4)(1 2)(6)(8)(5)(3)(7)(9)(1 2)
(7)(1 5)(3)(9)(2)(4)(8)(1 5)(6)
(9)(6)(1)(5)(3)(7)(2)(8)(4)
(2)(8)(7)(4)(1)(9)(6)(3)(5)
(3)(4)(5)(2)(8)(6)(1)(7)(9)done
この結果を漫然と見る限り、スロット内での重複関係を検証するだけでは解に至らないみたいだ。ここから先は、とあるセルの可能性をどちらか選択してみて、整合性がある解を探索していくしかないのだろうか?

2006/06/13

歯医者。磨きすぎといわれてしまった。歯茎が弱っちゃうって。うーん。自分の認識ではちゃんと磨けてる気がしてなかったんだけど……軽く強迫神経症ぎみになってるのかもしれない。電動ハブラシにすべきなんだろうか。すべきなんだろうなあ。でも置いておく場所がない。

歯医者って一般に「気が滅入ること」リストの上位に食い込むはずの存在だと思う。ところが、むしろ楽しみにしている部分があったりもしているわけですよ。もちろん歯科医院の雰囲気がいいというのもあるけど(受け付けの女の子がけだるそうにきちんと仕事してるようすとか)、それ以上に、ここのところ、いっそう、気の滅入ることが、多すぎる……

2006/06/10

誰も読む必要がない、ザ・日記が続きます。

朝から実家のある柏へ。レイソル戦のチケットを一緒に観戦する友人Kから受け取る。彼はゴール裏の自由席で観戦するので、試合開始の5時間も前からひたすら並んでいる。僕のほうはバックグラウンド側の指定席なので、一緒に観戦するといっても、試合中はお互いに別々の場所に陣取ることになる。ゴール裏は歌をうたったりして応援しなければならないので、つらいんですよ。

そんなわけで僕には試合開始までたっぷり時間がある。まず、彼の自転車を借りて実家へ。たまに顔を出すというのが一番難しい。主にピアノや猫と遊ぶ。それから別の旧友と待ち合わせて昼食。頼まれていた古い絵本を渡す。彼女と話をしていると、いつも、人間の面白さと社会に対する生産性とは相関しないものだと不思議に思う。いや、単純に「類は友を呼ぶ」なのかもしれない。たぶん、彼女も僕も、周囲から見ると同様に計りがたい類なんだろう。

試合開始時間がせまってきたので、あわただしく別れる。彼女は現役の柏市民だけど、ほとんどの柏市民の例に洩れず、レイソルには興味がない。なんか観戦に行く人達って遠足みたいに大きいバッグ持ってぞろぞろ歩いてるよね、とか、そういう感想がせいいっぱいらしい。

試合は楽しかった。個人的には久しぶりに勝ち試合を見ることができたし。

試合後、友人Kと合流してしばらくぶらぶらしてから、新宿の別な友人たちとの飲み会に参加して実のない一日を締めくくる。実のない会話を渾々と続けられる友人がたくさんいることが休日プレイの成否を決めると思う。そして休日プレイは生きていくのに絶対に必要な時間なわけで。

2006/06/06

歯医者。ここ数週間というもの、抜歯した奥歯の跡をどうするかという問題に頭を悩ませていた。方法は4つ。
  • 放置
  • 左右の歯を柱にしてブリッジをかける
  • 部分入れ歯
  • インプラント
デフォルトの治療方法は2番目のブリッジらしい。でも、そもそもこんな状態になった原因は、10年以上前に虫歯跡にかぶせた金属のクラウンの内部で腐食が進んでいたことだと思っているので、歯磨きによる日常のメンテナンスが困難な治療方法は嫌だ。つまり、ブリッジはいや。それに、ブリッジをかけるには左右の健全な歯を削らなければならないんだって。いまのところ左右の歯にはなんの障害もないのに、それを削るのは避けたい。
ブリッジでなければ、普通は入れ歯になるらしい。うーん。この年齢で入れ歯は遠慮したい。で、いっそのこと放置するのはダメなのかと聞いたら、上下左右の歯に支えがない状態になるため、虫歯はともかく歯茎の病気になりやすいくなるらしい。
というわけで、残された治療方法はインプラントしかないっぽい。インプラントは歯茎の骨に支柱を埋め込み、それに人工の歯を設置する方法で、文字通り新しい歯を一本埋め込む。したがって左右の歯を削ることもないし、かぶせものではないので普通に磨ける。ほかの歯への影響が少なく、メンテナンスが容易ってことで、やっぱり歯の治療もモジュール化が重要だな。難点は保険がきかないこと。1本につき30万円くらいみなければならないらしい。あと、僕の場合は土台になる歯茎の骨が再生するのを待たなければならないとのことで、最悪再生しない場合はブリッジをかけるしかないという問題もある。
ものすごく長いスパンで影響を及ぼすことなので、金額の問題は飲む覚悟を決めた。まあ、ぶっちゃけラップトップ一台分だと思えばいいんでしょ。あとは骨が再生するのを願うばかり。

2006/05/28

Dexter社のWalkMocsという靴が気に入っていた。靴底がウレタンのような特殊な材質で、革も軟らかく、なにより出来がよくてはきやすい。結婚したころに恵比寿の三越で見つけて購入したもので、そのころには国内に正規代理店があったんだろう。
最近になって靴底がだめになってしまったので修理したいと思い、取扱店を探したんだけど、ない。どうやら日本での取扱がなくなってしまったようだ。ボーリングシューズだけはどっかのボーリング用品店が輸入しているっぽいけど。こんなことなら先週USにいったときに注意して探せばよかった。革はまだまだしっかりしているので、普通の靴底でもいいから何とか修理できないかなあ。
このメーカーの靴が自分の足に合うことは分かっているので、USのWebサイトから購入しちゃうか。WalkMocsというシリーズはなくなってしまったようだけど、このDiscoveryが近い製品みたい。

2006/05/27

パズル「九個の?」をSchemeで解く

かれこれ1年以上も解けていないパズルがあった。ペントミノの一種で、9個のクエスチョンマークを9×8のグリッドに詰めるというもの。以下のページでJavaアプレットが遊べる(はず)。

実は年末にも一回挑戦している。このときは結局、3日3晩かけても計算が終わらなかった。 解法として思い付くのは、グリッドにクエスチョンマークを置く組み合わせを総当たりで調べる方法のみなんだけど、各クエスチョンマークは上下左右裏表に自在におくことができるため、グリッドのどこかにクエスチョンマークを1つ配置するだけで実は208パターンにもなってしまう。そうすると、調べなければいけない組み合わせはペタオーダーになり、今のコンピュータでは一時的に保持することすら物理的に無理な大きさになる。Gaucheに用意されているcombination-for-eachを使えばいい具合に対処してくれるかもしれないと楽観したけど、やっぱりだめだった。ここまでは咋年末の話。

実際にはクエスチョンマークが安易に重なってしまうようなパターンがほとんどなので、それを無視しつつ組み合わせを求めるようにすれば計算量の爆発が抑えられそうなものだ。そのためのプロシージャは先日作った。というわけで、あらためてこの問題に対峙すべく年末のプログラムを書き直してみた。

; 9q-problem.scm
; 2006/5/26
;
; solver for the "9 questions" quize
; k16.shikano 

(use srfi-1)

(define row 9)
(define line 8)

;; question-mark
(define (face s n)
  (call/cc (lambda (break)
    (cond ((= n 1)
           (if (or (> (+ (remainder s row) 2) row) (> (+ (quotient s row) 6) line))
               (break '())
               (list s                (+ s 1)           ; **
                                      (+ s row 1)       ;  *
                     (+ s (* 2 row)) (+ s (* 2 row) 1)  ; **
                     (+ s (* 3 row))                    ; * 
                                                        ;   
                     (+ s (* 5 row)))))                 ; * 
          ((= n 2)
           (if (or (> (+ (remainder s row) 2) row) (> (+ (quotient s row) 6) line))
               (break '())
               (list s               (+ s 1)            ; **
                     (+ s row)                          ; * 
                     (+ s (* 2 row)) (+ s (* 2 row) 1)  ; **
                                     (+ s (* 3 row) 1)  ;  *
                                                        ;   
                                     (+ s (* 5 row) 1)  ;  *
                     )))
          ((= n 3)
           (if (or (> (+ (remainder s row) 2) row) (> (+ (quotient s row) 6) line))
               (break '())
               (list                 (+ s 1)            ;  *
                                                        ;   
                                     (+ s (* 2 row) 1)  ;  *
                     (+ s (* 3 row)) (+ s (* 3 row) 1)  ; **
                     (+ s (* 4 row))                    ; * 
                     (+ s (* 5 row)) (+ s (* 5 row) 1)  ; **
                     )))
          ((= n 4)
           (if (or (> (+ (remainder s row) 2) row) (> (+ (quotient s row) 6) line))
               (break '())
               (list s                                  ; * 
                                                        ;   
                     (+ s (* 2 row))                    ; * 
                     (+ s (* 3 row)) (+ s (* 3 row) 1)  ; **
                                     (+ s (* 4 row) 1)  ;  *
                     (+ s (* 5 row)) (+ s (* 5 row) 1)  ; **
                     )))
          ((= n 5)
           (if (or (> (+ (remainder s row) 6) row) (> (+ (quotient s row) 2) line))
               (break '())
               (list s (+ s 2) (+ s 3) (+ s 5)           ; * ** *
                     (+ s row) (+ s row 1) (+ s row 2)   ; ***
                     )))
          ((= n 6)
           (if (or (> (+ (remainder s row) 6) row) (> (+ (quotient s row) 2) line))
               (break '())
               (list s (+ s 2) (+ s 3) (+ s 5)           ; * ** *
                     (+ s row 3) (+ s row 4) (+ s row 5) ;    ***
                     )))
          ((= n 7)
           (if (or (> (+ (remainder s row) 6) row) (> (+ (quotient s row) 2) line))
               (break '()) 
               (list s (+ s 1) (+ s 2)                             ; ***
                     (+ s row) (+ s row 2) (+ s row 3) (+ s row 5) ; * ** * 
                     )))
          ((= n 8)
           (if (or (> (+ (remainder s row) 6) row) (> (+ (quotient s row) 2) line))
               (break '())
               (list (+ s 3) (+ s 4) (+ s 5)                       ;    ***
                     (+ s row) (+ s row 2) (+ s row 3) (+ s row 5) ; * ** *
                     )))))))

;; available question-mark faces
(define valid-face-list
  (filter (lambda (x) (not (null? x)))
    (let fs ((s 0))
      (if (> s (- (* row line) 1))
          '()
          (let fn ((n 1))
            (if (> n 8)
                (fs (+ s 1))
                (cons (face s n) (fn (+ n 1)))))))))

;; check if two lists are distinct with each other
(define (distinct? l1 l2)
  (= (length (lset-union eq? l1 l2))
     (+ (length l1) (length l2))))

(define (distinct-cdr ls)
  (let R ((tail (cdr ls)))
    (cond ((null? tail) '())
   ((not (distinct? (car ls) (car tail)))
    (R (cdr tail)))
   (else
    (cons (car tail) (R (cdr tail)))))))

(define (trim-combinations ls n proc)
  (cond ((> n (length ls))
         '())
        ((= n 1)
         (map list ls))
        ((> (- n 1) (length (proc ls)))
         (trim-combinations (cdr ls) n proc))
        (else
         (append
          (map (lambda (x) (cons (car ls) x))
               (trim-combinations (proc ls) (- n 1) proc))
          (trim-combinations (cdr ls) n proc)))))

(trim-combinations valid-face-list 9 distinct-cdr)

これを9q-problem.scmとして、シェルからtimeした結果。

[05:37:46] k16@debian:~/gauche $ time gosh 9q-problem.scm > 9q-result.txt 

real    272m22.609s
user    266m0.799s
sys     0m1.174s

約4時間半。その後、求める組み合わせについてmemoizeとかしてみたりもしたんだけど、実行時間は変わらない。おそらく何か間違ってるんだろう。

気になる結果は、互いに対称かもしれない解が全部で16通り得られた(→解答)。

2006/05/24

仕事でワシントンD.C.に行ってきた。無理やり時間をつくってスミソニアン航空宇宙博物館とナショナルギャラリーへ。どちらも市の中心部にあり、無料なので、打ち合わせのない時間に拝観することができた。ホワイトハウスとか見てませんから。興味ないのでどうでもいいんだけど。残念なのは、市内にはない航空宇宙博物館の別館にいけなかったことかなあ。

R0010059

R0010146

2006/05/15

Firefox 1.5でいつのまにか日本語の均等割り付けができるようになってるのにきがついた。

むかしは苦労したものだ。http://k16journal.blogspot.com/2005/05/htmlcss2.html

2006/05/14

LaTeX(TeX)で文字列中の文字を置換したい。

cf. Character substitution in TeX
http://hisashim.livejournal.com/276024.html

上記のページにほとんど目的の答えがあるので、これをもう少し汎用にしてみた。
\newcommand{\replacechar}[3]{{\wordbyword{#2}{#3}#1\end }}
\newcommand{\cutoff}[2]{{\relax}}
\def\wordbyword#1#2#3{\ifx#3\end \let\next=\cutoff
\else\ifx#3#1#2%
\else#3%
\fi \let\next=\wordbyword\fi \next{#1}{#2}}
\replacecharの第1引数に文字列、第2引数に置換前の文字、第3引数に置換後の文字を指定する。
いくつか制限がある。判明しているのは以下のとおり。
  • 文字列中のスペースは切り詰められる
  • ほかのコマンドの中で使うには\string\replacechar{foo}{o}{a}などとする必要がある
上記の2番目の制限は、どうやら\defの再帰がトップレベルでしか使えないためっぽい。
同じ理由で、\replacecharを入れ子にして使えない。つまり、文字列中の複数の文字を置換したかったら、同様な方法で新しいマクロを定義する必要がある。たとえば、索引の特殊文字をエスケープするコマンドは、次のようにして作れる。
追記:この例では「"」そのもののエスケープができないので注意。
\newcommand{\indexescape}[1]{{\indexwordbyword#1\end }}
\def\indexwordbyword#1{\ifx#1\end \let\next=\relax
\else\ifx#1{!}"!%
\else\ifx#1{@}"@%
\else\ifx#1{|}"|%
\else#1%
\fi\fi\fi \let\next=\indexwordbyword\fi \next}

\def\myindex#1{\index{\string\indexescape{#1}}}
\myindexの定義に\stringを使っているのも2番目の制限のため。

いちおう、実行例。

replacechar.tex
replacechar.pdf

2006/05/12

sumiiの日記 - callccによる排中律の証明
http://d.hatena.ne.jp/sumii/20060507/1147006438

さらに劣化コピー。Schemeのcall/ccで悪魔の契約書を作ってみる。型についてはまったく無知なので、排中律の証明とは関係ありません。
(昨日は会社でhisashimさんに適当な説明をした気がする。ごめんなさい。)

追記:最初のは間違ってたので差し替えました。
(define able-to-pay? #f)
(define remind-contract #f)

(define (devils-contract init-choice)
(let ((ability able-to-pay?))
((lambda (choice)
(cond ((equal? 'first-answer choice)
(display ""))
((and (equal? 'B choice) ability)
(display "Say any wish!"))
(else
(display "Devil gives you one billion dollars"))))
(call/cc
(lambda (continuation)
(cond ((equal? 'A init-choice)
(display "Devil chooses A"))
((equal? 'B init-choice)
(display "Devil chooses B")))
(set! remind-contract continuation)
(continuation 'first-answer))))))
この挿話は、悪魔にとって選択肢が(A)と(B)しかありえないことがポイントなんだと思う。つまり、(B)じゃなければ(A)。(B)でもないし(A)でもないってことはありえないドライな世界。上のコードだと、最初のcondが契約書に相当するつもり。
で、悪魔は自分が「発話した内容」なんて覚えちゃいない。上のコードでいうと、call/ccの中のcondは覚えちゃいない。あくまでも契約書オンリーなので、後から1億ドル払っても、(B)じゃなかったんだから(A)を履行するってことなんだと思う。

くだんのシナリオは、こんな感じ。
gosh> (devils-contract 'B)
Devil chooses B
gosh> (set! able-to-pay? #t)
#t
gosh> (remind-contract 'B)
Devil gives you one billion dollars

最初の(devils-contract 'B)で悪魔自身は「(B)を選びます」と言っているけど、人間が1億ドル払わなかった時点で実は(A)の契約が成立している。あとから1億円用意(set! able-to-pay? #t)してもだめ(悪魔の契約履行が10年後だったのがひっかけっぽいかも)。

1億円支払い可能な状態で契約し、悪魔が(B)を選択すれば、望みがかなえられるんでしょう。そんな人に悪魔が契約を持ち込むとは思えませんが……
gosh> (set! able-to-pay? #t)
#t
gosh> (devils-contract 'B)
Devil chooses B
gosh> (remind-contract 'B)
Say any wish!

2006/05/10

去年の秋から超朝方を心がけているのだけど、それが可能な体調の期間と困難な体調の期間(ねむ期)がある。ねむ期には生産性のピークが20:00過ぎに訪れるが、それでも非ねむ期における早朝の生産性には程遠い。
ここ一ヶ月にわたって長いねむ期が続いている。ねむ期から抜け出す方法が知りたい。ねむ期→非ねむ期の切り替えは休日の直後に訪れることが経験的にわかっていて、ということは休めばいいのか……

2006/05/08

枝刈りをしながら組み合わせを求めたい。つまり、似たような要素を同一視して、集合の総数を絞りながら組み合わせを求めていきたい。

ふつうなら集合をユニークな要素だけで構成しなおしてから組み合わせを求めればいいんだけど、「ユニーク」の意味によってはそれができない場合もある。
たとえばベクトルからなる集合で、「いずれかの項が同じであれば同一視」のような場合。次のような2次元ベクトルからなる集合Aがあるとして、
A = {(1, 2) (2, 3) (3, 4) (4, 5) (5, 6) (6, 7)}
集合Aからすべての項が異なる3つのベクトルを取り出す組み合わせは、
{(1, 2) (3, 4) (5, 6)}, 
{(1, 2) (3, 4) (6, 7)},
{(1, 2) (4, 5) (6, 7)},
{(2, 3) (4, 5) (6, 7)}
の4つになるだろう。この4つを求めるのに、すべての3要素の組み合わせを求めてから相異なる要素で構成されているものを取り出していると、集合が大きくなるにつれ厄介なことになるのが目に見えている。かといって、集合Aをあらかじめユニークな要素だけで再構成することもできない。

まず、ふつうの組み合わせを求める combinations プロシージャを次のような戦略で考える。

集合をリスト ls とみなし、その要素から n 個の組み合わせをすべて求めるプロシージャ (noraml-combinations ls n) を、次の 1.と 2.の append として作る。
  1. (car ls) と (noraml-combinations (cdr ls) (- n 1)) の cons
  2. (normal-combinations (cdr ls) n)
1.から「(car ls) を先頭の要素に持つ組み合わせ」がすべて得られ、2.で同様の操作をリスト全体にわたって行うつもり。ザ・リストの再帰処理。
(define (normal-combinations ls n)
(cond ((> n (length ls))
'())
((= n 1)
(map list ls))
((> n (+ 1 (length (cdr ls))))
(list ls))
(else
(append
(map (lambda (x) (cons (car ls) x))
(normal-combinations (cdr ls) (- n 1)))
(normal-combinations (cdr ls) n)))))
境界条件がなんか複雑で、本当にこれであってるのかよくわからないのはここだけの話。まあ、とりあえず意図通りの結果にはなるみたい。
gosh> test-set
((1 2) (2 3) (3 4) (4 5) (5 6) (6 7))
gosh> (normal-combinations test-set 3)
((#0=(1 2) #1=(2 3) #2=(3 4)) (#0# #1# #3=(4 5)) (#0# #1# #4=(5 6))
(#0# #1# #5=(6 7)) (#0# #2# #3#) (#0# #2# #4#) (#0# #2# #5#)
(#0# #3# #4#) (#0# #3# #5#) (#0# #4# #5#) (#1# #2# #3#)
(#1# #2# #4#) (#1# #2# #5#) (#1# #3# #4#) (#1# #3# #5#)
(#1# #4# #5#) (#2# #3# #4#) (#2# #3# #5#) (#2# #4# #5#)
(#3# #4# #5#))
枝刈りしながら組み合わせを求めるという本題を達成するには、枝刈りプロシージャ proc を渡して、1.の操作の cdr を proc に変えればいいだろう(2.の部分の cdr はリストの再帰的な操作のためのものなので、proc に変える必要はない)。
(define (trim-combinations ls n proc)
(cond ((> n (length ls))
'())
((= n 1)
(map list ls))
((> (- n 1) (length (proc ls)))
(trim-combinations (cdr ls) n proc))
(else
(append
(map (lambda (x) (cons (car ls) x))
(trim-combinations (proc ls) (- n 1) proc))
(trim-combinations (cdr ls) n proc)))))
枝刈りプロシージャとしては、ベクトルが相異なることを表現する distinct? と、リストとして表した集合から car と相異なる cdr を導く distinct-cdr 用意する。gauche の lset-union は、(use srfi-1) が必要だね。
(define (distinct? v1 v2)
(= (length (lset-union eq? v1 v2))
(+ (length v1) (length v2))))

(define (distinct-cdr ls)
(let R ((tail (cdr ls)))
(cond ((null? tail) '())
((not (distinct? (car ls) (car tail)))
(R (cdr tail)))
(else
(cons (car tail) (R (cdr tail)))))))
実行結果。
gosh> (trim-combinations test-set 3 distinct-cdr)
((#0=(1 2) #1=(3 4) (5 6)) (#0# #1# #2=(6 7)) (#0# #3=(4 5) #2#) ((2 3) #3# #2#))
しかしアレだな。lengthとか使いすぎなので、あまり効率よくないんじゃないか、これ。

2006/05/06

『RailsによるアジャイルWebアプリケーション開発』の制作方式

ここのところ『RailsによるアジャイルWebアプリケーション開発』の制作方式について意見を求められる機会が何回かあったので、もう半年も前のことだけどまとめてみる。

前提として、ふつうのコンピュータ書籍の制作過程には昨日定義した「後戻り困難ポイント」があり、それが原因で誰かが泣いている。誰も泣かないようにするには、昨日愚痴った「後戻り困難ポイント」を埋めてしまえばいい。それには、現在の手作業によるページレイアウトの工程(つまり組版工程)を放棄して、内容に責任を持つべき人間が印刷の直前までハンドリングできるようにすればいい。

『RailsによるアジャイルWebアプリケーション開発』の制作では、この方針を実現すべく、以下のような方法を採用した。
  • 印刷所に納品するデータはフォント埋め込みのPDF
  • そのPDFはLaTeXによる組版データから生成する
  • しかし原稿はLaTeXではないので、原稿をLaTeXに変換するスクリプトを用意する
  • 原稿はSubversionで版管理する
こうやって書き並べると単純すぎる話だけど、これらの方法を採用することによるメリットは強大だった。なにせ、著者(監訳者)や編集者がSubversionからチェックアウトした原稿(≠組版データ)を印刷の直前まで自分で修正でき、その結果が人手を介さずに印刷用の最終的な組版データになるんだから。ようするに、昨日の「後戻り困難ポイント」のうち、組版→編集→執筆の容易な還流が可能になる。あるいは、こういいかえてもいい。これまで組版の工程にかけていた数ヶ月の期間をゼロにできる!

たぶん、こんなのはソフトウェア開発では当たり前のことなんだと思う。ソースコード(原稿)を版管理し、それをデイリービルド(組版)しながら開発(制作)するってだけの話なんだから。ところが現在のコンピュータ書の制作では、コンパイルやビルドに相当する「原稿を組版にする作業」が完全に手作業なため、組版した後で判明した問題を原稿に戻って直したり、直して組版し直した結果をすぐに確認することができない†1

実現にあたっては障壁もいくつかあった。まず、原稿がLaTeXじゃない。また、仮にLaTeXであっても、それだけでは印刷所に安心してデータを渡すことはできない(印刷所の環境で同じようにコンパイルできる保障がない。そのため、印刷所の環境でコンパイルした出力を確認するという作業が不可欠になってしまう)。

印刷所に渡すデータの問題については、PDF/X-1aという業界標準があるので、それに準拠したPDFを生成できれば問題ない。これについては「Debianにotfパッケージをインストールして、dvipdfmxでOpenTypeフォントを埋め込んだPDFを吐き出すまで」を参照。

原稿がLaTeXじゃない問題については、まず原稿がどんな形式だったか示す必要があるだろう。こんな感じのテキストデータだった。
■H1■はじめに
■H2■本書の読み方
本書は初めてRailsに触れる人うんぬん……

★Rubyのコード
本書にはRubyのコードがたくさん出てきます。
しかも予約語は太字に、ダブルクォーテーション内はスラントにしなくちゃなりません。
◆→コード←◆ruby
puts "Hello World"
◆→ここまでコード←◆
ちなみに★で始まってる部分は小さな見出しつきの項目になります。

ここは、また本文に戻ってます。

● 箇条書きもあります。
こんなふうに、なんとなく箇条書きな部分がタブで示されています。
● もうひとつ箇条書き

また本文。

◆→コラム←◆Joeの疑問
枠で囲った記事もあります。「David曰く」「Joeの疑問」のほかに、何も指示のないコラムもあります。
◆→ここまでコラム←◆

1. 箇条書きとは別に
2. 連番もあります

■H3■もっと細かい本書の読み方
脚注もつけなければ◆→訳注 ←◆

■H2■終わりに
おしまい。
これは下訳をお願いした業者さんがよく使っている形式(というか編集指示)を拡張したものだけど、多かれ少なかれどこも似たような「マークアップ」を使っているようだ。見てわかるように、あくまでも人間が手作業で組版するためのコメントみたいな指示書き程度しか施されていない。コンピュータでそのまま処理するにはちょっと厄介な状態といえる(しかも、バリデータなんてないから、けっこういいかげん)。

このような「自然言語+簡易マークアップ」の構造を読み取って適切なLaTeXのスタイルに対応付けるためのフィルタを用意する必要がある。今回はゆるい規則を処理しなければいけないこともあって、後から柔軟にフィルタの仕様を変更できるようなGaucheスクリプトを正月にでっちあげた(私的に余暇を利用して作ったものなので、後日公開する予定。いま見返すと謎な処理が多いけど、なんとか目的の動作は実現している)。

上記の例の変換結果は以下のとおり。
\chapter{はじめに}%

\section{本書の読み方}%

本書は初めてRailsに触れる人うんぬん……%

%

\begin{entry}%
\item[Rubyのコード]
\item 本書にはRubyのコードがたくさん出てきます。
\item しかも予約語は太字に、ダブルクォーテーション内はスラントにしなくちゃなりません。
\begin{ruby}

puts \codesl{"Hello World"}
\end{ruby}%
\item ちなみに★で始まってる部分は小さな見出しつきの項目になります。%
\end{entry}%

%

ここは、また本文に戻ってます。%

%

\begin{myitemize}%
\item 箇条書きもあります。%
こんなふうに、なんとなく箇条書きな部分がタブで示されています。
\item もうひとつ箇条書き%%
\end{myitemize}%

%

また本文。%

%

\begin{column}{J}{の疑問}
枠で囲った記事もあります。「David曰く」「Joeの疑問」のほかに、何も指示のないコラムもあります。
\end{column}%

%

\begin{enumerate}%
\item 箇条書きとは別に%
\item 連番もあります%%
\end{enumerate}%

%

\subsection{もっと細かい本書の読み方}%

脚注もつけなければ\footnote{\kern-.5zw[訳注]}%

%

\section{終わりに}%

おしまい。
このLaTeXの出力から書籍にするには、さらに各要素についてスタイルを定義する必要がある。実はこの作業がいちばん大変なところなんだけど(LaTeXのいい加減な規則や拡張に起因)、これは会社の業務として作成したものなので今のところ公開できない。まあ、LaTeXの泥臭いところが満載なので、実際のところあまり面白くないし。

さて、ここまでは、この方法が従来の方法に比べて万能であることを意図的に強調してきたけれど、現実には適用できないプロジェクトが大半である。著者や制作業者の並々ならない協力が必要なこと、全員がアクセスできるサーバ環境が必要なこと、編集者に現状に対する問題意識が少ないこと、など原因はいろいろある。今回はとくに変換スクリプトをSchemeで作っちゃったりしたので、問題意識のある編集者であっても実際に使ってもらいにくい。

希望はある。こういった問題を解決して汎用性の高いツールにまとめあげる大仕事を身を挺して背負ってくれそうな人がいる。できる限りの協力はしようと思う。



もちろん例外はあって、最初から(商品として妥当な)レイアウトを含めて執筆された(執筆者のローカル環境以外でも同様にコンパイルが通る)LaTeXの原稿があれば、少なくとも執筆者は現在でも最後まで原稿を修正できる。ただし、編集者にLaTeXのスキルがなかったり、印刷所でコンパイルが通らなかったりして、必ずしもうまくいくとは限らない。

2006/05/05

ふつうのコンピュータ書籍がどうやってつくられているか。
  1. 企画
  2. 執筆や翻訳
  3. 編集
  4. 組版
  5. 印刷製本
おわり。

といいたいとこなんだけど、現実はこんなにシンプルじゃない。問題なのは、各ステップがほとんど分裂していること。
  • 編集してから執筆をやり直してもらうことは、よほどのことがない限りできない。
  • 組版してから編集をやり直すことは、実際には頻繁にやらざるをえないんだけど、とても手間と時間がかかる。
  • 書いてはみたけど時間が経ちすぎて市場性がいまいちになっちゃったね。でも企画からやり直すわけには……
  • 当然のことながら、印刷してから誤植が見つかっても直せない。
うー。

上にあげた4つの「後戻り困難ポイント」のうち、最後の1つは物理的にどうしょうもない。どんな製造業にも後戻りできない一歩っていうのがあって、書籍制作の場合にはそのひとつが印刷工程なんだと思う(流通するまでは本当の意味で後戻りできないわけじゃないんだけど)。

で、残りの3つの「後戻り困難ポイント」に直面した場合どうなるか。従来ありがちなのは、順に
  1. 読者が泣く
  2. 制作業者が泣く
  3. 担当者(i.e. 出版社)が泣く
  4. 著者が泣く
この順番に並ぶのは理由がある。まず、「後戻り困難ポイント」に直面するのは制作中の書籍の内容に十分な価値が認められない危惧が生じた場合なわけで、そのまま後戻りできずに流通までいたってしまった書籍の読者が泣く(このケースがほんとに多くていやになる)。
残りは読者が泣いていないケース。つまり、後戻りが不要だった(原稿が完璧で編集も完璧で組版も完璧)か、どっかの時点で困難を乗り越えて後戻りに成功した本である。「後戻りに成功」って書くと、プロジェクトX的な何かっぽくて聞こえがいいけれど、そんなわけはない。誰かがどこかで泣きながら困難な後戻りをやっている。誰がやっているかっていうと、出版社は著者に頭があがらないし、制作業者は出版社に頭があがらないから、推して知るべし。

すっかり愚痴っぽくなってるけど、ようするに現在の書籍制作は前近代的な「誰かが泣く」ソリューションに負っている。もちろん各工程で何も問題がおきなければいいんだけど、文筆のプロではない著者に本業の傍らで執筆してもらい(しかも半ば善意で)、それを一人の編集者が何本も同時にハンドリングし、安い単価でMacオペレータに手作業で組版してもらっている限り、デフォルトで誰かが泣いている。ということは、現在のやり方には何かしら問題がある。

どこに問題があるんだろう。考えられる可能性は、こんなとこ。
  • 紙の本が儲からなすぎ。何事も品質を求めれば金がかかるけど、金をかけようとしないので品質が悪くなり、読者が泣く。あるいは、何とか限られた金額で品質を高めようとして制作業者が泣く。編集者もサービス残業漬けになって泣く。
  • みんな紙が好きすぎ。とにかく紙に印刷されたものベースでしか制作が進まない。内容に責任を負うべき人間が制作にかかわる唯一の方法は、紙へ手作業で赤字を書き入れること。それをもとに、内容には関与しない人間が、やはり手作業でちまちまとデータを修正する。赤書きした本人が修正結果を確認できるのは数週間後だったりする。修正作業者が内容を理解しているわけではないので、その作業が新たな問題を生むことも少なくない。
  • 制作技術の進歩がなさすぎ。ページレイアウトを作るのに、いまだに植字工が活版を組んでいるのと原理的に同じ作業をコンピュータを使って手作業でやってる。一度組んでしまったものをスケジュールを崩さずに直すのは不可能。あとどうしょうもなくバカバカしいのは、節番号や図暗号の連番をふったり、ページ番号を参照したり、索引のページを解決したりするのが、全部手作業なこと。そんなのLaTeXやMS Wordでも自動でできるって(DTPソフトによってはできるものもあるけど、オペレータの数が限られていて一般には利用されていないというオチ。このへんも手作業な世界観が支配的で情けなくなるところ)。
というわけで、企画するまではともかく、そこから先の印刷直前までの工程は一種のバクチなのが実情。

ではどうするか。いちばんキモなのは、内容に責任を持つ人間が最終的なページレイアウトまでをハンドリングできるようにすること。紙に赤字で修正指示を出して……という他人任せな方法は極力回避する。これには2つメリットがある。
  • ありえないミスがなくなる。赤字の修正指示は字の汚い人間にとって苦痛なだけでなくリスキーでもある。それを見てデータを直す人間が内容を理解していれば対応できるけど、専門書でそんなことは期待できないわけで。とくに数式をよろしく対応してもらうのは絶望的
  • 制作期間を短縮できる。紙をやりとりするのは時間の無駄。
これを実現するには、「最終的なページレイアウト」とか、そういう夢見心地な発想をあきらめること。この発想の背景には、内容とレイアウトは別物という意識がある。たぶんこの意識はページレイアウトをする側のものだと思う。確かに本当に凝ったレイアウトを扱うにはそれだけを見る人間が必要だけど、そんなのは「伝票をチェックするためだけに人を雇う」というのと同じ発想で、それだけの規模がある業務なりプロジェクトなりでなければ無駄でしかない。(ちなみにここでレイアウトって言ってるのはデザインのことじゃない。)

で、ページレイアウトって、正直Webページ程度の表現力があれば事足りるものが多い。Webページであれば、公開する直前まで執筆してる人間が内容を修正し、それにCSSなりでスタイルを当てれば十分なコンテンツになる。しかも、執筆している人間に公開の直前まで許されている修正は、「てにおは」レベルのものじゃない。文章の階層構造はもちろん、説明の順番や図の配置まで、全部修正できる。どうして書籍の制作ではWebページのようにぎりぎりまで原稿を修正することができないのだろう。
Webブラウザと紙ではメディアとしての性格が違うという人も(DTPによる書籍制作にかかわってきた人のなかには)いるだろうけど、それは説得力がない。LaTeXとか、20年前からふつうに同じようなことができてたわけで、紙の制作に限って技術的な制限があるなんていうのはDTPソフト会社にだまされているだけだ。

2006/04/25

今日の一行 2006-04-20 [quiz] 部分木の格上げ
http://oss.timedia.co.jp/index.fcgi/kahua-web/show/ossz/oneline/2006-04-20

「述語と木を与えて,述語を満すラベルの付いた部分木をその親の直ぐの弟にする関数を書け.」という問題。またまた出遅れ気味の回答。この翌日で、逆に部分木を格下げする問題も出題されているけど、こちらはカテゴリがquizではないらしいので手をつけないことにしよう。この問題だけで力尽きたともいう。
(define (lookup subtree pred)
(let lookup-for-suns ((suns (cdr subtree)))
(cond ((null? suns) '())
((pred (caar suns)) (car suns))
(else
(lookup-for-suns (cdr suns))))))

(define (include? subtree pred)
(let check-for-suns ((suns (cdr subtree)))
(cond ((null? suns) #f)
((pred (caar suns)) #t)
(else
(check-for-suns (cdr suns))))))

(define (without-grandchild? tree)
(or (null? (cdr tree))
(let without-children? ((suns (cdr tree)))
(cond ((null? suns) #t)
((null? (cdr (car suns)))
(without-children? (cdr suns)))
(else #f)))))

(define (extract tree pred)
(cond ((null? tree) '())
((pred (caar tree))
(extract (cdr tree) pred))
(else
(cons (car tree)
(extract (cdr tree) pred)))))

(define (rankup tree pred)
(if (without-grandchild? tree)
tree
(cons (car tree)
(let rankup-for-suns ((suns (cdr tree)))
(cond ((null? suns) '())
((include? (car suns) pred)
(cons
(cons (caar suns)
(extract (cdar suns) pred))
(cons (lookup (car suns) pred)
(rankup-for-suns (cdr suns)))))
(else
(cons (rankup (car suns) pred)
(rankup-for-suns (cdr suns)))))))))

(define test-tree
(list 'A (list 'B (list 'F)
(list 'G (list 'L)
(list 'M)))
(list 'C (list 'H)
(list 'I (list 'N)
(list 'O (list 'P)
(list 'Q)))
(list 'J))
(list 'D)
(list 'E (list 'K))))

(define eq-I? (lambda (x) (eq? 'I x)))

gosh> test-tree
(A (B (F) (G (L) (M))) (C (H) (I (N) (O (P) (Q))) (J)) (D) (E (K)))
gosh> (rankup test-tree eq-I?)
(A (B (F) (G (L) (M))) (C (H) (J)) (I (N) (O (P) (Q))) (D) (E (K)))

いかにも、SICPの前半を読んでいる最中です、という雰囲気のただよう回答にみえる。きをあつかうのは大変だ。
今日も歯医者。消毒だけだと思って気を抜いていたら、また注射があった。でも、昨晩心配していたような痛みもなく、本当によかった。この後は、一ヵ月くらいそのまま歯茎が再生するのをまって、ブリッジかなにかするらしい。歯茎が再生したらこのままでもいいんだけど、それはダメなんだろうな……

2006/04/24

先週、奥様が自分の歯医者にいくというのでくっついていって検診してもらったら、レントゲンをとられて奥歯を一本抜くことになった。で、いまさっき抜いてきた。小学校のころいれた銀歯で、ずいぶん前からぐらついていたのは自分でも知ってたんだけど、こういうのってどうしても放置しちゃう。結局、長年の間にクラウンの中はすっかりぼろぼろになっていた。レントゲン写真を見て、自分から「抜いてください」とお願いしたくなるくらい。へたに周りの歯がしっかりしているものだから、かえって痛々しさが目立つ。

歯を抜くのは初めてなので、ここ一週間は夜も眠れないくらいびくついていた。だいたい、人間が痛みに耐えるときは歯をくいしばるものなのに、歯を抜くってことはそれができない。麻酔をかければ痛くないっていうけど、その麻酔の注射を歯茎に射すわけじゃん。麻酔されるまで診察台の上で終始手をきつく握り合わせていたら、先生に「そんなに心配しなくて大丈夫ですよ」といわれてしまった。実際、ほとんど痛くなかった。歯を抜く作業も手際がよくて、すーっと何かが抜けていく感覚だけ。ここ一週間、歯を抜く痛みのイメージトレーニングをひそかに繰り返していたので、脳内で大量のエンドルフィンが生成されていただけかもしれない。もちろん、先生の腕もいいんだと思う。彼の一連の作業の流れを見ていればわかる。抜いた後に再度レントゲン撮影して状態を確認するのも好感がもてる。でも、本当に痛み出すのは麻酔が切れる今晩からだっていうしなあ。幸いなのは、頼みの綱として痛み止めと化膿止めをもらったこと。しかも、ロキソニンとケフラールだった。これならうちに山ほどあるので、使い切ってもちょっと安心。

2006/04/21

小島麻由美が好きだ。もう、3月に出た6枚目のアルバムが、すごくいい。

スウィンギン・キャラバン
http://www.amazon.co.jp/exec/obidos/ASIN/B000EAV942/

小島麻由美を聞いたことのない人には、2000年のベストがお勧め。

Me And My Monkey On the Moon�Singles and Outtakes�
http://www.amazon.co.jp/exec/obidos/ASIN/B00005HP68/


このベストの後に出た4枚目は個人的に肌に合わない。この6枚目にして、ようやく戻ってきてくれたみたい。
4枚目は、世間的な評価はいいようだけど、どうにもオサレ風なほんわかエフェクトが強くて好きになれない。録音も不用意に音を詰め込みすぎ。歌詞も斜めな視線を気取りました的だったし。もっとこう、かわいくって異次元な小島麻由美の歌を、モヤモヤしていないソリッドな音で聞きたいわけよ。ひたすらショートケーキとか連呼してほしいわけよ。
6枚目にいたって、ようやくそういう雰囲気に戻ってきてくれた。手放しでよろこばしい。脳内には「らすーとのつーもりでー」がリピートされまくり、チョコレートがとけまくり、トルココーヒーが渦巻いている。
ただ、初期のころに比べて「うまさ」を前に出してきてるのは、やっぱり進化だと思うしかないのかなあ。いまは、「うまさ」が「芸風」を犠牲にしていない、そういうぎりぎりのところにいる気がする。そのぎりぎり感が、このアルバムを傑作たらしめているんだとも思う。でもやっぱり引っかかるんだよなあ。なぜか一曲目にして「また明日ね!」といわれてしまうアルバムの構成とか、初期のころの特徴といっていいと思う「妄想のかたまりな歌詞」を意識的に創作しているように見えてしまうところとか。

社内の雑談で「小島麻由美なんてしらねえよ」と言われてしまったので勢いで書いてはみたけれど、読み返すと批判に見えちゃうな。でも違うよ。とにかく小島麻由美が大好きだってことで。


どうでもいいけど、「トルココーヒー」を聞いていると藤子・F・不二雄の短編の「アン子、大いに怒る」の一場面が思い浮かんじゃう。ちょうどはまっている最中っていうのもあるけど、それにしたってあそこで飲んでいるのはコーヒーじゃなくて紅茶だ。でも、外国土産のいかがわしい飲み物をきわめて日常な場面で飲むっていうところは通じていて、結局そういう「日常→妄想」の境界付近が自分は好きなんだろうと思う。

2006/04/19

一区切りついたので、ここ数日たまりまくった Bloglines を眺めていたら、また面白げな問題が話題になっている。例によって乗り遅れ気味。
2回作用させると符合が変わるという性質は、虚数とか、三角関数の微分とか、回転行列( (0 -1 \\ 1 0) )とかがあるけど、どれも実数から実数への関数ではない。というわけで、こんないんちきしか思いつきません。
x = sinθ
f(x) = (d/dθ) x

=>

f(x) = cosθ
f(f(x)) = -sinθ
= -x(!?)

こんなのは関数の定義とはいえないわけで。

2006/04/13

昨日、Red Bull という話題の清涼飲料水を試しに飲んでみたら、ほんとに眠くならない。それで、一日がんばって仕事をしたら、今朝になって風邪の初期症状が。2つの説明。
  • Red Bull は一時的に体力をブーストするだけであり、人間の体力には絶対的な限界があるので、必ずあとでツケを支払うことになる
  • Red Bull は本当に体力を増強する。本当は昨日から風邪だったのに、昨日は Red Bull のおかげでなんとかがんばれた
論理の錯誤は無視して、今日のところはもう一本 Red Bull を飲んでがんばらなければならない。
 

2006/04/07

昼休みの明けの妥協案。けっきょくカウンタみたいなものを使うことにした。あと、car部が巡回リストを含む場合はチェックできない(これをなんとかしようと考えてたら、もうこんな時間!)。あくまでも、「cdrを追っていくようなプロシージャが停止しないリスト」かどうかを調べるってことで(つまり、今朝の z みたいなものは引っかからない)。
(define (cdr-cycle? x)
(if (not (pair? (cdr x)))
#f
(let R ((x x) (y (cdr x)) (c 0))
(cond ((null? y) #f)
((eq? x y) #t)
((< 0 c) (R (cdr x) y (- c 1)))
(else (R x (cdr y) (+ c 1)))))))
gosh> x
(0 . #0=(1 2 . #0#))
gosh> (cdr-cycle? x)
#t
gosh> y
(0 0 . #0=(1 2 . #0#))
gosh> (cdr-cycle? y)
#t
gosh> z
((0 . #0=(1 2 . #0#)) . 4)
gosh> (cdr-cycle? z)
#f
突っ込みありがとうございます。>Anonymous
サブリストが巡回しているケースということであれば、昨日の「全巡回リストチェックルーチン」を car と cdr についてチェックするように修正すれば大丈夫そうだけど、はたして問題はそこなんだろうかという不安が残る。
;; ex. 3.19
(define (cycle? x)
(define (whole-cycle? sub)
(if (or (null? sub) (not (pair? sub)))
#f
(let R ((y (cdr sub)))
(cond ((null? y) #f)
((eq? sub y) #t)
(else (R (cdr y)))))))
(if (or (null? x) (not (pair? x)))
#f
(or (whole-cycle? (car x))
(whole-cycle? (cdr x)))))

gosh> x
(0 . #0=(1 2 . #0#))
gosh> (cycle? x)
#t

サブリストについては再帰的にチェックしていないので、こうすればまた同じ問題に悩まされる。
gosh> x
(0 . #0=(1 2 . #0#))
gosh> (define z (cons x 4))
((0 . #0=(1 2 . #0#)) . 4)
gosh> (cycle? z)
...

論理的には、こういう再帰的なチェックをすれば解決するのだと思う。or の評価順序の問題があるので、これはもっとどうしょうもなく停止しないけど。
(define (cycle? x)
(define (whole-cycle? sub)
(if (or (null? sub) (not (pair? sub)))
#f
(let R ((y (cdr sub)))
(cond ((null? y) #f)
((eq? sub y) #t)
(else (R (cdr y)))))))
(if (or (null? x) (not (pair? x)))
#f
(or (cycle? (car x)) (whole-cycle? (car x))
(cycle? (cdr x)) (whole-cycle? (cdr x)))))

どうすっかな。根本的な考え方を変更する必要があるのかも。

研究者にとっては問題を解くことじゃなく問題を見つけることが重要な仕事だって、どっかで聞いた記憶がある。僕は研究者ではないけれど、ほかの仕事の多くでも、問題を見つけることは積極的に評価されるべきだと思う。
というわけで、重ねてありがとうございます。>Anonymous

あー、なんか朝からハイだ。

どうでもいい妄想して勝手に怒りが込み上げてきた。あえて猥褻な言葉を使ってお茶を濁すと、問題を見つけることがビジネスモデルで、それを解くために投入する時間とコストと人材がビジネスポリシー。見つけた問題がセンスない&&周回遅れで、しかもその解決方法が的外れなんだから、誰とは言わないけど終わってる。まったく。

2006/04/06

かと思うと、exercise 3.19 のように、(This requires a very clever idea) と補足されているような「とんち問題」もある。問題は、巡回リストかどうか判定するプロシージャを set! とか使わないで書け、というもの。

clever idea がなくても、無限集合の重要な性質を知っていればすぐに思い付く方法がある。その性質というのは、「無限集合は、その部分集合と濃度が等しい」というもの。集合の濃度っていうのは、要素の個数くらいの意味。「無限の個数ってなんだよ」って開き直ったら負けで、「一部分だけ取り出してきたのに元と同じくらいたくさん詰まっているのを無限っていうことにしようぜ、逆に」というのが数学なんだと思う(適当)。なんにせよ、clever idea があるとしたら、それは集合論を思い付いた Cantor のものだ。

とにかく、巡回リストも無限集合みたいなもんなので、その一部分が元のリストと同じだけの要素を持っているっていう性質がある。とくに巡回リストの場合は、cdr を取っていくと、そのうち元のリストと同一(eq? の意味で)になる。必ず。

そこで、こう書ける。
;; ex. 3.19
(define (cycle? x)
(if (null? (cdr x))
#f
(let R ((y (cdr x)))
(cond ((null? y) #f)
((eq? x y) #t)
(else (R (cdr y)))))))

gosh> x
#0=(a b c . #0#)
gosh> (cycle? x)
#t


昨日も今日も定時で帰ってきたので、なんかハイだ。
ページ数かぞえ間違えた。まだ半分いってないや。ようやく3.3節に入ったところ。

実は、3章に入ってしばらく、いまいち面白くなかった。主な原因は、set! の使い方を覚えましょう的な雰囲気にあると思う。語弊のない言い方をすると、set! を通して Scheme を深く理解するきっかけをつかむ、っていうところかなあ。ここから先の解説は、関数型プログラミングというより、「コンピュータから見た(Scheme の)プログラム」なのかもしれない。ああ、そもそもそういう趣旨の書名だったか。

もしかしたら、関数型プログラミングにだけ興味がある人は、2章まで読めば十分なのかもしれない。

もちろん、まったく面白くないわけではない。当たり前だけど。ようするに自分が知らないことを気づかせてくれれば面白いので、たとえば exercise 3.16~3.17 は、リストの本当の姿、つまり cons セルとしての姿を実感させてくれて面白い。

リスト x = (a b c) には、いくつの cons セルがあるか。SICPでは、まずナイーブな方法が例示されている。
(define (count-pairs x)
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-pairs (cdr x))
1)))
(define x (list 'a 'b 'c))
gosh> (count-pairs x)
3

これの何が問題かっていうと、リスト内の cons セルを示している car や cdr がある場合に意図した答えにならないこと。
gosh> (set-car! x (cdr x))
gosh> (count-pairs x)
5

自分は、絵を書かないとわからなかった。最初、x はこうなっている。!は、下向き矢印↓のつもり。
x-->|*|*|-->|*|*|-->|*|/|
! ! !
|a| |b| |c|

たしかに、consセルが3つある。ここで、(set-car! x (cdr x)) すると、x のリスト表記は ((b c) b c) になる。絵で書くと、こう。
     ________
| !
x-->|*|*|-->|*|*|-->|*|/|
! !
|b| |c|

これを count-pairs で調べると、2個目の cons セルより後ろを2回数えてしまうので、実際の cons セルは 3 つのままなのに結果が5になる。ちなみに、消えた a については忘れていい。キーワードはガーベジコレクションらしい。

この問題を回避する count-pairs を書けというのが exercise 3.17。同じやつを2回数えなければいいだけなんだけど、「同じ」って何?という哲学的な命題と対峙しなければいけない。実際には哲学ではないので、eq? の本当の意味を理解しているかどうかがポイントになるんだと思う。僕の回答。
;; ex. 3.17
(define (count-pairs x)
(define aux-pairs '())
(define (member-eq? c ls)
(cond ((or (null? ls) (null? (cdr ls)) (null? c)) #f)
((eq? c (car ls)) #t)
(else
(member-eq? c (cdr ls)))))
(define (aux-count x)
(cond ((not (pair? x))
'())
((member-eq? x aux-pairs)
'())
(else
(set! aux-pairs (cons x aux-pairs))
(append (aux-count (car x))
(aux-count (cdr x))))))
(aux-count x)
(length aux-pairs))

gosh> (count-pairs x)
3

こんな小さな問題で、やたらにいろいろなことが示唆されているので、やめられない。
これなら、(set-cdr! (cddr x) x) みたいな循環しているリストに対しても、正しい答えが得られる。一見すると終わりがないものにケリをつける方法っていうのは、それだけで魔法みたいで、やっぱりやめられない。

ちなみにこんなことをわざわざ書いているのは、独習で不足しがちな「説明してみることで自分の理解度を確認する」という作業を補っているだけです。Webって本当に便利ですね。したがって、あらゆる記事は、きちんと理解した人間による解説ではありません。

2006/04/05

SICP がブームらしい。ということをsumiiさんの日記で確認した。職業がら、その手の「ブーム」には気づいてなきゃいけないんだけど、自分の中でなんとなくブームであることを否定していたような気がする。にしても、いつくらいからブームなんだろう。

自分は、5年くらい前に存在を知ってから「いつかきちんと読んでみたい」と断続的に思いつつだらだらと過ごし、いろいろあって去年の11月頃から一人でねちねちと問題を解き進めている。ちょうどそのころにクレジットの契約ができるようになって amazon で原書を注文することが物理的に可能になった、というのも大きい。

帆船模型を作るみたいに、ゆっくり、とにかく手を抜かずにやってるので、遅い。索引を除くページ数でようやく半分を越えた。内容を考えると、この先のほうが時間がかかるだろうから、今年中には読み終らないんじゃないかなあ。

ここまで来たので、べつにブームが来ようがブームが去ろうが、静かに勉強し続ける。

sumiiさんの一連の記事については、その影響で「ブーム」が去るような気がしないでもないし、何より「次」の教材を示してくれているので、大変うれしい。なんていうか、世の中には勉強することに飢えている非学生な人間が少なからずいると思うので、(信頼できそうな)研究者の方によるこの手の書き込みはとても助かります。

2006/04/04

土曜の朝から原因不明の発熱で寝込んでしまった。正確に言うと、土曜の昼前からか。その日の早朝は元気だった。土曜日はタワーレコードまで出かけて行ってCDを買うつもりだったのに。
ようやく熱がひいてきたので、土曜日にできなかったことを片付けることにした。とはいえ、あんまり食べてなくて体力がなく外出はできないので、Amazon なんだけど。
自分にとってタワーレコードは、新宿店がCDを漁るのに魅力的な場所なのであって、だからタワーレコードのWebで買物をしたいわけではない。

2006/03/31

ここのところ、職業倫理について考えることが多い。これまでは、考えるものではなく、事あるごとに意識するものだった。職業倫理にもとる行為は、しないのが当たり前。金もらってる以上、顧客やビジネスパートナー(いまどき、どっちも Buzz っぽい)の利益になる行動をしろ。信頼(これも Buzz っぽい)は裏切るな。自分の仕事に誇り(やっぱり Buzz っぽい)を持て。

なんか違うと感じるのは、職業倫理とされているものは、どれも「くいぶち」にあぶれないための知恵でしかないんじゃないかってとこだ。それはそれでいいし、必要だし、大切だと思うけど、はたらくってのは所詮「くいぶち」なのか? 「食ってけなかったらどうする。くいぶち上等」のようなコメントは受け付けません。「はたらく=くいぶち」は、極論すれば「第三次産業なんてやめちまえ」に聞こえるから。なんで書籍みたいなモノが僕の「くいぶち」に転化されるのさ。そのモノが誰かにとって「くいぶち」になるから。そんなことは19世紀の時点ですでに常識。いまは21世紀。もうちょっと進化してるはず。っていうか、進化しててくれないと困る。いまさら野麦峠は越えたくないって。

で、職業倫理について考えてみるわけだけど、考えれば考えるほど、これが実は危険思想なんじゃないかと思えてくる。もうちょっと限定すると、職業倫理ドリブンであることの危険性。

たとえば、「エンドユーザのため」という台詞は聞こえがいいけれど、それは「家族のため」とか「上司のため」とか「会社のため」とか「お国のため」っていうのと何が違うのか。いずれの場合も根っこにあるのは保身だし、くいぶちなんじゃないかと。批判はしない。ただ、「はたらく」の根っこにあるのが「○○のため」っていうのは、素朴に考えて寂しいだけじゃなく、いきつくところは野麦峠かもしれなくって鬱。

あるいは、本当に自分の仕事に誇りを持つべきなのか。仕事に関係なく、自分自身に誇りを持つべきじゃないの?

じゃあ、何を「はたらく」の背景に抱けばいいのか。まず信じていいのは、技術だと思う。どんな「はたらく」でも、技術がなければ役に立たない。もちろん、ここでいう技術はテクノロジーではない。「はたらく」によっては、無作為な人間とのコミュニケーション能力みたいなのが重要かもしれない。もうひとつはセンスだろう。本来は技術と切っても切り離せないものだけど。

となると、どうすれば技術とセンスを取得できるかが問題になる。暫定的な自分の答えは、「各自が好きなことを続ける」。好き→続ける→技術とセンスがついてくる、という進研ゼミのような理屈はわかりやすいと思う。それに、好きじゃないことを「仕事だから」って開き直って続けるのはやめようよ。それは不幸だから。

この答えは、実は何も言っていないに等しいし、好きじゃない仕事に就かざるを得ない人のことを考えていないことも十分に自覚している。あと、自分のことを棚に上げていることも自覚している。そもそも、好きなことっていうのがよくわかっていない。僕は、好きなことがなんだかずっとわからない(いまだにかなりあやふや)。だから、スポーツ選手とかミュージシャンのような、好きなことがわかりやすい「はたらく」がうらやましい。それが続けられるほど好きな人がうらやましい。僕の話はどうでもいいので終了。

2006/03/26

いまごろ気づいた面白いクイズ。

AA折れ線グラフ(nobsun)
http://oss.timedia.co.jp/index.fcgi/kahua-web/show/ossz/oneline/2006-03-14

あまり悩まないでさくっと書いてみることにした。各行で出力する切片たちを割り出して、上の行から順番に書いていくだけなので、Cでもいいじゃんと言われたらそれまでという悲しさ。自分としては、各行の割り出し方が数値的なところがお気に入りなんだけど。
そういえば K&R にも、垂直方向のヒストグラムを出力させる問題があった気がする。横のものを縦にするという点では近いものがある。ない?
;; aa-graf.scm

(define (aa-graf RFC)
(let ((aas (aas RFC))
(alts (altitudes RFC)))
(let draw ((alt (apply max alts)))
(graf-at alt alts aas)
(if (> alt (apply min alts))
(draw (- alt 1))))))

(define (graf-at alt alts aas)
(print (list->string
(map (lambda (x y)
(if (eq? x alt)
y
#\space))
alts aas))))

(define (aas RFC)
(map (lambda (x)
(cond ((eq? x #\R) #\/)
((eq? x #\F) #\\)
((eq? x #\C) #\_)))
(string->list RFC)))

(define (altitudes RFC)
(map (lambda (pair)
(min (car pair) (cdr pair)))
(let R
((s 0)
(ls (map (lambda (x)
(cond ((eq? x #\R) 1)
((eq? x #\F) -1)
((eq? x #\C) 0)))
(string->list RFC))))
(if (null? ls)
'()
(cons (cons s (+ s (car ls)))
(R (+ s (car ls)) (cdr ls)))))))
gosh> (aa-graf "RCRFCRFFCCRFFRRCRRCCFRFRFF")
__
/ \/\/\
_/\_/\ _/ / \__/\ /
\/

それじゃあほかの人たちのエレガントな解答を見てみよう、と思ったんだけど、いきなり shiro さんが何をやってるんだか分からない……。つーか、自分が比較的プリミティブな Scheme の機能しか知らないのがいけない。てなことを書くと、リファレンスマニュアルを調べろと怒られそうだけど、まだまだ調べるよりも作るほうが楽しいお年頃(関数年齢)。プログラミングを独習するには10年かかるからしょうがないか。

2006/03/23

木曜洋画劇場でスティーブン・セガールをやっているとつい最後まで真剣に見てしまう症候群

2006/03/22

ようやく第2章まですべての問題が終わった。
;; ex. 2.97
(define ex-2-97-p1 (make-poly-with-dence 'x '(1 1)))
(define ex-2-97-p2 (make-poly-with-dence 'x '(-1 0 0 1)))
(define ex-2-97-p3 (make-poly-with-dence 'x '(0 1)))
(define ex-2-97-p4 (make-poly-with-dence 'x '(-1 0 1)))
(define ex-2-97-rp1 (make-rational-number ex-2-97-p1 ex-2-97-p2))
(define ex-2-97-rp2 (make-rational-number ex-2-97-p3 ex-2-97-p4))
gosh> (print ex-2-97-rp1)
(x + x^0)/(x^3 + -x^0)
gosh> (print ex-2-97-rp2)
(x)/(x^2 + -x^0)
gosh> (print (add ex-2-97-rp1 ex-2-97-rp2))
(-x^3 + -2x^2 + -3x + -x^0)/(-x^4 + -x^3 + x + x^0)

Mathematica をはじめて使ったとき以来の感動。なにせ、SICP片手とはいえ自分でコードを書き、まがりなりにも中で何が起きているかを把握しているんだから。

それでもやっぱりはずかしい、ぐちゃぐちゃコードたち。主に自分のためのバックアップ用。いつの日かもう一度書き直す。

symbolic-algebra-generic.scm

arithmetic-tower.scm
install-sparse-polynomial.scm
install-dence-polynomial.scm
日帰りで別所温泉に行ってきた。長野新幹線で上田まで1時間半、そこから上田電鉄別所線に乗り継いで30分くらいで別所温泉駅に到着する。関東甲信越にはこの手のローカル線がけっこう残っていてうれしい。

DSC_0017 別所線。さすがにこの車両は走ってない

空がきれいなのは、朝の9時だからだけではないと思う。

DSC_0014 塩田の車窓から

別所温泉には立ち寄りの公衆浴湯が何箇所かある。今回は大湯と石湯に入った。いずれも150円で、大湯のほうが硫黄臭が強くて温泉っぽい。でもこれは、湯が浴槽に到達するまでの距離の違いだろう。いずれも、かけ流しではなく循環式。まあ、別に循環式でもいいんだよ、楽しめれば。

実際にこの土地にくるまで知らなかったんだけど、付近には鎌倉~室町のころの遺産が多い。とくに三重塔がやたらにある。日本史には疎いんだけど塔、それも三層のやつには興味があるので、なかでも正八角形が魅力的な安楽寺の三重塔と、未完成が魅力的な前山寺の三重塔に行ってみた。どちらの寺も、塔の周りはふつうの分譲墓地で、お彼岸のお参りに来ている人がちらほらいる。一般の墓地の真ん中に国宝があるのは不思議だと奥様にいったら、京都や東京と違ってこのあたりの寺を維持するには檀家からの収益が重要なんだろうとのこと。確かにそうかもしれない。

DSC_0049 安楽寺三重塔。四層に見えるのは1階に屋根が2枚あるから

散策には別所温泉駅で自転車を借りた。とくに前山寺は山の上にあって、ふつうの自転車(サドル低い)がつらい。寺の前で胡桃を売っていたおばちゃんが「どこから自転車?」と聞くので「別所温泉駅」と答えると、あきれられた。自転車が最強の東京だと 3 km くらいどうってことないけど、自家用車中心の山間ではアホに見えるんだろう。

DSC_0055 前山寺は右手の山の上

別所温泉駅で自転車を借りる人の大半は、この自転車でどこにいくんだろう。

2006/03/18

姉さんとしたう人の一人が、今日、これまで彼女が属した共同体を離れて別な仕事を初めるというので、お祝いをした。おめでとうございます。あなたは、ほかで仕事をしたほうがいいと思うよ。これからも遊んでね。


じっと手を見る。

2006/03/11

ひさしぶりに電車にのったら、どっかの塾の広告が「日本の小学生は 9×9 までだけどインドの小学生は 19×19」っていってる。これは趣旨としては「もっとつめこもうぜ」であり、「詰め込みのお手伝いをさせていただくので費用をたっぷりお支払いください」だよなあ。ゆとり教育の見直しバンザイ……なわけないだろ

実は 19×19 の暗算は、思うほどむつかしくない。中学生以上なら、こんなふうにできる。
19×19 = (20 - 1)2 
= 202 - 2×20×1 + 12

ようするに、19 を 20-1 だと思って、誰もが中学校で詰め込まれたはずの文字式の展開をしただけ。
(a - b)2 = a2 - 2ab + b2

202 - 2×20×1 + 12 には、九九の延長で対応できる掛け算しかなくて、結局 400-39 になる。19×19 を脳内筆算するのはしんどいけど、400-39 なら暗算できる範囲だと思う。400-39 にもっていくまでに多少の脳内レジスタは必要だけど、常態でそれくらいのレジスタを確保できない脳みそが 19×19 を暗記するのはどのみち無理ですから。

つまり2桁の掛け算を九九のように暗記させる必要はない。むしろ積極的に暗記させないほうがいいかもしれない。どうせ詰め込むなら、文字式の展開とか因数分解の公式を小学校で詰め込んで、こんなふうに応用できることを教えてやってください。「あげるなら魚じゃなく釣竿」ってやつね。

2006/03/07

いま質問されて思い出したけど、dvipdfmx では pstricks が使えない。っていうか、\special 環境が使えない。

Debianにotfパッケージをインストールして、dvipdfmxでOpenTypeフォントを埋め込んだPDFを吐き出すまで


前提1


dvipdfmxのインストールまでは、以下のページで完了しているものとする。

* Debian GNU/Linux (Sarge)でのLaTeX導入備忘録
http://cise.edu.mie-u.ac.jp/~okumura/texwiki/?Linux%2FDebian

ちなみに、debianでLaTeXを使うときに参考にできるURLは、事実上ここしかないと思う(ここ以外の情報は、申し訳ないけど情報が断片的すぎて使えなかった)。

前提2


仮想フォントは使わない。つまり、あくまでも正規のOpenTypeフォントのライセンスを持っている人が、それを使ってDebianでフォント埋め込みのPDFを生成することを目標とする。つまり、ぶっちゃけほとんどの人には役に立たない情報。

otfパッケージのインストール


まず、otfパッケージを導入する。以下からotfパッケージのソースを取得して、添付されているシェルスクリプト(makeotf)でインストールする。

* Open Type Font用VF
http://psitau.at.infoseek.co.jp/otf.html

なお、ソースのreadmeには「古いバージョンのovp2ovfやopl2ofmが必要」とあるが、仮想フォントを使わないのであれば不要。ここでも、あえて古いバージョンを探して入れるようなことは(パッケージ管理上も問題があると思うので)しない。シェルスクリプトそのものは、標準でplatexを入れた場合にインストールされるバージョンでも支障はないっぽい。

OpenTypeフォントのインストール


/usr/share/texmf/fonts/otf および/usr/share/texmf/dvipdfm/Fonts みたいなディレクトリを作って、そこにフォントデータをおく。その後、update-texmf(またはmktexlsr)を実行する。

この時点で、プリアンブルで\usepackage{otf}としたLaTeXのソースはコンパイルできるはず。しかし、dvipdfmxを実行しても、
[1kpathsea: Running mktexpk --mfmode ……
みたいなエラーメッセージが出るはず。

dvipdfmxでOpenTypeフォントを使えるようにする


/etc/texmf/dvipdfm/dvipdfmx.cfg の最後の行に、
f hiraginox.map
などと書く。hiraginox.mapのところは、使用するOpenTypeフォントに合わせて、kozukax.mapやmorisawax.mapを指定する。
なお、hiraginox.map などの本体は /usr/share/texmf/dvipdfm/config にある(otfパッケージを入れる際にインストールされる)。TeXの常識だとこのディレクトリに dvipdfmx.cfg を置くみたい(実際ここにも dvipdfmx.cfg があったりするかもしれないけれど)。でも Debian では、/etc/texmf/dvipdfm/dvipdfmx.cfg を上記のように修正しないと、kpathsea に文句を言われ続ける。

使う


プリアンブルに\usepackage{otf}を指定して platex と dvipdfmx を実行すればいい。
自分はプリアンブルにこんな感じに指定して使っている。
\usepackage{type1cm}
\usepackage[T1]{fontenc}
\usepackage[scaled]{helvet}
\usepackage[deluxe]{otf}
\renewcommand{\rmdefault}{ppl}
\renewcommand{\sfdefault}{phv}
\renewcommand{\ttdefault}{pcr}

[おまけ]PDF/X-1にする


印刷所に安心して入稿するには、PDF/X-1 で検証された PDF にすべき。でも、これは Adobe 社謹製の何かを使わないと実現できないんじゃないかしら。つまり、Debian だけではできなくて、Adobe Acrobat Professional 7.0 がインストールされた Win/Mac マシンで行わなければいけないと思われる。

基本的には、まず上記の方法で生成した PDF ファイルを Adobe Acrobat Professional 7.0 で開き、PS で保存し直す。それから、その PS ファイルを Adobe Acrobat Distiller 7.0 で PDF/X-1 に変換すればよい。

ただし、上述した方法で OpenType フォントを埋め込んだ PDF を生成しても、その PDF ファイルには欧文基本14書体は埋め込まれていない。したがって、Win/Mac マシンに英文のフォントがそれなりに入っていないと、Distiller による PDF/X-1 への変換でこける可能性がある(Helvetica みたいな Win マシンには標準で入っていない欧文書体は特に注意が必要な気がする)。
数式も注意。cmやamstexなどのTeXにしかないフォントを dvipdfmx がどう扱っているのか知らない。PDF に埋め込まれていたり、ビットマップに変換したりしているのでない限り、要注意かも。自分の環境では、Win マシンにもごっそり TeX の数式フォントを入れていることもあって(Illustrator とかで使いたいし)、それがなかった場合にどうなるかは検証していない。

2006/03/05

* なんで確率は 0 以上 1 以下 なのさ
http://yowaken.dip.jp/tdiary/20060305.html#p02

という疑問に呼応した次の話が面白い。

* 確率分布間の「距離」
http://wiki.fdiary.net/lacs/?Pr.App

たしかに、確率0.1と0.2の違いは、感覚的に0.4と0.5の違いとは違う。それをこんなふうに定式化して説明してもらえると、思わず「ぎゅう」ってなっちゃう。

ところで、この話は「確率が 0 以上」であるひとつの解釈にはなるけれど、「確率は 1 以下」の解釈にもなっているのだろうか(読みとれないのは自分の力量不足……)。実は、ょゎさんの疑問を見たとき「測度なんだから 0 以上じゃないと上手くねぇ」と脊髄反射してしまいそうになって、「でも測度なら [0, ∞] でも(のほうが)いいんじゃね?」と自問自答。それだと気持ち悪いくらいしか反論がない。

もしかして、ょゎさんの気持ちは [0, 1)∪{∞} か?
# じょうだんですって
こういう働きをするフィルタがほしい。
((a 1) (b 2) (a 3) (c 1) (d 0) (a 4) (c 5) (b 6))

((a 8) (b 8) (c 6) (d 0))

ようするに、同じタグを持つ要素をまとめ(この場合は加算)させたい。その心は、タグ=>多項式の各項の変数、要素=>多項式の各項の係数とみなして、リスト全体で多項式を定義すれば、こういうフィルタを使って多変数多項式の代数演算システムが構築できるはず。つまり、SICPのex.2.92が解けるはず。
(define (M-filter equ? proc ls)
(let ((L (merge-if-equ-with-car equ? proc ls)))
(if (null? (cdr L))
(merge-if-equ-with-car equ? proc L)
(cons (car L)
(M-filter equ? proc (cdr L))))))

(define (merge-if-equ-with-car equ? proc ls)
(letrec
((head (car ls))
(foot
(let M ((a (car ls))
(rest (cdr ls)))
(cond ((null? rest)
'())
((equ? a (car rest))
(let ((a (proc a (car rest))))
(set! head a)
(M a (cdr rest))))
(else
(cons (car rest)
(M a (cdr rest))))))))
(cons head foot)))
うまくないなあ。つーか、このex.2.92の時点で set! を使うのは、問題の回答としていけないような気もする。でも、"The Seasoned Schemer" の 16th Commmandment(set!はletで束縛している名前にだけは使ってもいい)は破ってないので、よしとすることにしよう。ちっとも先に進まないし。
(M-filter (lambda (a b) (equal? (car a) (car b)))
(lambda (a b) (list (car a) (+ (cadr a) (cadr b))))
'((x 1) (xy 2) (x 3) (y 4) (z 5) (y 6) (xy 5)))

=>((x 4) (xy 7) (y 10) (z 5))

このリストをもってして 4x + 7xy + 10y + 5z を定義するということ。多項式どうしの加算は append して M-filter、積は car 同士および cadr 同士かけたものを combination して M-filter すればいいっしょ。あとは、x^2*y と y*x^2 とかがちゃんと #t になるように変数と equ? を定義して、それらをまとめて代数演算パッケージと連係するようにパッケージ化するだけ。


やあ、ようやく佐川急便が来たよ。これで会社にいける。
佐川のWebにある「お荷物問い合わせ24時間サービス」では、今ごろ大阪から札幌に転送されていることになっている荷物なんだけど、なんとか無事に届いたね。

2006/03/03

こんなことをしている場合ではないという瞬間がたくさんある。時間をへたくそに浪費しているとき、不本意な問題で労役を強いられているとき、自分は思っているほどにさえ何もできないと感じたとき、突き進んでいる人を見るとき、それから、毎日の16:00。
べつやくれいと小島麻由美の親近性について
  • かわいらしい(重要)
  • シュール
  • 異次元(超重要)


上の2つだけだと、ただの奈良美智どまり

2006/03/01

ただの備忘録。

debian では、crontab ではなく、cron.*(cron.daily/, cron.hourly/, cron.monthly/, cron.weekly/)以下に定期的に実行したいコマンドをおいておく。ただし、コマンドの名前にドット(.)が含まれているものは実行されない。これで何日分のログが反映されていなかったことか……

2006/02/28

ひきつづき職場のPCの入れ替え。

弊社では、クライアントマシンのIPアドレスはDHCPで割り当てられている。この更新の頻度がけっこう高く、今日はちょっと固定的に同じ番号を使用したかったものだから、適当に空いてそうな番号を設定してみるという暴挙を犯してしまった。Windowsでは、固定的に割り当てようとしたIPアドレスがネットワークで既に使用されていると、強引に設定はされるものの警告が出る。おそらく、設定しようとしているIPアドレスを求めるARP要求をブロードキャストして応答を確かめているんだろうけど、とにかく警告が出ないアドレスは空いてるってことでしょ? ←これが思い込みであることがすぐに判明する

何がおこったかというと、最終的に警告なく設定できたIPアドレスが、Linux か何かが動いているマシンの固定IPアドレスと見事にバッティングして、相手をハングさせてしまった(おそらくNICが停止してリモートアクセスできなくなった)。管理者の S さん、ごめんなさい。本当にごめんなさい。

今回、相手のマシンは NIC の停止にいたったぽいけれど、ネットワーク上に同じ IP アドレスのマシンが存在してしまった場合(i.e. 自分のIPと同じ送信元IPを持つARPパケットを受信した場合)の挙動は、実装に依存して多種多用らしい。なかには気にせず通信を続ける実装もあるらしく、そんな場合は一部のパケットが別のマシンに取られてしまうことになる。ある種の実装で重複する IP アドレスを検出した場合に NIC を停止するのは、そのようなセキュリティ上のリスクを避けるためなのかもしれない。でも、ルータのような装置が NIC 停止型の実装だったら、かえって容易にサービス停止攻撃をまねきかねないよな。

こういう知識については、あまり知る機会がなかったりする。そんな話題にもしっかり触れている『基礎からわかるTCP/IP ネットワーク実験プログラミング第2版』は 2400 円。ぜひ ;)

『基礎からわかるTCP/IP ネットワーク実験プログラミング第2版』
http://ssl.ohmsha.co.jp/cgi-bin/menu.cgi?ISBN=4-274-06584-7
会社のPCのリース替え。自分のだけなら楽なんだけど。

Windows XP なので、なにはなくても Cygwin をいれなければならない。Cygwin のターミナルはぶっちゃけWindowsのコマンドプロンプトでいらいらするので、Cygterm を入れてターミナルを変えなければならない。ところが、Cygwin をフルインストールしたせいか、Cygterm のコンパイル中に、gccで「_WinMainCRTStartup が multiple definition」とかいうエラーが出てとまってしまう。
とりあえず、Makefile に gcc に与えるオプションとして「-Xlinker --allow-multiple-definition」を追加すればコンパイルできることはわかったものの、これでよかったのだろうか? これはつまり、せっかく新しい PC なのでできるだけ全てのインストールをクリーンに行いたいという病気です。

2006/02/22

Amazonで先行予約開始になったもよう。書店でも、はやいところは25日には並びます。

Amazon.co.jp『RailsによるアジャイルWebアプリケーション開発』
http://www.amazon.co.jp/exec/obidos/ASIN/4274066401/

前田さんのサポートページ
http://awdwr.shugo.net/

出版社のページ
http://ssl.ohmsha.co.jp/cgi-bin/menu.cgi?ISBN=4-274-06640-1

Amazonの先行予約は、Amazonの担当者の裁量で選ばれるもの以外は、出版社がペイを支払うことで実現するらしい。本書がどちらのケースなのか、残念ながら担当者レベルでは真相を知る由もないけれど、ずいぶんいろいろな人をお待たせしてしまった本だと思うので、少しでも早く予約可能になってくれてとてもうれしい。

ところで、巻末にある監訳者の前田さんの略歴には唐突に Scheme という文字が出てくる。あくまでも僕の主観では、これは「とても光栄な皮肉」だ。それはたぶん、この本の制作方法に関係している(あとで全体のフローをきちんとまとめること)。少しでも発行を早めるには、ほかに手段がなかったと信じているのですが、関係したみなさんには多大なるご迷惑をおかけしました。

2006/02/21

何かを観察したいっていうのは、人間の根源的な欲求のひとつだ。観察することにより周囲の危険や将来の出来事を想定したいとか、観察には科学の萌芽があるとか、そういう小理屈はぶっちゃけどうでもいい。ただ単純に、それをもっと見つづけたいという素朴な欲求があると思う。なんていうか、好奇心という概念だけではすませられない、観察という欲望。

たとえば交通事故の現場に遭遇したら、どうしたって見てしまう。不謹慎でも興味本位でもなく、ただ「何がどうなっているのか」を観察せずにはいられない。それだけ。事故や事件の現場はあえて避けて通るという人でも、映画のロケなんかやってたら、(別に興味なくても)とりあえず何を撮っているのか気になって、ちら見程度はすると思う。あと、おとこのこだったら、スーパーカーとか、通りの向こうを走り去っただけで凝視しない? それはもう、興味があるからとか、好きだからとか、カッコいいからとかじゃなく、とにかく見たい。もっとよく見たい。

女の子のスカートが短いのとかも、そのような事例に含まれると思う。いちいちセクシャルな感情を強く抱く男性もいるとは思うけど、そんなのは基地外であって、もっと単純に、ただ状態を観察したいというのが、個人的には本音だったりする。ほとんど交通事故とかと同じ。いちいち性的感情を抱くほど頭のなかはヒマじゃない。異性の下着=性的感情という短絡なイメージは放棄してもらいたい。小学生じゃないんだから。

というはなしを会社でしていたら、おまえは逮捕されてしまえと嘲笑された。だれに嘲笑されたとは、あえていわない。

2006/02/19

『亡命ロシア料理』のローストビーフがうまそうでたまらない。やつらは壺の中にタマネギのみじん切りを敷き詰め、そこに牛肉のブロックを置き、白ワインだけ加えてオーブンにかけるらしい。塩も加えない。タマネギと牛肉のエキスだけをスープにして、あとは壺に任せる。これで極上のローストビーフができるんだって。

このレシピは、化学的にも正しいと思う。NaClのような、分子が小さいわりに味覚を左右する調味料を最初から使ってしまうと、肉汁がしみ込む余地がタマネギの細胞になくなるし、タマネギの甘みがしみ込む余地も牛肉の細胞からなくなってしまう。アミノ酸の分子は塩に比べると巨大だから。

ただ、どうにも我が家には壺が見あたらないので、この知見を応用してスープを作ってみることにした。無水鍋(商標はピタクラフト?)でオリーブ油を加熱し、大量のタマネギのみじん切りを胡椒で軽く炒め、その上に豚コマを設置し、白ワインを半カップほど注いで弱火にかける。5分くらいで十分に水分が出てくる。さらに白菜を投入して、白菜がイメージどおりにしなしなしてきたら、ひたひた程度に水を加える。最後に塩とかサドンデスソースで味付けするだけ。これはうまい。

ここまでは数週間前の話。

今日は、このスープで得た知見を、さらにカレーに応用してみた。カレーを作るとなると下ごしらえに2~3時間くらいかかっちゃうわけで、2~3時間というのは、仮にそれくらいの物理的な時間ができても、実際に不慣れな行動を起こすのはおっくうだったりする。そこで亡命ロシア流タマネギの下ごしらえですよ。

まずはスターターオイルを作る。いずれもホールのクローブ、クミン、カルダモン、黒白胡椒を砕いて、火にかけた油に投入する。焦がさないように注意。油に香りが移ったと信じたら、タマネギのみじん切りと生姜を炒め始める。今回は亡命ロシア流なので、タマネギから出る水気をとばさずに、白ワインだけ加えてタマネギ自身の水分で煮込んでいく。そのため保温鍋(商標はシャトルシェフ)を使う。全体に火が通ったら、頃合いを見てマンゴチャツネやヨーグルトも投入する。チャツネとか、いままでは最終工程に近いとこで入れてたんだけど、分子の大きさを考慮すればこのタイミングだよな。あとは保温鍋にまかせて1時間くらい本でも読んでれば、下ごしらえは終了。

タマネギの下ごしらえができたら、別に茹でておいた豚バラ肉をスープごと加えて、具を煮込む。今日はジャガイモとにんじんとトマト。具材の煮込みも保温鍋に任せる(壺はもってないし)。やっぱり1時間くらいしたら、コリアンダー、ターメリック、ガラムマサラを加え(今日の比率は2:1:3)、最後に塩で味を整えて完成。

curry

従来よりも味のまとまりがいいような気がする。そういえば、にんにく忘れてた。

2006/02/14

先日、『ホテル・ルワンダ』を見に行った。日本公開のためのWeb署名をしたような記憶もあったので。

この映画はとてもいい作品なので、見に行ける人は見に行ってください。
ぶっちゃけ、いまこの映画を見に行っている人は、80%はルワンダやコンゴで何があったか(ニュースとして)知っている人だと思うし、そのうちのさらに80%は、この映画で語られている時間のに何があったかも(ニュースとして)知っていると思う(フツでもツチでもない、コンゴのカビラ政権のことね)。でも、そういう人たちが見に行っているだけでは、いつまでたっても政治臭い作品だとみなされてしまう。まあ、旧社会党系と共産党系の皆さんにもぜひ見てもらって、生ぬるい頭をかちわってきてもらいたい部分もあるけど、この映画は、ただの政治ツールにしてはいけない。だから、やっぱり自衛隊反対とかいってる人は見に行かなくていいです。かといって右翼も見に行かなくていいです。普通の人に見に行ってほしい。すべからく社会人は、ポールのかっこよさを見て、ああ、ぼくは明日も同じような仕事の繰り返しかもだけど、プロとして、会社とか目の前の客とか中途半端な職業倫理なんかじゃなく、ほんとうの品格をむねに自分の仕事に向き合わなくちゃだめだなあと痛感してほしい。実践するのはむずかしくても。

自分の今の仕事が、自分が何かをつっぱってまで品格を保ちたいと思えるものかどうかは大部分の人にとっては問題で、そのジレンマが、高々「お金をもらっている以上……」とか、「人に出して恥ずかしくない……」とか、そういう職業倫理に落ち着いちゃうんだよなあと、自省を含めて感じる。

レイトショー中心だったりするから、かえって大人は見に行きやすいと思う。

2006/02/11

そういえばきのう T さんに、「ブログにプログラムとか書いてるのは正直どうかと思う」と言い放たれたのを思い出した。かわいらしいくせに言うことはこにくたらしい。
小学校の教員的には順序を付けるのは NG なんだろうけど、数学、とくに集合論では、ある関係によって順序を定義し、集合の要素を整列させたい。ある順序集合が整列できるかどうかは、少なくとも「なんとか伝票のどこに印鑑を押すか」よりは重要な問題なのである。だから邪魔しないでください。

あるリストを集合だと思って、その要素 a と b の関係「a > b」を、「a が b よりもリストの左側にあること」だと考えることにする。そのリストの部分集合(必ずしも降順に並んでいない)を、重複する要素を除外して降順に並べるには、どうやるのが効率的か。つまり、
(a b c d e f g h i j)
というリストがあって、その部分集合、たとえば
(f c a c)
を、もとの親玉のリストと同じ順番
(a c f)
にしたいというはなし。

たぶん、これは、ソートの問題の一般化なんだと思う。順序を定義しているリストをランダムアクセス可能なデータ構造に対応させて、関係「>」を自然数における「>」と同一視してクイックソートかなんかしちゃうのがひとつの解法なのかもしれない。つーか、Cとかで配列を使うなら、自然にそういう方法を選択していることになる。

リストのまま整列させるにはどうするんだろうなあと一時間考えて、ようやくたどり着いた貧相な解法は、ビンソートっぽい方法。
(define (be-same-order ls1 ls2)
(if (or (null? ls1) (null? ls2))
'()
(let C ((ls ls1)
(passed '()))
(cond ((null? ls)
(be-same-order ls1 (cdr ls2)))
((equal? (car ls) (car ls2))
(cons (car ls)
(be-same-order (append passed (cdr ls)) (cdr ls2))))
(else
(C (cdr ls) (cons (car ls) passed)))))))

なんでこんなことを考えているかというと、SICP の ex. 2.84 のせい。2種類の型のどっちがどっちを包含してるか判定する方法を示せっていうんだけど(たとえば有理数と実数はどっちがどっちに含まれるか?)、意味を分離させて機械的に判定するには、あらかじめ数値型のタワー構造を表すリスト (complex real rational integer) を用意しておいて、上の be-same-order みたいなので (type-of x) と (type-of y) を整列させてから car をとればいいじゃん。いいじゃん、とか安直に思って、肝心の be-same-order みたいなのがすぐに用意できない、このどうしようもなさ。

2006/02/05

僕みたいな環境で Scheme を使うのは、子どもがクローゼットにしまってあるショットガンを見つけて弄んでいるのと同じなのかもしれない。
昼過ぎに柏から友人Kがくる。仕事を中断して奥様が作ったドライカレーを3人で食べて、しばらくたあいない(たわいない?)話をしてた。たぶんこれはとても贅沢なことなんだと思う。

2006/02/01

今日の LaTeX おばかちゃん。
  • どうグルーピングしても\kernにマイナスの値を指定できない場合がある。マイナスの値を指定した\kernを定義に使っているコマンドに対して、さらに \kern-1zw とかできないっぽいという挙動が、なんとなく見受けられる
  • \item[]の中に"["と"]"を入れるには、[]の中全部を{}でグルーピングする。エスケープしても無駄

ようするに、定義域がはっきりしなさすぎってことか。
むかし、ねえさんと文通してた KM が、2/14 にいよいよ結婚するらしいよ。という電話をさっきうけた。
正規表現に慣れすぎていて、構文解析とかやったことない。しかし避けても通れそうにないので、「10分で書ける、お手軽パーザー」というサイトを参考に四則演算のパーズに挑戦してみることにした。目標は1時間(つまり昼休み)。

10分で書ける、お手軽パーザー
http://fxp.hp.infoseek.co.jp/arti/parser.html

ところで、このサイトの構文定義っておかしくない? 左から解析することを前提にすると、オペレータの右側は必ず Expr のはず。
Expr = Term { (+|-) Term }
Term = Fact { (*|/) Fact }
Fact = ( Expr ) | number

Expr = Term { (+|-) Expr }
Term = Fact { (*|/) Expr }
Fact = ( Expr ) | number


とにかくやってみる。計算の優先順位を示すカッコのパーズは省略。
;; 10minutes-parser.scm
;; 2006/2/1
;; k16.shikano
;; inspired from http://fxp.hp.infoseek.co.jp/arti/parser.html

; Expr = Term { (+|-) Expr }
; Term = Fact { (*|/) Expr }
; Fact = Expr | number

(define left car)
(define op cadr)
(define right caddr)

(define-syntax or-match
(syntax-rules ()
((_ e0 e1 e2 ...)
(call-with-values
(lambda ()
(let R ((ls e0))
(cond ((null? ls)
(values '() '() '()))
((or (char=? (car ls) e1)
(char=? (car ls) e2)
...)
(values '() (car ls) (cdr ls)))
(else
(call-with-values
(lambda () (R (cdr ls)))
(lambda (l o r) (values (cons (car ls) l) o r)))))))
list))))

(define (expr ls)
(let ((parsed (or-match ls #\+ #\-)))
(let ((l (left parsed))
(r (right parsed))
(o (op parsed)))
(cond ((null? o)
(term l))
((char=? o #\+)
(+ (term l) (expr r)))
((char=? o #\-)
(- (term l) (expr r)))
(else
(error "unrecognize expr"))))))

(define (term ls)
(let ((parsed (or-match ls #\* #\/)))
(let ((l (left parsed))
(r (right parsed))
(o (op parsed)))
(cond ((null? o)
(fact l))
((char=? o #\*)
(* (fact l) (expr r)))
((char=? o #\/)
(/ (fact l) (expr r)))
(else
(error "unrecognize term"))))))

(define (fact ls)
(define (list->integer ls)
(let F ((ls ls) (added 0))
(let ((car-int (digit->integer (car ls))))
(if (null? (cdr ls))
(+ car-int added)
(F (cdr ls) (* (+ car-int added) 10))))))
(if (not (char-numeric? (car ls)))
(error "unrecognize fact")
(list->integer ls)))

実行結果

gosh> (expr (string->list "1+3*2"))
7


なんとか昼休みで終わらせたので、不都合があるかもしれない。
もちろん、今日の昼休みだけで全部作れたわけはなく、昨晩から or-match の部分は考えていた。この方法を Scheme で書くには、こんなふうに多値を使わざるをえないように思える(この使い方が洗練されているかどうかはおいといて)。それが面倒。

2006/01/30

SICP を Gauche (R5RS な実装全般?) で勉強していると、真偽の扱いで戸惑うことがある。というのも、SICP では '() を偽としてサンプルが記述されている場合があるけど、ディヴィグ本によればこれは R4RS 流らしい。いや、それともこれは、僕の putプロシージャと getプロシージャの定義の仕方がまずかったから?

そしてもうこんな時間……
「ところで」禁止

2006/01/29

このところのエウレカセブンは、クライマックスにむけてキャラクターの描き方に迷っているように思える。50週もあると脚本が行き届かないのかもしれない。

ところで、自宅で仕事のあいまに、マンションギャラリーというものに行ってみた。マンションを購入するつもりはほとんどないんだけど、近所のあれだけの好立地に建設予定となると本気で考えたくもなる。行ってみると意外に面白いもので、現代的なマンションも悪くないような気持ちになってきた。なんてったってラクだよ。こっちがすることといったらオプションを選ぶくらいなもので、それで展望やセキュリティや空調の完備された心地よい住処が手にはいるんだもん。
で、応募した。最終的には19倍の抽選になったらしく、案の定落選した。こんなものでも、やっぱり落選ってしょんぼりするものだった。

2006/01/27

ううん、昨日の regexp-replace-delimiter-all* では後方参照が使えないな。gaucheのregexp.scmを見ると、regexp-replace-all が最後まで \1 などをサブマッチした文字列に置き換えないらしく、それが原因っぽい。例えば #/foo(.*?)bar/ という正規表現オブジェクトは、内部では ("for" 1 "bar") というリストになっていて、それが regexp-replace-rec という regexp-replace-系プロシージャの請負プロシージャで rxmatch-substring により展開される。昨日の状態で置き換える文字列 に \1 などが指定されていても、最初にデリミタで分解されてしまうので、最終的に取り出される文字列は "1" とかになっちゃう。
置換文字列としてはマッチオブジェクトを引数にしたプロシージャが渡せるので、デリミタを施す処理を外でやるようにすれば対処はできるんだけど……

(define delimiter "%%%")
(define (set-delimiter str)
(string-append
delimiter
(string-join (map x->string (string->list str)) delimiter)
delimiter))
(define (erace-delimiter str)
(regexp-replace-all (string->regexp delimiter) str ""))

(define (regexp-replace-delimiter-all* str . args)
(if (eq? (remainder (length args) 2) 1)
(error
"Need even args -- REGEXP-MATCH-REPLACE*"
args)
(if (null? args)
(erace-delimiter str)
(apply regexp-replace-delimiter-all*
(append
(list (regexp-replace-all
(car args)
str
(cadr args)))
(cddr args))))))

(regexp-replace-delimiter-all* "foobarbaz..."
#/foo(.*?)baz/
(lambda (m) (set-delimiter
(string-append
"|"
(rxmatch-substring m 1)
"|"
)))
#/bar/
(lambda (m) (set-delimiter "..."))
)
 
=> "|bar|..."

なんかだんだん意図がわからなくなってきたけど、ようは regexp-replace-all* だとこうなっちゃうのをなんとかしたいということです。

(regexp-replace-all* "foobarbaz..."
#/foo(.*?)baz/
(lambda (m) (string-append
"|"
(rxmatch-substring m 1)
"|"
))
#/bar/
(lambda (m) "...")
)

=> "|...|..."

2006/01/26

なんらかの構文をルールにしたがってマークアップしたいときに、どんなテキストフィルタを使えばいいかという話。ことに、直前のフィルタリングの結果を次のフィルタリングで上書きされないようにする方法の正解はなんだろう。最初にきちんと構文を解析するのが、もっとも間違いがなく、robustであるのは認める。けれども、よほど汎用性のあるフォーマットでないかぎり自分で構文解析までするのは非現実的だと思う。誰かが、ちょうど自分の目的にあったものを書いてくれていれば、積極的に流用したい。けど、その目的のものを探すコストがばかにならないことも多い。
それなら、「アドホックさ」と「再利用性」のあいだのどこかでバランスをとった解決方法を、その場で作り出すしかないんじゃないかと思う。

「直前のフィルタリングの結果を次のフィルタリングで上書きされないようにする方法」に関しては、とりあえずこんな方法しか思いつかないんですが、この程度の再利用性があればいいんじゃない? だめ?
(define (regexp-replace-delimiter-all* str . args)
(define delimiter "%%%")
(define (set-delimiter str)
(string-append
delimiter
(string-join (map x->string (string->list str)) delimiter)
delimiter))
(define (erace-delimiter str)
(regexp-replace-all (string->regexp delimiter) str ""))

(if (eq? (remainder (length args) 2) 1)
(error
"Need even args -- REGEXP-MATCH-REPLACE*"
args)
(if (null? args)
(erace-delimiter str)
(apply regexp-replace-delimiter-all*
(append
(list (regexp-replace-all
(car args)
str
(set-delimiter (cadr args))))
(cddr args))))))


gosh> (regexp-replace-delimiter-all* "abcde" #/a.*?c/ "xx" #/xd/ "zz")
=>"xxde"

2006/01/23

Joel on Software、amazon で8000円とかのユーズド商品が出ちゃってますが、今週中には緊急に重版してる分ができるはずなのでもう少しだけお待ちください。早まって高値で購入してしまう人がいなければいいけれど。

ところで amazon の在庫情報は、US 本社のデータベースと取り次ぎのデータベースと出版者のデータベースとを連携させているらしい。例えば、市川の amazon.co.jp の倉庫に在庫がなくても、出版社や取り次ぎの倉庫に在庫があれば、「通常は3〜4日以内にお届けします」と表示される。で、どうもこの連携がうまくないときがあって、出版社に在庫があっても「在庫切れです」と表示され続けたりすることがある。Joel on Software に関しては、出版社にも現時点では在庫がないんだけど……(今週できますって)

amazon の在庫表示がゼロになることがどんなに全体での売れ行きに影響するかを考えると、もっと特別に対応すべきなのかもしれないけど、個々の書店への販売担当部署の対応にはリソースの限界があるし(そういうことが重視されていない場合は、とくに)、もっと上のほうの人達との認識の差もあるんだろう。企画編集者としても、いったん商品として本が出荷された状態になっちゃうと、「〜らしい」というレベルの情報しか得にくい。なにせ、重版の出来本が自動的に手もとにやってくることさえないんだから。重版が出来たころに出庫伝票を書いて(しかも手書き)自分で倉庫に赴くしかなく、それってなかなか不便なんだよね。

2006/01/21

うちの目の前はずっと廃屋で、夏になると野バラが咲き、秋になると紅葉し、春になると猫がひなたぼっこをしていた。5年前にこの部屋を借りたときの動機のひとつだったと思う。

DSC_0052 DSC_0072

それがいよいよ取り壊されてしまった。

DSC_0138

さて何かできるのか。何ができても、もうあの神社の裏に忘れられていた空間は戻ってこないんだけど。

2006/01/18

ささやかながら株式投資をしている身としては、もう少し身構える必要があるだろうか? でも、なんか投機スジが騒いでいるだけな気がしてしょうがないので、いままでどおりのスタンスをしばらくは維持しよう。実際、自分が所有している銘柄の評価額はさほど落ちてないし。

にしても、これってUSで数年前に起こったエンロンショックの再演でしょ。いったいこの国は、いつになったらUSの失敗から学習するんだろう。どうでもいいけど、当時エンロンと WorldCom がつぶれたことは、自分が転職をしたことの遠因(といっても直接の利害関係もないわけじゃない)になっていると思っている。

2006/01/16

いっけん仕事で使うスクリプトのように見えてただの道楽シリーズ。けっして就業時間中は遊んでません。つーか、ほんとに日中は(時間的精神的な)余裕なさすぎで、あちらこちらに迷惑をかけている。この場を借りてお詫びします。

で、今日の成果は、なんとなく階層化された文章の見出しに連番をふる gauche スクリプト。
numbering.scm

おなじことをするプログラムは、むかしはPerlで書いて、それからRubyでも書いたけど、いずれも上位レベルの見出しの現在値を保持するのに大域変数を使っていた。大域変数を使わないようにするには継続を使うんだろうなあと知ってはいたけれど、"Seasoned Schemer" を読んで、ようやく何をどうやって継続すればいいのかイメージがわいた。ここでは、現在までのレベルの見出し情報を次の行の処理に継続させている。

(define (numbering file)
(with-input-from-file file
(lambda ()
(letrec
((Ns (lambda (ctrs)
(let ((line (read-line)))
(cond
((eof-object? line)
'())
((leveled? line)
(let ((ctr-lv (length ctrs))
(lin-lv (level-of line)))
(cond
((< ctr-lv lin-lv)
(let ((ctrs (deeper ctrs)))
(cons (put-head line ctrs)
(Ns ctrs))))
((> ctr-lv lin-lv)
(let ((ctrs (add1 (chop ctrs lin-lv))))
(cons (put-head line ctrs)
(Ns ctrs))))
((equal? ctr-lv lin-lv)
(let ((ctrs (add1 ctrs)))
(cons (put-head line ctrs)
(Ns ctrs)))))))
(else (cons line
(Ns ctrs))))))))
(Ns '())))))

2006/01/15

むっかー☆
自宅で仕事してたら Windows2000 Server がブルースクリーンだ。集中力きれちゃったよ。

先日感想を書いた『グラン・ヴァカンス』の作中での時間は、丸2日じゃなくて丸1日だった。ところで、読み終えてから数日たって今ごろ気づいたのは、この作品の世界が晩年のシューベルトのように明るい悲劇性を伴っているってことだ。SFちっくな道具だてでテンポよく語られてしまうので、読んだ直後は単に恍惚としてしまう。シューベルトの後期ピアノソナタ、とくに18番とかも同じで、一聴するときれいでやさしげな旋律なんだけど、ひょんな瞬間に絶望がまとわりついているのに気づく。いつもなら明るい陽光に照らされた光景なのに、いったん地面にうつった影に気になってしまうと、もう影のほうばっかり気になってしかたない、みたいな。

2006/01/04

LaTeX には、テキストをワク囲みするためのまともな環境がない。screenみたいな、minipageを使う環境は全部だめな。footnoteが使えないから。そもそも改ページできないし。で、Knooth本を読んだり Web を見たりしていると、やっぱり本当におかしいのは TeX でも LaTeX でもなく、LaTeX 信者なんだと確信してくる。LaTeX ベースのシステムそのものは、しばらく便利に使わせてもらいます。
LaTeX 信者の特徴は、Web で \ を \ を使って表したがることだ。ありゃどういう風習なんだ? コピペでいきないじゃん。しかしそんなこと言おうものなら、TeXの習得には練習あるのみです、とかいわれかねない(これもまた信者の特徴)。練習して熟練するようなもんじゃないとおもうんだが。ツールだぜ。同じツールでもセンスのいいものなら喜んで勉強したいが。
しかたがないので、ページをまたぐことができ、かつfootnoteを安直に付けられる書籍コラム用のlatex環境を、かつて仕事で作った。今度また必要になりそうなので、仕事中に作った部分は改めてスクラッチから作りなおし、public domain で晒しておくことにしようと思う。

column.sty

出力例(まだ)

実体はlongtable環境。どこでも改ページできるわけではなく、段落の切れ目でのみ改ページできる(もちろん手動で改行を入れればそこで改ページできる)。longtableがけっこう強力なので、このコラムの内部にもほとんどの要素をぶちこめると思う。
なお、column.sty とか名前はついているけれど、スタイルファイルとして汎用に使えるわけじゃないです。コンピュータ書に多い、B5変形で使った場合の一例を公開しているものにすぎないと考えてください。とくに、ワクの上下の角丸の罫線の定義は相当きたない。あくまでも、これをパラメータをあちこちいじればほぼ使える環境がカスタマイズできるはず、というレベルのものであって、「コラム」と呼ばれるスタイルに汎用の要素を切り出して整理したりするつもりはないのであしからず。

2006/01/03

2006/01/02

いまさらだが、正月1日と2日で飛浩隆『グラン・ヴァカンス』を読めた。作中の時間も、いちおう正味2日の出来事になっているようなので、これはつまり、ほぼリアルタイムに、夏の区界をめぐる攻防を追体験したことになる。
実は、amazonからこの本が届いてずいぶん経つ。届いたときにページをぱらぱらめくって、けっこう文字が多かったものだから、しばらく放置してしまっていた。昨日、昼からぽちぽち仕事をしてたんだけど、すぐにむくむくと現実逃避したくなってしまい、それで夕方になって本書を手にとったら一気に引き込まれてしまった。だから、作中とのほぼリアルタイムな読書体験に貢献したのは、正月休みのヒマっぷりではない(実際余裕なさすぎ)。この作品には異様なスピード感がある。左翼ちっくな口上とか、無駄に冗長な人物描写とか、ない。それでも主要な登場人物は魅力でいっぱいだし、彼らに死亡フラグが立って舞台から消えるまでの一文一文は読んでいて本当に寂しい(とくにアンヌ)。
で、これは『廃園の天使I』って銘だけど、IIはいつよ。