作品番号の整列 |
作成日: 2005-09-23 最終更新日: |
古典音楽では、作品ごとに作品番号がつけられることが多い。 番号というからには数字である。したがって、数字を若い順に並べれば整列できる。
一番簡単な例を考える。ある作曲家が、作品1から作品12まで作ったとする。 作品番号の配列を op という変数に格納してみる。若い順に並べてみよう。 そして、一度逆順にしたのち、整列させる。
op = %w(1 2 3 4 5 6 7 8 9 10 11 12) op_sorted = op.sort{|a, b| a <=> b } p op_sorted #=> ["1", "10", "11", "12", "2", "3", "4", "5", "6", "7", "8", "9"]
"1" の次に"10" が来ている。"2" ではない。これはおかしい。なぜか。 文字として解釈され、比較されているからだ。これだと辞書式順序になる。 比較を文字列としてではなく、数字として行えばよい。文字列を数字化するto_iメソッドを使う。
op = %w(1 2 3 4 5 6 7 8 9 10 11 12) op_sorted = op.sort{|a, b| a.to_i <=> b.to_i } p op_sorted #=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
これで解決した。
次の問題である。同じ番号で枝番がついている場合がある。たとえば、3-1, 3-2 というように、 ハイフンの後に来るのが枝番だ。 to_i メソッドでは、枝番が無視される。すなわち、3-1 も 3-2 も to_i メソッドでは3に変換され、 整列で必要なブロック付き呼び出しの要件を満たさない。 そこで、to_i メソッドで同じとみなされれば、今度は文字列の比較に切りかえるとよい。 試してみよう。初期配列では、わざと3-1と3-2の順番を逆にしている。
op = %w(1 2 3-2 3-1 4 5 6 7 8 9 10 11 12) op_sorted = op.sort{|a, b| if (a.to_i != b.to_i) a.to_i <=> b.to_i else a <=> b end } p op_sorted #=> ["1", "2", "3-1", "3-2", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
この方法には問題がある。少し後で述べよう。さて、この方法のいいところはもう一つある。 枝番には、ハイフンをつけずに、アルファベットの小文字を入れる方法もある。 これにも適用できる。12b と 12の順序がどうなるか。
op = %w(1 2 3 4 5 6 7 8 9 10 11 12b 12) op_sorted = op.sort{|a, b| if (a.to_i != b.to_i) a.to_i <=> b.to_i else a <=> b end } p op_sorted #=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "12b"]
合格だ。
さて、前記の方法には問題があると指摘した。それは、枝番が二桁になると順序が間違えてしまうことだ。
op = %w(1 2 3-1 3-12 3-2 4 5 6 7 8 9 10 11 12)
op_sorted = op.sort{|a, b|
if (a.to_i != b.to_i)
a.to_i <=> b.to_i
else
a <=> b
end
}
p op_sorted #=> ["1", "2", "3-1", "3-12", "3-2", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
当然、"3-1" の次には "3-2" がこなければならない。 したがって、ハイフンを枝番の区切りとして認識させないといけない。 正規表現を使う方法もあるが、ここでは正規表現はつかわず、文字列のsplitメソッドを使うことにした。 splitメソッドの第1引数によって、文字列が配列に分解されることを利用する。 長いが次のようになる。
op = %w(1 2 3-1 3-12 3-2 4 5 6 7 8 9 10 11 12) op_sorted = op.sort{|a, b| a_arr = a.split('-') b_arr = b.split('-') a0 = a_arr[0].to_i b0 = b_arr[0].to_i a1 = a_arr[1].to_i b1 = b_arr[1].to_i if (a0 != b0) then a0 <=> b0 elsif (a1 != b1) then a1 <=> b1 else a <=> b end } p op_sorted #=> ["1", "2", "3-1", "3-2", "3-12", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
気が利かないプログラムだが、少なくとも何をやっているかはわかるだろう。 後半の条件文 a1 != b1 は、12 と 12b を比較したときに生じる場合のためである。 ハイフンがない場合は a_arr[1] などは nil であり、a_arr[1].to_i は数字の0である。 そこでこの条件文が成立する。この場合には、文字列の比較でよい。
これで、大概の作曲家で使われている作品番号の整列には、ほぼ対応できる。 これで対応できないのは、ハイドンのホーボーケン番号だろう。これは、 ローマ数字-通し番号だからだ。ローマ数字はジャンルに対応する。 例:ピアノソナタ第52番 Hob.XVI-52
まりんきょ学問所 > Ruby の浮き輪 > 作品番号の整列