Island Life

2011/05/25

ピアノレッスン4回目

  • 基本
    • スケールは126まで上がったが左手の下降でよくこける→左手だけ練習。 スタッカートでやるといい。レガートで少し目標値より速く弾けるようになってから 両手で目標値。
    • アルペジオ(triad): 今週は116までいった。スケールと同じ速度目指してがんばりましょー。
    • アルペジオ(7th): 指使い、一番楽な形を当てていたんだけど、 白鍵から始まる時は5-4-3-2-1-4-3-2-...でやるべし、とのこと。 確かにこっちの方が4指の強化につながりそうだ。
  • Kapustin Op40-6: 最初に弾いてみせて以来だが、"100%良くなった" と言われた。へへ。
  • Kapustin Op40-2: 初めて人前で弾くとやっぱりめっさ緊張する。
    • A part, 右手メロディが強いが、Kapustinは左手もおもしろいのでもっとevenにするといい
    • A part 左手3つのeighthと符点がつくパターンの違いを意識
    • A part 譜面に明示されてないけどダイナミクスをつけるべきところはもっとある
    • B part もっとはじけちゃっていい。A partとの対照。
    • ペダル全体的にもっと控えてみたら。音の雲にまぎれちゃう。Kapustinはもっと明瞭に弾いてる。
  • Rachmaninoff Op23-5 は時間切れで見せられず。

Tag: Piano

2011/05/24

Paul Grahamとサンダル

Y Combinator’s Paul Graham: We’re Looking For People Like Us

コメントで、Paulが靴下にサンダルを履いてるのを突っ込んでる人たちがいてふと思い出した。

昔、ニューヨークのILCでPaul他何人かと昼飯か何か食べに出た時、 Paulは私の足元を見て「やけに立派な靴を履いてるじゃないか」と茶化したものだ。 それは私が一足だけ持っている革靴で、普段は裸足にサンダルで暮らしてるわけだが、 ニューヨークはきっと恐ろしく寒いに違いないと思ってその時は靴を履いて行ったのだ。 だがPaulの口調はまるで靴を履いたLisperが珍奇な生き物か何かであるかのようだった。

Tag: PaulGraham

2011/05/24

コードの所属

日常出会うプログラミングの問題の多くにおいては、 コードを書くこと自体は難しいことではなく、 むしろそれを後から参照できるように「適切な名前をつけて」「適切な置き場所を見つける」 ことの方がはるかに難しい。

クラスベース(メソッドがひとつのクラスに従属するタイプ) のOOPがこんだけ流行った理由のひとつは、この置き場所問題に 簡単なrule of thumbを与えたからかもしれない。そのコードが主として作用する 「もの」を見つけ、それをクラスにしてその名前空間に放り込んでしまえば良いのだ。 クラスをどう名づけるかという問題は依然として存在するものの (そしてJavaのクラス名一覧を眺めると名付け問題についてはうまく解決できていない であろうことが推し量られる)、一度クラスが決まれば場所が決まるという 安心感は大きい。

コードが2つの種類の「もの」の間に作用する場合とか、 コードが抽象的すぎて作用する「もの」を抽象的な性質でしか示せない場合とかは このrule of thumbがうまく使えない。クラスベースOOPで分類に困るケースって だいたいこんなところだと思う。

CLOSのマルチメソッドやHaskellの型クラスにはそういう困難は無いけれど、 場所問題の解決は示してくれないので、プログラマが自分で考えないとならない。 Haskellの事情はよく知らないけど、CLOSの人気が出ないのはそのへんにも 理由があるやもしれぬ。

★ ★ ★

何でこんなことを思ったかというと、長年頭の隅にひっかかってた 「場所問題」にひとつけりがついたのだ。

Unicodeのコードポイントとutf-8との変換を行うルーチンって、 これまで必要に応じて何回も書く機会があり、Gaucheに標準でつけときたいな と思ってたんだけど、どうも適切な置き場が見つけられなくて、 一から書いても10行くらいだから、結局毎回書いていた。 Gaucheの内部エンコーディングがutf-8でコンパイルされていれば コードポイントを文字にしてI/Oするだけで変換できるんだが、 「integerで表現されたコードポイントをutf-8のオクテット列に」とかその逆、 っていうのは内部エンコーディングに関係なく必要になるし、 関係なく書ける処理である。 内部エンコーディングに関わらないんで、文字や文字列の処理と 同じ場所に放り込むのは落ち着かない。入出力とも違う。 処理の作用する対象は整数だけど、数値モジュールでもない。

R6RSやR7RS draftのstring-downcasestring-titlecaseの 実装をしていて、これはUnicode Standard Annex #29の Unicode Text Segmentationをちゃんと実装しないと書けないという ことに気づいて、結局そのへんの処理がtext.unicodeというモジュールの 形を取ってきたところで、上の「コードポイントとutf-8との変換」も ここに属する処理であることに気づいた。 内部エンコーディングにかかわらず、Unicode Standardで決められてる 何かを扱うモジュール、というわけだ。

Tags: Programming, Gauche

2011/05/18

ピアノレッスン 3回目

  • 基本: スケール MM=120, アルペジオ MM=96, アルペジオ(7th) MM=72, までできるようになった。「アルペジオはスケールと同じ速度で弾けるように なりましょう」だそうなので当分アルペジオに重点を置こう。
  • ラフマニノフ Op23-5 ゆっくりだがデュナーミクや多声部の表現については だんだんできるようになってきた。だが音を全部頭に入れないとつっかえる。
  • Kapustinは今日はレッスンで見せる時間が無かった。 一応拍を数えながらOp40-6はだいぶスムースになってきて、 Op40-2をゆっくり数えながらさらいはじめたところ。 ラフマニノフがもうちょい速くなれば見せる時間が取れるだろう。

Tag: Piano

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

More entries ...