INDEX
■はじめに
Rubyの基礎的な問題をたくさん解くことで基本的な考え方やメソッドの使い方を定着させたい。
基本的にはAtCoderというプログラミングコンテスト(競技プログラミング)の過去問を使う。(AtCoderは難易度が分かれており、難易度の低いA問題かB問題を解いていく)
■問題
●出典
AtCoder Beginner Contest 027のA問題
https://atcoder.jp/contests/abc027/tasks/abc027_a
●問題文
ある長方形の 3 つの辺の長さが与えられる。 残り 1 つの辺の長さを求めよ。
●入力
入力は以下の形式で標準入力から与えられる。
l1 l2 l3
1 行目には、長方形の 3 つの辺の長さを表す整数 l1 , l2 , l3 (1≦l1 ,l2 ,l3 ≦10) が与えられる。
●出力
長方形の残り 1 つの辺の長さを出力せよ。 ただし、答えはちょうど 1 つ存在することが保証される。 出力の末尾には改行を入れること。
■回答
●愚直に書く
重複した2つを削除すれば良いかなと思ったけど、意外とどうやれば良いのかわからない。
思いついたプランとしては、uniq
でまず重複した2つを1つにして、残りの2つを掛け算して、重複した数値で割れば、求める数値が出るはず。
と思ったけど、3つが全部同じ数字の場合に破綻するな…。3つが全部同じ場合は求める数字も3つの同じ数字と同じになるから、それだけ条件分岐するか。冗長になりそうだけど…。
l = gets.split.map(&:to_i)
if l[0] == l[1] && l[0] == l[2]
puts l.uniq[0]
else
uniq_num = l.group_by{|i| i}.reject{|k,v| v.one?}.keys
puts l.uniq[0] * l.uniq[1] / uniq_num.join.to_i
end
通った!
「重複する値を取り出す」については、見たこともない出し方を偶然見つけたので使わせていただきました。
https://spirits.appirits.com/doruby/9197/?cn-reloaded=1
●リファクタリング/別アプローチ
もっと断然シンプルな方法が絶対ありそう…。
重複している2つを取り除けばそれで終わるんだけど、どうやるのかな。
l = gets.split.map(&:to_i)
if l[0] == l[1] && l[0] == l[2]
puts l.uniq[0]
else
uniq_num = l.group_by{|i| i}.reject{|k,v| v.one?}.keys
l.delete(uniq_num.join.to_i)
puts l
end
これも通った。が、全然シンプルになってない…。理解できていない「おまじないコード」を使わない方法はないものか。あと3つとも同じ場合の条件分岐も、可能であればしたくない。
なんか違う方法を思いついた。
要素は3つだけなので、
- 1番目と2番目が同じ数字なら3番目の数字を表示
- 1番目と3番目が同じ数字なら2番目の数字を表示
- 2番目と3番目が同じ数字なら1番目の数字を表示
この分岐だけで行けるのでは…!?
しかも3番目の条件はelse
にしちゃってよさそう。
l = gets.split.map(&:to_i)
if l[0] == l[1]
puts l[2]
elsif l[0] == l[2]
puts l[1]
else
puts l[0]
end
通った!!嬉しい。
もうちょいシンプルに書けるのかな。今日はこれでギブアップ。
●他の方の回答例
1位の人の回答、
p eval`tr " " ^`
嘘でしょ笑。何が書いてあるのか全然わからない…。
もう少し下の方を見てみると以下のような感じ。
a,b,c=gets.split.map(&:to_i).sort
p a==b ? c : a
あ〜なるほど、自分の書いたコードが断然シンプルになっている。
1番目と2番目が等しければ3番目を表示だし、等しくなければ1番目を表示すればOKということになるのか。
sortをしているのがキモ(sortしないと、等しくない場合に2番目になってしまう可能性があるから)
思いつかなかったのが悔しい!
●出てきたメソッド等
公式リファレンスを見る訓練。
-
Array#uniq
https://docs.ruby-lang.org/ja/latest/method/Array/i/uniq.html -
Array#delete
https://docs.ruby-lang.org/ja/latest/method/Array/i/delete.html
delete
は、破壊的メソッド。前も出てきたけど忘れていた。
■振り返りなど
上記の過程の部分でたくさん書いているので振り返りは特に無し。
すごく勉強になった。
■5/17追記
フィヨルドブートキャンプの先輩受講生であるはるぐちさんに別解を教えていただいた。
a, b, c = gets.split.map(&:to_i).sort
puts a * c / b
これもsort
がキモ。sort
することで、a
は必ず短辺になり、c
は必ず長辺になる。
なのでa * c
で長方形の面積が出て、それをb
で割ることで求めたい数値が出る、ということになる。
そもそも今回は面積の問題なので、面積的に解くというのはすごく綺麗でしっくりきた。