AtCoderはじめました

Last Update: 2020-01-17

競技プログラミングをはじめた。はじめたと言っても、はじめてではないのだけどね。真面目にコンテスト参加をするようになったというだけである。

これまでも何度かやってみてはいた。しかしどうにも続かなかった。今回はなぜ続かなかったのかと、何がきっかけでやり始めるようになったかを書いておこうと思う。

プログラミングの学習が目的

そもそも私の場合、プログラミングコンテストへ参加するその最大の理由は、自分のスキルアップのためである。スキルアップというか、もっと具体的に言うと、たとえばKotlinの練習だったりするわけである。

というより当初はKotlinスキルアップの題材として、プログラミングコンテストを利用していた。この観点から言うとAtCoderは全く使えないので長続きしなかった。理由としては、AtCoder上のKotlinはバージョンが低すぎるからである。

競技プログラミングにおいて、言語のバージョンはそこまで重要ではないだろう。言語バージョンが上がることによって増える便利な構文などに頼らず実装するのが競技プログラミングだろう。それはわかる。

しかし私の目的からすると、手元で使える書き方が使えずわざわざ古いバージョンでコードを書かないといけないのは本末転倒である。それゆえにAtCoderは続かなかった。

標準入力・出力

競技プログラミングはその殆どが、標準入力から入力を受け取り、標準出力に回答を出力する形式であろう。これが私にとっては障壁となっていた。

その理由は、書いたコードをテストしづらいからだ。

この観点から言うと、LeetCodeは指定されたメソッドを実装する方式だったので、ユニットテストで動作確認がやりやすく問題に挑戦しやすかった。

もっとも、LeetCodeも結局長続きしなかったわけだが。そのわけは、自分の書いたコードの効率の良さとかがさっぱり分からないからだ。コードを提出しても、LeetCodeではすべての提出されたプログラムのうち、あなたのプログラムはこれくらいの処理速度ですよというのが返ってくるだけである。しかもその処理速度も、まったく同じコードなのに処理時間にブレ幅があり、指標として役に立たない。

そして長続きしなかった最大の理由は言語の壁であったが。問題文を理解できずにそもそも土俵にすら立てないこともしばしばである。英語の勉強になるかなぁと思って少しずつやってはいたが、時間がないときに問題文の理解に1時間とか書けてられないという話である。こういうことかなと思って実装しても、テストケースに通らずということも心をくじくには十分であった。

つまりそれらの障壁が取り除かれたわけだ

AtCoderを再開するようになったのは、前述の2つの障壁が取り除かれたからである。

1つ目は私の意識改革である。Kotlinにこだわるのをやめただけの話だけどね。

現在はPythonを使って参加している。Pythonを使っている理由は、2つ目の障壁に関わる。

2つ目の障壁は、Pythonで標準入力・出力をモックしてユニットテストを回せるようになったからである。というよりPythonでこれができるようになったので、AtCoderに取り組みはじめたのである。

Pythonを選んだ理由はそれともう1つ、標準入力からの読み込みが比較的ラクに書けたからである。標準入力からの読み取りが、競技プログラミングをやる上で障壁になってる気がするのは私だけだろうか。普段標準入力からデータを受け取るようなコードを書かないから、競技プログラミングのためだけにその部分で頭を使うのは嫌だったのだ。

その点Pythonはinput()とすれば標準入力から受け取れるから楽である1

私気になります

話は逸れるが、競技プログラミングに参加している人は、コードを実装したあとの動作確認をどうしているのだろうか。競技プログラミングはじめました的な記事はよく見かけるのだが、こういった話まで書いている人はあまり見かけない気がする。

競技プログラミングでは問題文とともにサンプルデータとそれに対する出力例が提示される。まずはこれを使って動作確認をするはずである。私はPythonでユニットテスト環境を整えるまではコードを実行し、標準入力からサンプルデータをコピペして出力をチェックするというめちゃくちゃ面倒くさい方法をとっていた。これがめんどくさすぎて競技プログラミングが長続きしなかったと言っていい。

類まれなる実装力を持っていて、バグなく実装できるとかいう超人なら別にこんなことはどうでもよいのだろう。しかし、私はそこまで実装力に自信があるわけではない。ときにはステップ実行して実装のどこがまずいのか確認しなければコードを完成させることができないこともある。

そのステップ実行をやるたびに、標準入力へ入力値をペーストするのはすごい面倒くさい。だからユニットテストができるようになってから、ようやく出発点に立てたと言ってもいい。

このあたりはみんなどうやっているのだろうか。私気になります。

Pythonでユニットテスト

Pythonでユニットテスト

Stackoverflowにあったコードを使わせてもらった。元コードはこちら

IntteliJ2を使ってやっているので、ついでにFile Templateを使ってさらにボイラープレート部分を減らしている3

実装したコードをユニットテストで回せるので、随分とAtCoderの問題をやりやすくなった。

AtCoder以外でも恩恵が

これまた手を出したのは2年以上前だったりするのだが、AOJを使ってアルゴリズムの勉強もはじめた。こちらもPythonで実装している。標準入力・出力を使ったコードをユニットテストで回せるようになったのが大いに役立っている。

ただ問題を解くだけでは進歩がないと思ったので、アルゴリズムとデータ構造を買ってちゃんと学習することにした。こちらは少しずつ進めているところ。どの程度身についているかはよくわからないのだけども。

AtCoderをやっていて、Dくらいまではなんとかできても、その先となるととんと歯が立たない。こういった本を利用して学習しないとどうしようもないなと思ったのである。

少なくとも三日坊主だけはクリアしている。一日一問くらいの進み具合ではあるが、少しずつ進めていっている。代わりにAndroidアプリ作成まで手が回っていないけれど。

まとめ

本エントリの肝は、Pythonで標準入力・出力を使ったプログラムをユニットテストできるようにしたら、競技プログラミングへの参加ハードルがぐっと下がったという話である。

PythonでできたからPythonでやってるだけなんだが、他の言語でも標準入力・出力を使ったテストの書き方がわかればそっちへ移行というのもありかなと思う。もっともKotlinに関しては、AtCoder側のバージョンが上がらないと選択肢に上がらないが。

他の言語でのテスト方法にも挑戦しようかとも思ったのだが、とりあえずPythonでできたし、Pythonでやってるとこれはこれで楽しくなってきてしまった。というわけで、他の言語での方法を調べるよりも、Python使ってどんどん問題を解いていく方を優先している。

他の言語で自分はこういうふうにしてユニットテスト回してやってるという知見があれば教えてほしいなというのが、今回のエントリを書いた私の下心である。


  1. とはいえinput()がくそ遅いので、これのせいで処理時間内に実行が終わらないこともあったりするので注意が必要だが。

  2. 課金してるから使っているだけである。別にコミュニティ版PyCharmを使えばいいし、何なら他のエディタでもいい。

  3. main関数を定義して、スクリプトとして実行されたらそのmain関数呼び出すようにするとか、ユニットテストの定形部分などをテンプレート使って作成するようにしている。もうちょっとなんとかできんのかという部分もあるのだが、そこを突き詰めるより問題解いていく方が大事だと思って、ある程度の状態で満足することにしている。