0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ruby初心者向けプログラミング問題10選を解いてみた(下)

Last updated at Posted at 2021-01-25

Ruby初心者向けプログラミング問題10選を解いてみた(上)の続きです
引き続き自分の回答を貼っていきますが、2問解けてないです(へなちょこ)

素晴らしき元サイト様

アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた(全10問)

国民の祝日.csv パースプログラム (回答無し)

問題文

問題文(折りたたみ)
>その昔、「国民の祝日.csv」という扱いづらいCSVが話題になっていました。 具体的にはこんなCSVファイルです↓ > ``` 平成28年(2016年),,平成29年(2017年),,平成30年(2018年), 名称,月日,名称,月日,名称,月日 元日,2016/1/1,元日,2017/1/1,元日,2018/1/1 成人の日,2016/1/11,成人の日,2017/1/9,成人の日,2018/1/8 建国記念の日,2016/2/11,建国記念の日,2017/2/11,建国記念の日,2018/2/11 春分の日,2016/3/20,春分の日,2017/3/20,春分の日,2018/3/21 昭和の日,2016/4/29,昭和の日,2017/4/29,昭和の日,2018/4/29 憲法記念日,2016/5/3,憲法記念日,2017/5/3,憲法記念日,2018/5/3 みどりの日,2016/5/4,みどりの日,2017/5/4,みどりの日,2018/5/4 こどもの日,2016/5/5,こどもの日,2017/5/5,こどもの日,2018/5/5 海の日,2016/7/18,海の日,2017/7/17,海の日,2018/7/16 山の日,2016/8/11,山の日,2017/8/11,山の日,2018/8/11 敬老の日,2016/9/19,敬老の日,2017/9/18,敬老の日,2018/9/17 秋分の日,2016/9/22,秋分の日,2017/9/23,秋分の日,2018/9/23 体育の日,2016/10/10,体育の日,2017/10/9,体育の日,2018/10/8 文化の日,2016/11/3,文化の日,2017/11/3,文化の日,2018/11/3 勤労感謝の日,2016/11/23,勤労感謝の日,2017/11/23,勤労感謝の日,2018/11/23 天皇誕生日,2016/12/23,天皇誕生日,2017/12/23,天皇誕生日,2018/12/23 ,,,,, 月日は表示するアプリケーションによって形式が異なる場合があります。,,,,, ``` >(中略) >これをいい感じにパースして、以下のようなデータ構造(Rubyのハッシュオブジェクト)に変換しよう、というプログラミング問題です。 > ```Ruby { 2016 => { # 実際のキーは文字列ではなくDateオブジェクト '2016/01/01' => '元日', '2016/01/11' => '成人の日', # ... '2016/11/23' => '勤労感謝の日', '2016/12/23' => '天皇誕生日', }, 2017 => { '2017/01/01' => '元日', '2017/01/09' => '成人の日', # ... '2017/11/23' => '勤労感謝の日', '2017/12/23' => '天皇誕生日', }, 2018 => { '2018/01/01' => '元日', '2018/01/08' => '成人の日', # ... '2018/11/23' => '勤労感謝の日', '2018/12/23' => '天皇誕生日', }, } ```

コード

csvの扱い方が全くわからなかったので解けなかったです…
代わりと言ってはなんですが本家の模範解答のURLを貼っておきます
【短命に終わった】国民の祝日.csvをパースして変換するRubyプログラムとコード解説動画

要点解説

解説動画です
【短命に終わった】国民の祝日.csvをパースして変換するRubyプログラムとコード解説

「Rubyで英語記事に含まれてる英単語を数えて出現数順にソートする」問題

問題文

問題文(折りたたみ)
>テキストファイルの中から英熟語や英単語を抜き出してカウントする、という問題です。

入力に使うテキストファイルはこんな感じです。

Interior design and decorating resource Houzz is the overall best Android app of the year, according to Google, which this evening announced the results of the first-ever Google Play Awards at its developer conference, Google I/O. 
(略)

出力結果はこんな感じになります。

単語数(熟語以外):331
英熟語?------------------------------------------------------------------
  2 Google I/O
  2 Google Play Awards
  1 And Google
  1 Best App
  1 Best Game
  1 Best of
  1 Best Standout Startup
  1 Best Use of Google Play Game Services
  1 Best Use of Material Design
  (略)
英単語------------------------------------------------------------------
 22 the
 11 and
 11 of
  8 a
  6 apps
  5 app
  5 best
  5 for
  5 Google
  5 to
  4 that
  4 this
  (略)

コード

*かなりゴチャゴチャしてます
*熟語の判定がガバガバなので本家と出力結果が違います

file_pass = # 任意のパス
sentence = File.read(file_pass)
words = []
idioms = []

# 英熟語を抜き出す
while (idiom = sentence.slice!(/[A-Z]\w*(?:[\/\s-]+[A-Z]\w*)+/))
  idioms << idiom
end
idioms_hash = idioms.tally

# 記号系を消す
while (s = sentence.slice!("’s"))
  words << s
end
sentence.gsub!(/[,.“”?!;:ー"–]/, " ")

# 単語を抜き出す
words << sentence.split
words.flatten!

words_hash =  Hash.new(0)
words.each { |word| words_hash[word.downcase] += 1}

# ソート
idioms_hash = idioms_hash.sort_by { |idiom, freq| [-freq, idiom] }.to_h
words_hash = words_hash.sort_by { |word, freq| [-freq, word] }.to_h

# 出力
puts "単語数 : #{words.count + idioms.count}"
puts "英熟語?-----------------------------------------------------------------"
idioms_hash.each { |word, num| puts "#{num} #{word}" }
puts "英単語------------------------------------------------------------------"
words_hash.each { |word, num| puts "#{num} #{word}" }

要点解説

while (idiom = sentence.slice!(/[A-Z][\w]*(?:[\/\s-]+[A-Z][\w]*)+/))
  idioms << idiom
end

(idiom = sentence.slice!(/[A-Z][\w]*(?:[\/\s-]+[A-Z][\w]*)+/))は条件式の中で警告を出さずに変数代入を行う書き方です(カッコを外すと警告が出ます)

idioms_hash = idioms_hash.sort_by { |idiom, freq| [-freq, idiom] }.to_h
words_hash = words_hash.sort_by { |word, freq| [-freq, word] }.to_h

sort_byメソッドでは、レシーバの各要素を<=>で比較します
配列を<=>で比較すると、各要素を順番に比較し0でない値が出た時その値を返します
なので、[-freq, idiom]と書くとまずfreqの値でソートし、もし同じ値だったらidiomをアルファベット順に並べる、ということができます

sortの結果は配列になっているのでto_hでハッシュに戻します(執筆中に気づきましたがここは配列のままでも動作するみたいです)

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

問題文

問題文(折りたたみ)
>行単位、列単位で合計値を出しましょう、という問題です。

たとえばこういうインプットであれば、

- col1 col2 col3 col4
row1 9 85 92 20
row2 68 25 80 55
row3 43 96 71 73
row4 43 19 20 87
row5 95 66 73 62

こういう結果になります。

- col1 col2 col3 col4 sum
row1 9 85 92 20 206
row2 68 25 80 55 228
row3 43 96 71 73 283
row4 43 19 20 87 169
row5 95 66 73 62 296
sum 258 291 336 297 1182

ただし、出題元のブログでは以下のような仕様になっていました。

  • ランダムに数字を出力する
  • 計算結果は以下のようなフォーマットで出力する
   9|  75|  83|  74| 241
   0|  27|  32|  48| 107
  51|  66|  76|   3| 196
   2|  37|  69|  85| 193
  55|  40|  25|  88| 208
 117| 245| 285| 298| 945

コード

class SumMatrix
  class << self
    def sum
      matrix = generete_matrix
      matrix = sum_matrix(matrix)
      format_matrix(matrix)
    end

    def generete_matrix
      Array.new(5) { Array.new(4) { rand(100) } }
    end

    def sum_matrix(matrix)
      2.times do
        matrix.each { |ary| ary << ary.sum }
        matrix = matrix.transpose
      end
      matrix
    end

    def format_matrix(matrix)
      matrix.each { |elem| elem.map! { |x| x.to_s.rjust(4) } }
      matrix.map! { |elem| elem.join("|") }
      matrix.join("\n")
    end
  end
end

要点解説

class << self

endまでで定義したメソッドがクラスメソッドとして定義されます
ネストは増えるけど便利

      Array.new(5) { Array.new(4) { rand(100) } }

Array.newは第1引数に要素数、第2引数に中身を指定してデフォルト値(?)を作れます
しかし、出来上がった配列の中身はすべて同じオブジェクトを参照しているのでうまく動きません
なので別々のオブジェクトを与えるためブロックを使っています

また、常に重複した値が出る可能性があるようにしています
このコードを教えてくださったコメント欄の@scivolaさんに感謝

      matrix.each { |elem| elem.map! { |x| x.to_s.rjust(4) } }

例えば[1,2], [3, 4][[" 1", " 2"], [" 3", " 4"]]にします
ここはもうちょっといい書き方があった気がします

ガラケー文字入力問題

問題文

問題文(折りたたみ)
>英語のガラケーでは「2」キーを2回押すと「b」になり、「3」キーを3回押すと「f」になります。 >ただし、この問題では特別ルールとして「0」で文字を確定させます。

たとえば、このプログラムに対して"440330555055506660"を入力すると、"hello"が返ってきます。

コード

*keitai_messageの引数はStringとして取っていますが、これはIntegerとするとメソッド呼び出しのとき8進法の数と解釈されてエラーを起こすことがるからです
*入力を10進法のリテラルにするのも違うかな、と考えこうしました

CHARSES = {
  "1" => %w(. , ! ? \ ),
  "2" => %w(a b c),
  "3" => %w(d e f),
  "4" => %w(g h i),
  "5" => %w(j k l),
  "6" => %w(m n o),
  "7" => %w(p q r s),
  "8" => %w(t u v),
  "9" => %w(w x y z)
}.freeze

def keitai_message(message)
  nums = message.scan(/([123456789]+)0/).flatten
  return if nums.empty?

  nums.map! do |num|
    chars = CHARSES[num.chr]
    index = (num.size % chars.size) - 1
    chars[index]
  end
  nums.join
end

要点解説

nums = message.scan(/([123456789]+)0/).flatten

scanの引数の正規表現にカッコが含まれる場合、カッコの内側にマッチした文字列が配列になって返ります
つまり、"01022"は[["1"], ["22"]]になるわけですね

値札分割問題 (回答無し)

問題文

問題文(折りたたみ)
>「値段らしき文字列」を「数字部分」と「単位部分」に分割する、split_priceメソッドを作ってください、というプログラミング問題です。 > ```Ruby split_price '110.0万円' #=> ['110.0', '万円'] split_price '2015円' #=> ['2015', '円'] split_price '1,123,456円' #=> ['1,123,456', '円'] split_price '110 - 120万円' #=> ['110 - 120', '万円'] split_price '2015円' #=> ['2015', '円'] split_price '価格未定' #=> ['価格未定', ''] split_price nil #=> ['', ''] ```

コード

全然わかりませんでした…!
なので本家の模範解答のURLを貼っておきます
【Rubyプログラミング問題】値札分割メソッド(split_price)を作成してください:解答編

おわり

以上、「Ruby初心者向けプログラミング問題10選を解いてみた」でした
ここまで読んでいただき本当にありがとうございました!

0
0
8

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?