はじめに
9 か月くらい前から AtCoder のコンテストに参加しています。とても楽しいです。エンジョイ勢の緑なのであまりガチっぽいことは語れないですが…
僕はとても面倒くさがりで手元に開発環境を構築するのですら億劫だと感じているので、Docker で Jupyter Lab を立ち上げてそこでコンテストの問題を解いてます。そんな僕が面倒くさいなあと感じているのは、問題の入力が複数行にわたるときです。Jupyter は標準入力に一度に入力できるのが 1 行だけなので、複数行の場合だと入力例を 1 行ずつコピペして入力していかないといけません。2 行くらいならまだ我慢できるのですが、10 行とかになるとそれだけで投げ出したくなります。
これまでは我慢しながらやっていたのですが、そろそろ何とかしたいなあと思い、ちょっと考えてみました。所詮は Python のことをよく理解しておらず、かつ実行速度をキリキリにチューニングしないと解けないレベルの問題には手が届かないレベルの弱者が考えた拙いアイディアですが、どなたかのお役に立てたら幸いです。(試していないですが、Google Colab でも同じことができると思います)
本題
実現したいこと
実現したいことは主に以下の 2 点です。
- 同じ Notebook 上で解答と入力を同時に扱いたい(入力を別ファイルから読み込むとかはしたくない)
- Cell の内容をそのままコピペして解答を投稿したい(投稿する直前に部分的にプログラムを書き換えるとかはしたくない)
これが実現できていれば解答の実行時間が多少落ちてもまあ仕方がないかなという考えでいます。
アイディアの説明
「解答部」と「入力部」をそれぞれ別の Cell に記述します。
解答部
以下のプログラムをテンプレートとします。
import sys, os
from collections import deque
# ストリームを引数に指定して呼び出す(引数なしの場合は、標準入力を使う)
def main(f = sys.stdin):
# ストリームの内容をすべて読み込み deque に詰め込む
answer(deque(map(lambda s: s.rstrip(os.linesep), f.readlines())))
def answer(lines):
# ☆★☆ ここに解答の処理を記述する ☆★☆
# 入力は lines(deque)に詰め込んでいるので input() の代わりに lines.popleft() などを使えばよい
# debug という名前の変数が宣言されていなければ、引数なしの main メソッドを呼び出す
if 'debug' not in globals():
main()
ポイントは最後の 2 行です。Jupyter 上で、最初に別の Cell で debug = 1
などのように debug
という名前の変数を定義しておけば main
メソッドが呼び出されなくなります。AtCoder に提出するときは、解答部の Cell だけを丸々コピペすれば debug
という名前の変数が定義されていないことになるので、 main
メソッドが呼び出されて標準入力からテストケースの入力が読み込めるようになります。
入力部
以下のプログラムをテンプレートとします。
import io
main(io.StringIO("""\
# ☆★☆ この中に入力例を丸々コピペする ☆★☆
# 複数行にわたっていてもよい
"""))
# 複数の入力例を同時に試したいときは main メソッドの呼び出しを増やせばよい
main(io.StringIO("""\
# ...
"""))
StringIO を使うとテキストをストリームとして扱うことができます。これを標準入力の代わりとすることで、Jupyter 上で複数行の入力も容易に扱えるようになるという仕掛けです。
実際に試してみた
AtCoder Beginner Contest 194 の B - Job Assignment で試してみました。
(解答内容がショボいのは見逃してください…)
Jupyter 上での動作結果
実際に Jupyter 上で実行した結果をキャプチャしました。debug = 1
の呼び出し、解答部、入力部をそれぞれ別の Cell に分けて上から順番に実行しています。
AtCoder 上での動作結果
解答部の Cell だけコピペして提出してみたところ、期待どおり AC になってくれました。やったぜ。
おわりに
AtCoder 楽しいです! みなさんもぜひやりましょう!