LoginSignup
2
0

More than 3 years have passed since last update.

Ruby カラオケマシン問題 解いてみた(解答例あり)

Last updated at Posted at 2020-05-27

はじめに

『プロを目指す人のためのRuby入門』通称チェリー本を学習後のプログラミング初心者です。
インプットしたものを手を動かして実践してみたいなと思ったら、作者の記事を見つけました。
「アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた(全10問)」

この二つ目の問題を解いてみました。

一問目:カレンダー作成問題(たのしいRuby 練習問題)
二問目:カラオケマシン作成問題
三問目:ビンゴカード作成問題
四問目:ボーナスドリンク問題
五問目:電話帳作成問題

問題

詳しくは実際の問題文をみてください。
問題知ってる人は、解答例へジャンプ

<前提>

カラオケにはキーを変える機能があります。
+1するとキーが1つ上がります。
-1するとキーが1つ下がります。
たとえば「ドレミファソ」というメロディのキーを2つ上げると「レミファ#ソラ」になります。
image.png
「ドレミファソ」のようにカタカナだとプログラムで扱いづらいので、英語の読み方、つまりアルファベットに置き換えましょう。
ド レ ミ ファ ソ → C D E F G
レ ミ ファ# ソ ラ → D E F# G A

<本題>
かえるのうたのメロディは以下のような文字列で表現されます。
これを(+6)や(-11)など指定大きさ分キーを変えます。

"C D E F |E D C   |E F G A |G F E   |C   C   |C   C   |CCDDEEFF|E D C   "

実行例はこんな感じになります。

melody = "C D E F |E D C   |E F G A |G F E   |C   C   |C   C   |CCDDEEFF|E D C   "
karaoke = KaraokeMachine.new(melody)
karaoke.transpose(2)
# => "D E F# G |F# E D   |F# G A B |A G F#   |D   D   |D   D   |DDEEF#F#GG|F# E D   "
karaoke.transpose(-1)
# => "B C# D# E |D# C# B   |D# E F# G# |F# E D#   |B   B   |B   B   |BBC#C#D#D#EE|D# C# B   "
# 1オクターブ(12音)以上変えることもできる
karaoke.transpose(14)
# => "D E F# G |F# E D   |F# G A B |A G F#   |D   D   |D   D   |DDEEF#F#GG|F# E D   "

解答例

こんな感じになりました。

class KaraokeMachine

  def initialize(melody)
    @melody = melody
  end

  def transpose(amount)
    scales = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
    @melody.split(/(\w#?)/).map{
            |m| /(\w#?)/.match?(m) ?
            scales[(scales.find_index(m) + amount) % scales.length] : m 
           }.join
  end
end

当初下記のコードで完成したと思ってたのですが、

class KaraokeMachine

  def initialize(melody)
    @melody = melody
  end

  def transpose(amount)
    scales = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
    @melody.split("").map{
                |m| m == " " || m == "|" ?
                 m : scales[(scales.find_index(m) + amount) % scales.length]
               }.join
  end
end

これだと下記のようなメロディが来たときにアルファベット#が分離してしまうことに気付き正規表現を用いて修正しました。

melody = "F# G# A# B |A# G# F#   |A# B C# D# |C# B A#   |F#   F#   |F#   F#   |F#F#G#G#A#A#BB|A# G# F#   "

解説

自分の備忘録としても書いておきます。

この問題での音階はシャープも入れると「ドド#レレ#ミファファ#ソソ#ララ#シ」の繰り返しなので,

  scales = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]`

このように一つの配列にしました。

melody =で受け取った文字列を、音階それ以外に区切り、配列にします。

melody = "C D E F |E D C   |E F G A |G F E   |C   C   |C   C   |CCDDEEFF|E D C   "
melody.split(/(\w#?)/)
=> ["", "C", " ", "D", " ", "E", " ", "F", " |", "E", " ", "D", " ", "C", "   |", "E", " ", "F", " ", "G", " ", "A", " |", "G", " ", "F", " ", "E", "   |", "C", "   ", "C", "   |", "C", "   ", "C", "   |", "C", "", "C", "", "D", "", "D", "", "E", "", "E", "", "F", "", "F", "|", "E", " ", "D", " ", "C", "   "]

配列の各要素をmapメソッドのブロック内で条件の真偽値を求めぞれぞれの値を、新しい配列に追加していきます。
1. 音階なら指定された分キーを変更する。
2. それ以外ならそのままの要素を追加。
を次のコードで実行していきます。

@melody.split(/(\w#?)/).map{
          |m|  /(\w#?)/.match?(m) ?
          scales[(scales.find_index(m) + amount) % scales.length] : m 
         }.join

音階indexと指定されたキーの増減数を足した数をscaleslengthで割ると、キーを変更した先の対応する音階indexが求められます。
ですので、scales[ ]の値に代入しindexを指定すればキー変更をした音階がわかります。

最後にこの音階それ以外をもう一度joinして文字列に直せば完成です。

さいごに

若干強引気味になってしまったところもあるかなと思います。
何か間違いやご意見あれば教えてください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0