AtCoderとは
AtCoderとは競技プログラミングのコンテストを行っているサービスです。良質なプログラミングの問題が沢山提供されています。またRubyistとはRubyを書く人の事で私もRubyistです。今回はRubyistがAtCoderに取り組む姿勢についてまとめてみました。
といっても私は偉そうにプログラミングを語れる程の技術力がある訳ではないです。なので今回はRubyistがAtCoderを利用することで何を得られるか、どう向き合っていくべきなのかという事を自分なりの目線で勝手にまとめてみたという記事です。
RubyistがAtCoderに取り組む意味
まず先に断っておくと私がAtCoderに取り組んでいる目的は競プロで良い成績を残したいからではありません。そもそもパフォーマンスで他の言語に劣るRubyは競プロには向いていません。
※もちろん少ない記述でコードを書けるRubyは大好きですよ
私がAtCoderに取り組んでいる目的はRubyを使って様々な問題を解くことでRubyに対する理解を深めるためです。AtCoderで問題を解いているとそれまで知らなかったメソッドを知れたり思いがけない発見があったり多くのことを学べます。そしてRubyによる課題解決の訓練をたくさん積むということは確実に仕事にも活きてきます。場数を踏んだエンジニア達は面構えが違います。
人の回答を読む
AtCoderで問題を解いてみると分かりますがコードを提出すると即採点してくれます。ただ、その結果が合格だからといってそれで終わらせてしまうのは勿体ないです。大事なことは先にも言いましたが、Rubyの理解度を高める事です。他のRubyist達がどんな解き方をしているか見ることは非常に勉強になります。これは自分の知らないメソッドだけじゃなくアルゴリズムや考え方など概念レベルで学べる事が多くあり、コーディング以外の副産物も得られます。
コードを書くこととコードを読むことは表裏一体です。コードを書くのが速い人はコードを読むのも速く、逆にコードを書くのが遅い人は大抵読むのも遅いです。人のコードを読む能力は書く能力と同じぐらい重要なことです。
テストを書け
AtCoderを続けていればいつか必ず提出したコードが不合格になります。その時優秀な貴方ならきっとこう思うでしょう。「何故不合格なんだ、落ちたテストサンプル見せろや」と。お気持ちはよく分かるのですが、しかしこの考え方はエンジニアの姿勢として正しくありません。何故なら「テストサンプルくらい手前で作って自力でバグを見つけろや」が世のエンジニア達の一般論だからです。
もしかするとこの記事の中で一番伝えたかった事はこれかもしれません。どんなに小さな機能でもテストを書きましょう。どんな機能でもテストを書けるということはそれ自体が高度な技術です。テストを書く事はベストプラクティスです。また、テストを書く事に慣れるほど仕事の開発速度・精度が向上し必ず幸せになります。
これはつい最近の話ですが兄弟会社からあるシステムの機能拡張を依頼されましたが、こいつがとんでもなくクソコードでした。恐らく様々な人達が手を加えたのであろう無秩序で煩雑なコードで書かれた無数のファイル、もといゴミの山がそこにありました。勿論テストは一切ありません。
一応は要件通りの機能を追加しました。ですが果たして今回の変更が既存の機能に影響が出ないと言い切れますでしょうか。無理でした。システム全てを把握している時間は無かったですし、何より作成者との連絡が取れなかったので。そもそもバグの上に機能を追加するような状況でしたからとりあえずリリースして出たとこ勝負です。
こういう時テストさえきちんと書いていればどこで不具合が発生しているかの切り分けが楽になるのです。切り分けがスムーズにいけばデバッグに掛かる時間も短縮され、精神的にも余裕が生まれます。面倒でもテストは書きましょう。AtCoderでテスト力も身につけてられたらエンジニアとしてもっと上に行けるはずです。
とりあえず一問解いてみる
まずは一問解いてみましょう。AtCoderのチュートリアルのような問題です。
あなたは、500 円玉を A 枚、100 円玉を B 枚、50 円玉を CC 枚持っています。 これらの硬貨の中から何枚かを選び、合計金額をちょうど X 円にする方法は何通りありますか。
同じ種類の硬貨どうしは区別できません。2 通りの硬貨の選び方は、ある種類の硬貨についてその硬貨を選ぶ枚数が異なるとき区別されます。0≤A,B,C≤50
https://atcoder.jp/contests/abs
A+B+C≥1
50≤X≤20,000
A,B,Cは整数である
Xは50の倍数である
まずはテスト駆動開発に則ってテストを書きます。RubyなのでRSpecで書いています。
require 'rails_helper'
load 'app/just_total.rb'
describe JustTotal do
describe '#just_pattern_num' do
let(:test_class) { Struct.new(:hoge) { include JustTotal } }
let(:just_total) { test_class.new }
it 'is correct response' do
expect(just_total.just_pattern_num(30, 40, 50, 6000)).to eq 213
end
end
end
順を追って説明すると、これは後で作るJustTotalモジュールのjust_pattern_num
メソッドのテストを書いています。モジュールのテストにはStruct構造体クラスを使うと書きやすいです。5行目でJustTotalモジュールをインクルードしたStruct構造体クラスのサブクラスを作成しています。コンストラクタに:hoge
というインスタンス引数を持つとしているのは、何か引数を渡さないとエラーになるからです。
そして6行目ではサブクラスのインスタンスを作成しています。このjust_total
インスタンスは先程インクルードしたJustTotalの中で定義しているメソッドを呼び出せます。そのメソッドとは後ほど記述しますが、500・100・50円玉の数A, B, CとX円を受け取り、金額の合計が丁度X円になるパターンを返すメソッドであると言えます。
テストデータはAtCoderの出力例を入力しています。今回は手打ちですがこのテストサンプルも本来考慮すべき対象です。
次に実際のモジュールを書いてみます。
module JustTotal
def just_pattern_num(a, b, c, x)
i, j, k = 0, 0, 0
count = 0
while i <= a
while j <= b
while k <= c
if (i * 500) + (j * 100) + (k * 50) == x then
count += 1
break
end
k += 1
end
k = 0; j += 1;
end
k = 0; j = 0; i += 1;
end
p count
end
end
読んで理解出来ると思いますが、硬貨それぞれの0枚のときから上限までのパターンを総当りで評価しています。X円と合致する組み合わせが見つかったらカウントして、最後に出力しています。下記のコマンドでテストを実行してみて下さい。
$ rspec spec/just_total_spec.rb
エラーが出なければ成功です。何度も繰り返しで申し訳ないですが面倒でもテストを書く癖は付けたほうが良いです。
続けること
競プロの問題と聞くと敷居が高く感じるかもしれませんが、AtCoderはむしろ気軽に参加出来ます。絶対に全ての問題を解かないといけないとか赤色を目指すといった高い志を持つのを否定はしませんが、仕事前の素振りのような感覚で毎日コツコツ取り組む方が長続きして結果的にRubyistとして成長出来ている気がします。
出来るだけ多くの回答を見て使えると思った書き方をどんどん吸収すること、そしてテストも書くという事を半年くらい継続するといつの間にかRuby力が最初の頃には想像もつかなかったレベルに到達しています。エンジニアリングとはそういうものです。逆説的に言えば日々の積み重ねでしか成長出来ません。
プログラミングの問題を解き続けることは簡単ではないですが、緩く継続しましょう。そうすればいつか大輪の花が咲きます。