2005/05/31

HTMLで日本語を両端ぞろえにレイアウトしたい。CSS2.0の text-align:justify で簡単に実現しそうだが、英語文化のCS技術がそのまま他国語ですんなり通用することはあり得ない。
なんで text-align:justify がダメかというと、そもそもは組版文化の違いに起因するようだ。奴らは、一行にアルファベットをレイアウトするとき、単語の間のスペースを調整して組版する。 だからtext-align:justify でも、当然のように単語間のスペースしか調整してくれない。そのため、日本語のような単語区切りのない言語の行幅を調整することはできない。
CSS 3.0では、text-justifyというプロパティが用意されるらしい。こいつは、CJKおよびハングルのレイアウトを調整できるような実装が求められそう。どうやらIEでは勝手に実装しているみたいだが、そんなものは使いたくない。
しかたがないので、こういう荒業しかないのか?

line.gsub! /(\w)/e, '\1 '

Webで情報を提供する場合には絶対に避けるべき方法だよなあ。明らかに問題がある。今は情報公開のためにHTMLを使っているのではないので、もうこれでいいや。
クランベリージュース(190cc缶)が近くの酒屋に110円で売っていたので飲んでみた。もっとすっぱいのがよかった。
肌寒いので、ユニクロのジャケット(3900円)を羽織ってきた。会社でTさん(20代女性)に、今日はおしゃれですわね、と言われた。いつもはそんなにダサいのかよ。

2005/05/30

PowerPointで書かれたスライド達を一枚ずつgif画像として取り出したい。それ自体はPowerPointのメニューから[名前付けて保存]できるんだけど、日本語の「スライド1.GIF」みたいなファイル名で何百枚とかGIF画像ができるので萎える。しかもextensionが大文字だしい。この仕事には、Rubyしかないよね(Pythonでこういうのをサポートするツールもあるにはある)。

for i in `ls -A`; do ruby -e"old=ARGV[0]; new=old.gsub(/.*?(\d{1,2})\.GIF/, '\1.gif'); File.rename(old, new)" $i; done

これは備忘録ではなく、憤慨の記録でうs。

2005/05/29

引き続きcall/ccに悩み中。Dibvig本の「3.5 内部定義」には、本文で定義したcalcというプロシージャをcall/ccを削除して定義し直せ、という練習問題がある。call/ccを使ったバージョンは以下のとおり。ちなみに、これは本文のオリジナルバージョンではなく、問題3.5.2の前半までに対する僕の回答。

; Dibvig-3.5.2-1
(define calc1
(lambda (exp)
(define-syntax complain
(syntax-rules ()
((_ e1 e2 e3) (e1 (list e2 e3)))))
(call/cc
(lambda (ek)
(define apply-op
(lambda (ek op args)
(op (do-calc ek (car args)) (do-calc ek (cadr args)))))
(define do-calc
(lambda (ek exp)
(cond
((number? exp) exp)
((and (list? exp) (= (length exp) 3))
(let ((op (car exp)) (args (cdr exp)))
(case op
((add) (apply-op ek + args))
((sub) (apply-op ek - args))
((mul) (apply-op ek * args))
((div) (apply-op ek / args))
(else (complain ek "invalid operator" op)))))
(else (complain ek "invalid expression" exp)))))
(do-calc ek exp)))))

gosh> (calc1 '(div 2 3))
=> 0.6666666666666666

gosh> (calc1 '(div (div (div 3 2)) 2))
=> ("invalid expression" (div (div 3 2)))

エラー処理もうまくいっている。で、ここからcall/ccを削除したいんだけど、単純に次のようにしてはダメらしい。

; Dibvig-3.5.2-2
(define calc2
(lambda (exp)
(define-syntax complain
(syntax-rules ()
((_ e1 e2) (list e1 e2))))
(let ()
(define apply-op
(lambda (op args)
(op (do-calc (car args)) (do-calc (cadr args)))))
(define do-calc
(lambda (exp)
(cond
((number? exp) exp)
((and (list? exp) (= (length exp) 3))
(let ((op (car exp)) (args (cdr exp)))
(case op
((add) (apply-op + args))
((sub) (apply-op - args))
((mul) (apply-op * args))
((div) (apply-op / args))
(else (complain "invalid operator" op)))))
(else (complain "invalid expression" exp)))))
(do-calc exp))))

gosh> (calc2 '(div 2 3))
=> 0.6666666666666666

gosh> (calc2 '(div (div (div 3 2)) 2))
=> *** ERROR: operation / is not defined between ("invalid expression" (div (div 3 2))) and 2
Stack Trace:
_______________________________________

エラー処理がうまくない。一回だけエラー処理をしているけど、そこで抜けられていない。apply-opの再帰を継続引き渡しで書き直せばいいのかな?
なんだかやるせないのでCDを買った。たいていアタリハズレが大きいけど、今日はどれもアタリ。これだけアタリだとうれしくなる。
MESSIAEN: Preludes, et al
CANTELOUBE: Chants d'Auvergne
ADAMS: Grand Pianola Music, REICH:Vermont Counterpoint, Eight Lines
Mozart: Piano Sonatas
System of a Down: Mezmerize
Marilyn Monroe: I Wanna Be Loved By You
しかし、どれもアタリなのは、購入するときに冒険しないからともいう。Naxos の Messiaen はダブったし(弟に貸してたのを忘れてた)。結局好きなものばかり物色してればハッピーなのかもしれない。

2005/05/25

そうか。リストに0があったときに掛け算を脱出しているのかどうかは、こうすればわかるのか。

gosh> (product/cps '(2 3 4 0 'a) 1)
0
gosh> (product/cps '(2 3 4 'a) 1)
*** ERROR: real number required: (quote a)

どうやら脱出はできているようだ。
継続がらみは悩ましい。Kent Dibvigでは、call/ccの最初の例として、次のような局所的脱出が挙げられている。

(define product
(lambda (ls)
(call/cc
(lambda (break)
(let f ((ls ls))
(cond
((null? ls) 1)
((= (car ls) 0) (break 0))
(else (* (car ls) (f (cdr ls))))))))))

(product '(2 3 4) => 24

これはつまり、リストの途中に0が出てきたら、その時点の継続を0に設定して返すというものだ。そうすれば残りの掛け算をせずにfの再帰から抜けられる、というのがメリットらしい。ここまではわかる。
わからないのは、単にリストの途中に0が出てきたら抜けるという動作をさせたいだけなら、次のコードでも一緒じゃないのか? という点だ。

(define product
(lambda (ls)
(let ((ls ls))
(cond
((null? ls) 1)
((= (car ls) 0) 0)
(else (* (car ls) (product (cdr ls))))))))

もっとも、これだと末尾再帰じゃなくて気持悪いので、こうは改良したい。

(Define product/cps
(lambda (ls k)
(let ((ls ls) (k k))
(cond
((null? ls) k)
((= (car ls) 0) 0)
(else (product/cps (cdr ls) (* (car ls) k)))))))

(product/cps '(2 3 4) 1) => 24

この三つ目のバージョンでは継続引き渡しを使ってるつもりなんだけど、このように末尾再帰で書けることをもってして継続のメリットだと主張されるならとてもクリアだ。となると、この例における call/cc のありがた味っていったい……

さらに、もう一つ、Kent Dibvigの継続の解説でさらに分からなくなることがあって困る。本の中では、最初のリストの掛け算を継続引き渡しで書き直す例も説明されてるんだけど、それがどうにも妙ちきりんなんだよね。

(define product/cps
(lambda (ls k)
(let ((break k))
(let f ((ls ls) (k k))
(cond
((null? ls) (k 1))
((= (car ls) 0) (break 0))
(else (f (cdr ls) (lambda (x) (k (* (car ls) x))))))))))

(product/cps '(2 3 4) (lambda (x) x)) => 24

もしかしたら、こういう風に「自分自身を返す継続」を使うことが推奨されるのだろうか。それとも、単に僕の解釈が間違っていて、上記の3番目のバージョンでは継続引き渡しになっていないのだろうか。ああ、そもそも3番目のバージョンは「リストの途中に0があったらその時点で後続の掛け算をやめる」という仕様を満たしていないのかもしれないのか。Gauche に Chez Scheme のような trace があればなあ。どうやって作るんだろう。

2005/05/23

一昨日のエントリのように文字列をうねうね曲げる。とりあえず1バイト文字バージョンで妥協。あとは、折り曲げるポイントを割り出すための仕組みが必要か。
; (fold-puts '("a" ("b" . "l") "c" "d" ("e" . "d") "f" "g"))
; =>
; edcb
; f a
; g
;
(use srfi-1)

(define culc-room-space
(lambda (ls)
(cons
(if (>= (car ls) (cadr ls))
(car ls)
(cadr ls))
(if (>= (caddr ls) (cadddr ls))
(caddr ls)
(cadddr ls)))))

(define count-direction
(lambda (ls)
(let f ((ls ls) (height+ 1) (height- 1) (width+ 1) (width- 1) (direction "u"))
(if (null? (cdr ls))
(cond ((equal? direction "l")
(list height+ height- (- width+ 1) width-))
((equal? direction "r")
(list height+ height- width+ (- width- 1)))
((equal? direction "u")
(list (- height+ 1) height- width+ width-))
((equal? direction "d")
(list height+ (- height- 1) width+ width-)))
(if (and (pair? (car ls)) (dotted-list? (car ls)))
(cond ((equal? (cdr (car ls)) "l")
(f (cdr ls) height+ height- (+ width+ 1) width- "l"))
((equal? (cdr (car ls)) "r")
(f (cdr ls) height+ height- width+ (+ width- 1) "r"))
((equal? (cdr (car ls)) "u")
(f (cdr ls) (+ height+ 1) height- width+ width- "u"))
((equal? (cdr (car ls)) "d")
(f (cdr ls) height+ (+ height- 1) width+ width- "d")))
(cond ((equal? direction "l")
(f (cdr ls) height+ height- (+ width+ 1) width- direction))
((equal? direction "r")
(f (cdr ls) height+ height- width+ (+ width- 1) direction))
((equal? direction "u")
(f (cdr ls) (+ height+ 1) height- width+ width- direction))
((equal? direction "d")
(f (cdr ls) height+ (+ height- 1) width+ width- direction))
(else
(f (cdr ls) height width direction))))))))

(define make-room
(lambda (size)
(do ((m (make-vector (car size)))
(i 0 (+ i 1)))
((= i (car size)) m)
(vector-set! m i (make-vector (cdr size))))))

(define puts-room
(lambda (m)
(do ((i 0 (+ i 1)))
((= i (vector-length m)))
(print
(let ((column (vector-ref m i)))
(do ((i 0 (+ i 1)))
((= i (vector-length column)))
(let ((char (vector-ref column i)))
(if (string? char)
(display char)
(display " ")))))))))

(define set-char-at!
(lambda (room i j character)
(vector-set! (vector-ref room i) j character)))

(define fold-puts
(lambda (ls)
(let* ((dirs (count-direction ls)) (m (make-room (culc-room-space dirs))))
(let puts ((i (- (car dirs) 1))
(j (- (caddr dirs) 1))
(ls ls)
(dir "u"))
(if (null? (cdr ls))
(puts-room m)
(if (and (pair? (car ls)) (dotted-list? (car ls)))
(begin
(set-char-at! m i j (caar ls))
(cond ((equal? (cdr (car ls)) "l")
(puts i (- j 1) (cdr ls) "l"))
((equal? (cdr (car ls)) "r")
(puts i (+ j 1) (cdr ls) "r"))
((equal? (cdr (car ls)) "u")
(puts (- i 1) j (cdr ls) "u"))
((equal? (cdr (car ls)) "d")
(puts (+ i 1) j (cdr ls) "d"))))
(begin
(set-char-at! m i j (car ls))
(cond ((equal? dir "l")
(puts i (- j 1) (cdr ls) "l"))
((equal? dir "r")
(puts i (+ j 1) (cdr ls) "r"))
((equal? dir "u")
(puts (- i 1) j (cdr ls) "u"))
((equal? dir "d")
(puts (+ i 1) j (cdr ls) "d"))))))))))


しかしまたみにくいなあ。やたらにcondだらけ。屈折した文字列をディスプレイするのに必要なマスの大きさを求めるのと、実際に屈折させた文字列を書き出すところは、同じようなcondが続く。こういう場面で、きっとマクロを使うに違いない。
ところでこんなものを曝すことに第三者的な意義なんてありえない。だから、これは自身のモチベーション維持としての役割なんだろう。

2005/05/21


きゅうじつにしなければいけないしごとは


して、いつまでもいつまでもやる の
そ き な
。るなにちもきいらくてくおおがのもい

てさらにくらいき き
っ ぶ ず
なくながんかじんどんど、

ますや さ
す つ い
まてれまな



スパイラル

2005/05/19

数年ぶりの知人から連絡がきて、その人間が勤めている会社のURLがわかると、つい採用情報をクリックしてしまう罠。サラリーマンでいる限り、どこも一緒だろ。

2005/05/18

十年以上にわたって、この三日間ほど父親に話しかけたことはなかった。決していい父親ではなかったが、自分もいい息子ではなかった。それだけのこと。

2005/05/10

『非決定性 選択公理』ってキーワードでGoogleしても電波は感じられないのか。むう。

2005/05/09

肩こりがひどくて水海道(茨城)の病院にいったら、脳波を測定されて精神障害で強制入院させられそうになる。
という夢で目が覚めた。

眠たいという欲求は現実からの逃避を内包している。

2005/05/05

なんのためにはたらいているのだろう。

* 自分のため
* 他人のため
* 社会のため
* 知人のため
* 会社のため
* お国のため
* 家族のため
* お客のため
* ひまつぶし

順不同ですあしからず。

2005/05/03

逓信総合博物館へ収蔵品の写真を借りにいく。たいへん親切に対応していただきありがとうございました。

上野の科博は140分待ちの行列ができてたけど、ここは静か。それでも、普段と比べると多いらしい。みんな博物館にいくのはやめてください。どこもかしこも科博みたいになったらがっかりだ。

逓信総合博物館は、NTTとNHKと郵政公社という、並べるといい感じに何かが凝縮される3つの団体が共同で運営している。ただし、館内の展示はすっかり分断していて、通信と放送の融合なんていうキーワードとは無縁の世界になっている。企画展ではいろいろと協力するのかな。展示のおもしろさは、NTT > 郵政公社 >= NHK という感じ。なにせNHKのブースで印象的だったは、モニターにカードキャプターさくらを垂れ流してるコーナーだったから。

博物館が工夫すべきなのは、見た目の親しみやすさではないと思う。珍しいものを、「それがどういう意味で価値を持ち、単に珍しいだけじゃない」とわかるように展示してほしい。いかにも「わかりきったことだし努力しています」って言われそう。でも、展示物の脇の説明とか、博物館が提供してくれる解説が微妙に読みにくいと感じているのは、僕だけじゃないはず。ほかの展示物との関係をもっと明確に示してくれるとか、そういう工夫だけでもいいんだけど。
たとえば、『強構造化ダニエル式ラダー』と『非構造化リズ式ラダー』が展示されていて、『強構造化ダニエル式ラダー』の解説プレートに「『非構造化リズ式ラダー』の欠点を補って2150年にJ.ダニエルにより開発された」とだけ書いてあっても、『非構造化リズ式ラダー』が何なのか閲覧者にはわからないのがオチだ。『強構造化ダニエル式ラダー』の直前に『非構造化リズ式ラダー』が陳列してあったのかもしれなけど、そんなことはたいてい覚えてない。もし『強構造化リズ式ラダー』も置いてあったら、まちがいなく専門家以外には区別ができず、博物館を出た瞬間(それどころか展示物を見た次の瞬間)にすべて忘れてる。なんか書いてるだけでもこんぐらがってきたし。

つまり見せる工夫というのは、「見たい」需要が顕在化していないものを見せるための工夫であって、見たい人がたくさんいることがわかっているものを用意して客を寄せることではないのです。潜在的な需要をたたき出すための工夫という意味では、広告代理店とかでの研修をするのもよろしいのではないでしょうか。

実際には、必ずしも広告代理店が潜在的な需要のたたき出しに成功するわけではないので、無意味だと思う。ただ、同じ意識を持つことの役には立つかもしれない。

2005/05/02

なんだか会社を出てから忙しい一日だったが、それは少なくとも不本意な仕事でないという一点で十分に虚しくない。虚しさと不本意の出現順は逆かもしれない。

奥様の実家へ向かう電車のなかでR5RS(鈴木さんの訳したバージョン)を読み出したら、止まらなくなった。なんていうかその、(1社内での)出世とか給料とかは本気でどうでもよくて、こういうことだけ考えていられないものかね。しかしこれは、一歩間違うと受動的な趣味の枠内に陥る。ぼくは漫画やアニメが好きですという青年男子の主張と何ら変わりない、という可能性はないだろうか。ある。あるんだよ。
反論:自分がいま社会に対して責任を持っているのは別の業務だから、そちらに時間を割く。ここでいう時間というのは、物理量ではないし、区分可能でもない。ヘンに何か始めると際限なく没頭することになり、社会的な責任を放棄せざるを得なくなるため、どうにかこうにかブレーキをかけている。
正論:とにかく資産がない。資産とは、一時的な貯金額ではなく、将来に対する不安の少なさを意味する。