Island Life

2011/05/17

Scheme脳とCL脳

たまたまAllegro CLからLuceneベースの検索プラットフォームSolr http://lucene.apache.org/solr/ をたたく必要があって ライブラリを書いたのだけれど、ついでだからと思い立って 同時にGauche用のモジュールも書いてみた。

普段はSchemeとCLを行ったり来たりしているし、一方で書いたコードを もう一方に移植することもあるけれど、同じことをするモジュールを 同時進行で書くというは初めてで、なかなか面白かった。

入門用の簡単な課題を書くぶんにはSchemeとCLの違いはせいぜい 異なる方言くらいにしか見えないけれど、 現実的なものを書こうとすると、根っ子の哲学の違いが 実際のコードに無視できない影響を与えているのがわかる。 英語とフランス語くらい離れているかもしれん。

CLではライブラリからして、副作用ベースで動くものが多い。 XMLのシリアライズに使ったこれ https://github.com/franzinc/net-xml-generator もその典型的なパターンで、with-xml-generation マクロで 動的環境をセットアップして、その動的環境内でXML構築のDSLを 実行すると、それがセットアップ時に指定されたポートへと出力される。

一方Schemeでは、下請け関数がSXMLの断片を作って返し、 全部まとめた後でシリアライザに渡す、という関数的なスタイルの方が普通だと思う。

どちらの方式にも一長一短がある。CL方式は、 バッファリングが不要な場合はどんなに出力が大きくなってもメモリを圧迫しない。 Scheme方式では出力の大きさに比例したメモリを必要とする (手軽にlazyな木が構築できればいいんだけど。)

条件によって要素を出したり出さなかったりする場合、 CL式では制御の流れだけを考えて、どういうコンテキストにあってもwhenで囲めば良いが、 Scheme式では、その戻り値がどういう形で埋め込まれるのかを考えないとならない。

もちろんSchemeでCL式に書くことも、その逆もやればできなくはないけれど、 素直に有りもののライブラリを使ってゆくと、こういう異なるパラダイムの上に 乗っかってゆくことになる。

★ ★ ★

Allegro CLの方のコードも晒せると比較ができるんだけど、 出せるかどうかまだ不明なので、ちょっと抽象的な例をば。

CLの方でネストしたリストをXML化する。 (classとか適当につけてる。^@net-xml-geneartor が定義するリーダマクロ)。

(defun emit (obj port)
  (with-xml-generation (port)
    (emit-element obj)))

(defun emit-element (obj)
  (if (listp obj)
      (emit-list obj)
      @obj))

(defun emit-list (lis)
  ^((div @class "list")
    ^(ul (loop for e in lis
               for n from 0
               do (emit-item e n)))))

(defun emit-item (e n)
  ^((li @class (if (zerop (mod n 2)) "li0" "li1"))
    (emit-element e)))

出力例。

> (emit '(a b (c d) e) *standard-output*)

<div class="list">
  <ul>
    <li class="li0">a</li>
    <li class="li1">b</li>
    <li class="li0">
      <div class="list">
        <ul><li class="li0">c</li><li class="li1">d</li></ul>
      </div>
    </li>
    <li class="li1">e</li>
  </ul>
</div>
nil

同じ機能をSchemeで実現しようとすると、各下請け関数は SXMLを組み立てて返すようにしといて、一番上で完全なSXMLを受け取って レンダリングするように書くだろう。

(define (emit obj port)
  (srl:sxml->xml (build-element obj) port))

(define (build-element obj)
  (if (list? obj)
    (build-list obj)
    (x->string obj)))

(define (build-list lis)
  `(div (@ (class "list"))
    (ul ,@(map-with-index build-item lis))))

(define (build-item n e)
  `(li (@ (class ,(if (zero? (modulo n 2)) "li0" "li1")))
       ,(build-element e)))

出力:

gosh> (emit '(a b (c d) e) (current-output-port))
<div class="list">
  <ul>
    <li class="li0">a</li>
    <li class="li1">b</li>
    <li class="li0">
      <div class="list">
        <ul>
          <li class="li0">c</li>
          <li class="li1">d</li>
        </ul>
      </div>
    </li>
    <li class="li1">e</li>
  </ul>
</div>#<undef>

どちらもコードの構造はほとんど同じだから、LispにもSchemeにも詳しくない人が 見たらほとんど見分けがつかないんじゃないか。

でもある程度片方がわかる人が、もう片方をよく理解しようとすると「あれ?」となるんじゃないかな。 特に、Scheme脳の人は上のCLのコードを見た時に、

^(tag ...)<tag>...</tag>を表すのか。 ってことは^(tag ...) 式は要素を表すオブジェクトを返すのかな?」

って考えてしまうんじゃないかと思う。 実は ^(tag ...) は「コマンド」であって、値は返さない。 基本的なところで、かなり大きなパラダイムの差がある。

個人的にはScheme式の方がストレートで好きなんだけれど。 性能の問題を脇におくとしても、CL方式にはもうひとつ、レンダリングの途中で スペシャル変数をいじることでその枝だけ出力を変える、なんてことができる。 Scheme式の場合は、レンダリングが別フェーズなので、一部だけ出力を変えたければ その情報を木に埋め込むか、 あるいはモナドを使って暗黙のコンテキスト情報を受け渡して行くことになる (これはKahuaでやったんだけれど、静的型でないSchemeでモナドを使いまくるのは わりとしんどい。)

というわけでどっちも捨てがたいんだよなあ。

Tags: Programming, Lisp, Scheme, Gauche

2011/05/15

芋づる開発

ふとアイディアを思いついて、実装して試してみようとする。 すると、ああこのフレームワークにこの機能があった方が書きやすいや、 とか、ああこのユーティリティ関数はもうちょっといじれば汎用的になって モジュールとして独立させよう、とか、 それを書いてるうちに、そのモジュールが依存している このライブラリの仕様はもっと柔軟にできるな、と気づいたりとか、 そういう派生タスクが芋づる式にどんどん出てくる。

締切りのある仕事だと、それをやってると終わらなくなるので、 ライブラリは所与のものとしていじらず、あくまで自分が今手をつけている コードの塊の中で何とかすることが多い。 でもGaucheで書いてる時は、好きな部分にどんどん手が入れられるし、 そうやって汎用的な部分を改良したものは将来にわたって利益になるから、 ついつい手を出してしまう。

そうやって、派生したタスクから派生したタスクから派生した…タスクのひとつを 仕上げてテストしてドキュメント書いてコミットして、ああなんだか充実感。 と思った時には、そもそも最初にやろうとしてたアイディアはどっか行っちゃってたり。

でも自分の考えることなんてそうそう変わらないので、 何ヶ月後か何年後か、また同じようなことを思いついて、さて実装して試してみようと すると、まさにそのアイディアのために用意されているようなライブラリの機能を 見つけることになる。

最近、そんな派生タスクから実現されたんだけど、きっかけとなったアイディアがどっか行っちゃったもの:

  • map* - 不完全リストにも適用できるmapdefine-method:optionalとかサポートしなきゃって時に、 mapで不完全リストも扱えるとmethod lambda listの処理がもっと綺麗に書けるんじゃないか、 と思って書いたんだけど結局最初の目的には使わなかった。
  • with-lock-file - file.utilモジュールでロックファイルを扱うユーティリティ。前からあるといいなと思ってたんだけど汎用的にしようとしたら意外と面倒でほったらかしてた。今回何か使いたいことがあって、せっかくの機会だからえいやと書いてみたんだけど、さて何に使いたかったんだっけ。
  • atom - gauche.threadsモジュール。Clojureのatomに触発されていつか入れたいとは思ってたんだけど、実際に書くきっかけになったアイディアはどっかのgitのローカルブランチで腐っている。

いくつか挙げてみるとパターンがあるな。どれも昔から「こういうのがあるといいかもなあ」というアイディアのぼんやりした形があって、ある時「これが必要かも」という別のアイディアからの要請でもって臨界を越えると実際に書かれることになるようだ。

でもこれを無制限に続けてると最初思いついたアイディアをいつまでたっても実装できないので、 適当に見切りをつけることも重要だなあ。だいたい3層くらいスタックを積んだところで我に返って本来の目的を思い出すことが多いかな。

gitに切り替えてからは派生タスクの度にブランチを切ってるので、そうやって放棄されたローカルブランチがついついたまってしまう。

Tag: Programming

2011/05/12

素人の自由な発想

またRTされてきたやつについつい突っ込み。

http://twitter.com/hiroshiishiguro/status/68849345889439744

@hiroshiishiguro: 素人のように考え、玄人のように実行する。これが研究の極意と、米国の著名な日本人教授は言う。そうだと思う。素人の何の制約もない発想が物事の本質を捕らえている可能性は高い。

「素人のように…」は、専門分野の発想にとらわれずに探索空間を広く取れ、 ということだろう。それはおおいに頷ける。でも、後半の 「素人の何の制約もない発想が物事の本質を捕らえている可能性は高い。」 にちょっとひっかかった。

専門分野の典型的な発想の外側に「本質」がある場合、 「玄人のように考え」ている人は探索範囲が狭まっているので「本質」を見つけられない。 自由な「素人」ならば「本質」を見つけられる可能性がある

けれども、自由な「素人」はまったくランダムに発想するから、「本質」でない発想も大量に生じる。

従って、

  • 膨大な「素人」を集めればそのうちの誰かの発想が「本質」を突いている

という可能性は高いけれど、

  • ある特定の「素人」が自由に思いついた発想が「本質」をとらえている

可能性は限りなく低い。

石黒浩氏が「素人の何の制約もない発想」で意図してたのは前者だとは思うけれど、 元発言だと後者のようにも取れるよなあと。140文字の制約って難しいね!

さらに、発想が本質的なものであったかどうかというのは実行してみないとわからないので、 たまたま「本質」をとらえた「素人」本人は、それを実行できない限り、自分が本質を捕らえたかどうかの判断はできない。

なので、

「素人の何の制約もない発想が物事の本質を捕らえている可能性は高い。」

というのは、実行して確かめることができる人間が言うなら意味があるけど、 実行できない人間が言っても意味がない。石黒浩氏は実行する人なので意味があるけど、 この言葉だけ一人歩きするのも考えものだなあと。

発想に関しての指針なら、この言葉が好きだ。

  • Any idea is a good idea if you do it well.

素人か玄人か、で判断するのではなく、うまく実行できたかどうか、で判断すればいい。

前にも似たようなことを書いた。

技芸とサイエンス

理論を学ぶことによって自由な発想ができなくなってしまうのではないか、と懸念する人もいる。「素人の方が既存の概念に縛られない発想ができる」ってやつだ。そういうこともたまにはある。宝くじを当てるのに理論は必要ない。何かを作ることをただ趣味でやりたいだけなら、ひたすらスロットマシンのレバーを引くように続けるのも良いだろう。退屈はしないし、時々小さな当たりがあるし、ジャックポットの可能性だってゼロではない。

Tags: 表現, ものつくり

2011/05/12

リスクを巡るすれ違い

坂本龍一のこのtweet、たくさんretweetされているから共感を呼んだのだろう。

http://twitter.com/skmt09/status/68031808876462080

@skmt09: とうとう本音を言ったね!RT @neneyu: @SeiichiMizuno 山下教授フジテレビ今朝のトクダネで「20ミリを引き下げたら、避難させなければならないでしょ。これだけ大勢の人を、あなた何処に避難させますか?」(大意)と宣いました。

「とうとう本音を言ったね!」 この発言には、二つの見方があり得る。

字面だけを見るとこの発言はちと妙だ。 確率的なリスクへの対応は、コストとのトレードオフだ。 基準を厳しくすればコストは上がる。緩めれば下がる。 現実的に、このくらいのコストでこのくらいのリスクなら受け入れましょう、 というところで基準が決められる。 だから、「これ以上引き下げたらコストがかかりすぎる」というのは 別に隠されていた本音でも何でもない。 20mSvに決めた、ということは、決めた人間は安全とコストのバランスをそう判断したということと同値だ。 「いやその計算はコストを高く見積もりすぎ」とか「リスクを低く見積もりすぎ」とかいう議論は できるだろうけれど、 既に明らかに述べられている事実を別の言葉で言っただけのことに対して 「とうとう本音を言ったね」というツッコミは 「今更何言ってるんだ?」ってことになる。

でも、教授もRTしている人も、リスクとコストがトレードオフだなんてきっとわかってる。 行政がリスクのその点に線を引いたということは、コストでも線を引いたということを包含する、 それは論理的に正しいけれど、ポイントはそこではないのだ。 見積りの多寡について議論はできるにせよ、最後にはこの問題は政治的決着をつけるしかない。 つまり、どう切っても不満が残る問題を、決断を任された人がえいやと切るしかない。 大事なのは納得できる人を最大化することだ。 それには、決断した人が、切り捨てられた人の不満を正面から受け止めるしかないと思う。 あなたのこれ以上の安心のためにこれ以上の金はかけられないと判断したのだ、 どうか呑んでくれ、と明白に口に出すことだ。

もちろんそれで全てが免罪されるわけではなく、むしろそこが出発点で、 そうはっきり認めることで、じゃあそのバランスを具体的にこういじれば改善できるんでないの、 という建設的な話ができるようになる。 口に出すことは、そういう話にも真剣に向き合いますよ、という決意表明なのだ。

「とうとう本音を言ったね!」という言葉が出てくる背景には、 決断する責任にはっきり向き合おうとしない者への不満がくすぶっているのではないか。 ただちに健康に影響はない、タバコの方が危険が高い、それらは事実であろう。 けれども、皆が知りたいのは事実だけではないのだ。その上で執行する者たちの、姿勢こそを知りたいのではないか。

一方は、事実をいくら説いても納得されない、もっと噛み砕いて説明しなければ、と考え、

もう一方は、既に知っている事実ばかり並べて、肝心の姿勢についてはぐらかしている、と感じる。

これで平行線になってることが、多々あるように思う。

(このエントリは『リスクをめぐる専門家たちの"神話"』に触発されて書いた。2002年の文章だが、今の現実を述べているかのようだ。)

Tag: 社会

2011/05/11

ピアノレッスン 2回目

先週のレッスンから1週間でやったこと:

  • 拍を声に出して数えながら弾く:これは劇的な効果があった。 リズムがあやふやなところに来るとたちまち数えられなくなる。 その境界が非常にはっきりしているので、どこが弾けないかがピンポイントでわかる。 初めて最適化にプロファイラを使った時のような感動だ。可視化重要。
    • Kapustin Op40-6では、今の鬼門は40小節目の1, 2拍と60小節目の第3, 4拍。 今まで、「ゆっくり弾くとちゃんと弾ける、ここだけ取り出して弾いても弾ける、でも全部を速く弾くとこのへんがわからなくなる」という状態で、なんで弾けないのかわからなかったんだけれど、その理由がわかった。どちらも直前(39小節目の最後の16分音符、60小節目の第2拍の最後の16分音符)に強いアクセントがあって、次の拍頭は16分休符なんだけど、この休符がちゃんと取れておらず、前のフレーズからそのまま流していた。ここの休符でちゃんと休んで手の形を取り直すと次のフレーズが掴みやすいのだ。
    • 他にも、できてるつもりでできてなかったところ(31小節目の左手とか)がおもしいようにあぶり出される。
  • スケールとアルペジオ。今までなおざりにしておりましたすみませんすみません。 スケールはMM=96から始めて今は108で。短調がまだ怪しい。アルペジオはまだ96。
  • 新しい曲として、ラフマニノフのト短調プレリュード(Op23-5)。ものすごくスローだけど 一応最後まで通せるようにはなった。 ラフマニノフって弾きたかったんだけどなんか大変そうでこれまで後回しにしてたんだが、 色々な作曲家をやった方が良いと言われたので手をつけてみた。 音符がたくさんの和音を掴むのが苦手なんだよね。手は大きい方なんだけど、覚えられなくて。 この機会に和音を和声として感覚で掴めるようになりたい。
  • 初見(sight reading)の練習は、 アルカンの25のプレリュード(Op31)の中の、今まで聞いたことがなくて短くて易しめのをやってた。超スローテンポで。 手元にある他の易しめの楽譜はだいたい既に曲を知ってるから使えない。

レッスン:

  • スケール: 「その調子でがんばってね〜。MM=144くらいを目指しましょ〜」まじですか。 上行でクレシェンド、下行でデクレシェンドをつけること。
  • アルペジオ: 常にレガートで。同じく上行でクレシェンド、下行でデクレシェンド。 余裕があったら7thとdim7も。
  • ラフマニノフ: うるさくなりすぎないように細心の注意を。連打の和音はあくまで伴奏。 中間部の多声のところも、声部同士が「けんかしないように」。 和音の構成音が微妙に変化するところがあるから注意して楽譜を読むこと。
  • 初見練習: スローで良い。もうちょっと易しめのでもいいからどんどんやりましょう。

帰りに初見用の楽譜を仕入れた。

Tag: Piano

More entries ...