ぶちのブログ

競プロとCTFが趣味なWebエンジニアのアウトプットの場

「アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた」のをワンライナーっぽく解いてみた(前半)

はじめに

こちらの記事の問題が結構楽しそうだと思ったので解いてみました。

blog.jnito.com

最初は普通に書いていたのですが、リファクタリングしていくうちにどんどんワンライナーに寄ってきたので、かなりワンライナーっぽくなっています。
全10問中8問まで解いてあり、4問目まで紹介します。

コード

1. カレンダー作成問題

今月のカレンダーを文字列として返すクラスを作成する。

require 'Date'

class Calendar
  def self.generate
    [
      Date.today.strftime('%b %Y').center(20),
      [
        *Date::DAYNAMES.each_with_object(0..1).map(&:slice),
        *Array.new(Date.today.cwday % 7),
        *1..Date.new(Time.now.year, Time.now.month, -1).day
      ].map(&:to_s).each_with_object(2).map(&:rjust)
       .each_slice(7).each_with_object(' ').map(&:join)
    ].join("\n")
  end
end

軽く解説すると、

[
  *Date::DAYNAMES.each_with_object(0..1).map(&:slice),
  *Array.new(Date.today.cwday % 7),
  *1..Date.new(Time.now.year, Time.now.month, -1).day
]

ここの返り値が[mo, tu, ..(曜日部分).. , nil, nil, ..(曜日のズレの数だけnil).. , 1, 2, 3, ..(日付)..]という1次元配列になっている。
それを.map(&:to_s).each_with_object(2).map(&:rjust)で2文字の右寄せの文字列に変換し、 .each_slice(7).each_with_object(' ').map(&:join)で7個ずつ連結した文字列にしている。

2. カラオケマシン問題

仕様はこちら

class KaraokeMachine
  KEYS = %w[A A# B C C# D D# E F F# G G#].freeze

  def initialize(melody)
    @melody = melody
  end

  def transpose(amount)
    @melody.gsub(/[A-G]#?/) { |k| KEYS[(KEYS.index(k) + amount) % 12] }
  end
end

配列を作っておいて、うまくmodを取ってずらしてやるだけ。

3. ビンゴカード作成問題

仕様はこちら

class Bingo
  def self.generate_card
    [
      'BINGO'.chars,
      *(1..75).each_slice(15).each_with_object(5).map(&:sample).transpose.tap {
       |nums| nums[2][2] = nil
      }
    ].map do |row|
      row.map(&:to_s).each_with_object(2).map(&:rjust).join(' | ')
    end.join("\n")
  end
end

カレンダーと似た発想で解く。 (1..75).each_slice(15).each_with_object(5).map(&:sample).transpose.tap { |nums| nums[2][2] = nil } で、いい感じに数字が入った二次元配列ができる。tapメソッドを使って、メソッドチェーン中で真ん中の数字をnilにしているのがRubyっぽい。

4. ボーナスドリンク問題

仕様はこちら

class BonusDrink
  def self.total_count_for(amount)
    (amount - 1) / 2 + amount
  end
end

再帰のようで、実は閉じた式で表せる。

後編に続きます。