読者です 読者をやめる 読者になる 読者になる

Fibonacci Music, Mathematica 6.0, left-associative operator

LLSpirit .NET

Python Workshop the Edge 2007 (id:NyaRuRu:20070701:p1) のときも思ったのですが,西尾さんのセッションはおもしろいなぁと.言語にプレゼン向きかそうでないかという絶対的な性質があるかどうかは分かりませんが,西尾さん + Python という組み合わせが極上であることは間違いないと.
というわけで,LL 魂での西尾さんのセッションにインスパイアされて,Mathematica 6.0 で Fibonacci Music を作ってみました.

Why Mathematica?

まずは,なぜ Mathematica かというあたりからですが,以下の点で Fibonacci Music に向いています.

Mathematica は良いものです.どう書く?orgMathematicaまだまだ盛り上がるべき

Act.1 Mathematica で Fibonacci Music

出発点として,ちまちまと変数束縛を書いていくスタイルで書いてみました.

Module[ {seq, fibs, fibmod, notes, sound},
 seq = Range[50];
 fibs = Map[Fibonacci, seq];
 fibmod = Map[(Mod[#, 19] &), fibs];
 notes = Map[(SoundNote[#, 0.05 ] &), fibmod];
 sound = Sound[notes];
 EmitSound[sound];
]

これだけで Fibonacci Music 完成と.Module を作ったのは気まぐれで,最初の行と最後の行は取ってしまって構いません.本当に数行でできてしまいます.
ちなみに "(Mod[#, 19] &)" は Mathematica における無名関数の記法です.# がスロットで,& が無名関数の末尾を表します.
関数 Fibonacci は Listable なので,以下のようにも書くこともできます.上で Map を明示的に書いているのは,後で行う書き換えをわかりやすくするためです.

fibs = Fibonacci[Range[50]]
fibs = Fibonacci /@ Range[50]

Act.2 リストを左に

.NET の世界では,一般的にリスト操作関数の第一引数がリストになります.一方 Mathematica では,関数,リストの順で引数が渡されます.
ここで引数の順序をひっくり返した map 関数と関数適用を作ってみましょう.

Map2[x_List, y_] := Map[y, x];
SingleApply[x_, y_] := y[x];

最初のプログラムを書き直してみます.

Module[ {seq, fibs, fibmod, notes, sound},
 seq = Range[50];
 fibs = Map2[seq, Fibonacci];
 fibmod = Map2[fibs, (Mod[#, 19] &)];
 notes = Map2[fibmod, (SoundNote[#, 0.05 ] &)];
 sound = SingleApply[notes, Sound];
 SingleApply[sound, EmitSound]
]

引数の左側にリストがきて,少し C# っぽさが出てきました.

Act.3 中置演算子で左から右へ

先ほどのプログラムをひとつの式で書き下してみましょう.

SingleApply[
 SingleApply[
  Map2[Map2[
    Map2[Range[50], Fibonacci], ( 
     Mod[#, 19] &)], (SoundNote[#, 0.05 ] &)], Sound], EmitSound]

SingleApply や Map2 といった関数をひとまず無視すれば,左から右に順に変換処理が並んでいるのが分かります.
ここで,左から右へ処理を書き下す記法を導入してみましょう.C++ の >>,Haskell の >>=,C# 3.0 の Extension Method みたいな奴です.
まずは Mathematica の誇る変態パッケージ Notation を用いて,Map2 と SingleApply に以下のような中置演算子を対応付けてみます.

Mathematica では,数学屋さんが使うような変な記号 を導入し放題なので,敢えて既存の演算子を再利用する必要はありません.が,変な記号を使うと引用が難しくなるという罠.画像で引用しているのはそういうわけです.
さらに,左結合の中置演算子にするために,以下のようなルールを追加します.

Map2[x_List, y_, z__] := Map2[Map2[x, y], z];
SingleApply[x_, y_, z__] := SingleApply[SingleApply[x, y], z];

これで準備は整いました.いよいよ最終形です.

行末の "\" は行継続です.
同じプログラムでも,中置演算子の導入によって,ずいぶん印象が変わるのがおもしろいですね.