作品番号の整列

作成日: 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 の浮き輪 > 作品番号の整列


MARUYAMA Satosi