「Ruby初心者向けのプログラミング問題」を解いてみる

blog.jnito.comやってみました。

カレンダー作成問題

ここで似たようなことをやっているので省略。(ただし、Date クラスは使っていません。)
 

カラオケマシン問題

class KaraokeMachine
  Key = %w(C C# D D# E F F# G G# A A# B)
  def initialize(melody)
    @scaned = melody.scan(/C#|D#|F#|G#|A#|C|D|E|F|G|A|B| |\|/)
  end
  
  def transpose(n)
    @scaned.map do |k|
      idx = Key.index(k)
      idx ? Key[(idx + n) % Key.size] : k
    end.join
  end
end

 

ビンゴカード作成問題

stock = 5.times.map {|i| (i * 15 + 1..(i + 1) * 15).to_a.shuffle}
table = 5.times.map do
  5.times.map {|i| stock[i].shift}
end
table[2][2] = ""
table = ([%w(B I N G O)] + table).map do |row|
  row.map {|x| "%2s" % x}.join(" | ")
end
puts table

出力例。

 B |  I |  N |  G |  O
15 | 28 | 32 | 58 | 68
 8 | 27 | 35 | 49 | 73
12 | 18 |    | 47 | 64
 1 | 21 | 42 | 56 | 62
 3 | 25 | 38 | 52 | 75

 

ボーナスドリンク問題

class BonusDrink
  def self.total_count_for(amount)
    drink = ->(left, drinked) {
      return drinked if left.zero?
      i = left / 3
      d = i.zero? ? left : i * 3    #今回飲む本数
      drink.(left - d + i, drinked + d)
    }
    drink.(amount, 0)
  end
end

puts BonusDrink.total_count_for(100)    #=>149

再帰で解いています。
残りが3本より少なければそのまま飲みます。3本以上なら、3の倍数本だけ飲めるだけ飲んで、(いま飲んだ本数÷3)本だけ追加します。残り0本ならば終了します。
 

電話帳作成問題

class NameIndex
  ADan = %w(ア カ サ タ ナ ハ マ ヤ ラ ワ ン)
  def self.create_index(names)
    table = Hash.new([])
    names.each do |name|
      ADan.each_cons(2) do |be, ed|
        table[be] += [name] if (be...ed).include?(name[0])
      end
    end
    table.each_value {|v| v.sort!}
    table.map {|k, v| [k, v]}.sort
  end
end

NameIndex.create_index(['キシモト', 'イトウ', 'ババ', 'カネダ', 'ワダ', 'ハマダ'])

 

行単位、列単位で合計値を求めるプログラム

module SumMatix
  extend self
  
  def output(input)
    result = calc(input)
    length = result.last.map {|x| x.to_s.size}
    result.map do |row|
      row.map.with_index {|x, i| x.to_s.rjust(length[i])}.join("| ")
    end
  end
  
  def calc(input)
    tmp = input.map {|row| row + [row.sum]}
    last = tmp.first.each_index.map do |i|
      tmp.map {|row| row[i]}.sum
    end
    tmp + [last]
  end
end

実行例。

input = [
    [ 9, 85, 92, 20],
    [68, 25, 80, 55],
    [43, 96, 71, 73],
    [43, 19, 20, 87],
    [95, 66, 73, 62]
]
puts SumMatix.output(input)

  9|  85|  92|  20|  206
 68|  25|  80|  55|  228
 43|  96|  71|  73|  283
 43|  19|  20|  87|  169
 95|  66|  73|  62|  296
258| 291| 336| 297| 1182

 

ガラケー文字入力問題

module KeitaiMessage
  Allocation = [%W(. , ! ? #{" "}), %W(a b c), %W(d e f), %W(g h i), %W(j k l),
                %W(m n o), %W(p q r s), %W(t u v), %W(w x y z)]
  def self.key_in(input)
    result = ""
    input.chars.chunk_while {|i, j| i == j}.reject {|x| x[0] == "0"}.each do |line|
      group = Allocation[line.first.to_i - 1]
      result += group[(line.size - 1) % group.size]
    end
    result = result[0...-1] if input[-1] != "0"
    result
  end
end

puts KeitaiMessage.key_in("440330555055506660")    #=>hello

正しく問題のとおりにするなら、入力が例えば "5" の場合、何も出力されてはいけませんが、ここではじつは空文字列が出力されます。ただ元の仕様は美しくないので、まあいいかということにしてしまいました。正しく修正するなら、メソッドの外ですることになります。

Enumerable#chunk_while は見たことがないという人もいるかもしれませんが、意外と有用なメソッドだと思います。
 

終りに

国民の祝日.csv パースプログラム」と「値札分割問題」は問題の定義がわかりにくかったので解いていません。
全体的にそんなにむずかしくはないですね。すなおな回答を心がけました。