LoginSignup
60
21

More than 1 year has passed since last update.

競プロでのデバッグを標準エラー出力で行っているという話

Last updated at Posted at 2021-12-05

はじめに

この記事は競技プログラミングを始めたばかりの人に伝えたいことアドベントカレンダー6日目の記事です!

アドベントカレンダー初めてです!始めたばかりの人向けということで軽い内容をお届けします。

前の日の記事はこちら
次の日の記事はこちら

追記

@mkawa2さんにコメントにて、AtCoderだけではなくcodeforcesとyukicoderでも使えるという報告をいただきました!

この記事ではPythonのみを取り扱っていますが@superrino130さんがRuby、@uhooiさんがSwiftでの実装の記事を書いてくれました。

こんなやらかししたことない…?

~AtCoderコンテスト中~
image.png
(ABC228B問題より)

(ふむふむ…こんな感じかな…?)
image.png

(よし、テストケースを試してみよう)
image.png
(おりゃ!)
image.png

(あれ!?出力例と一致しないぞ!)
(printを挿入して処理が思い通りか確認してみよう)
image.png
image.png

(えーっと…2つ目のインデックスが最初に参照されて…って問題文は1-indexで実装は0-indexなのにindexを変換していない!)
(入力を0-indexに変換しよう)
image.png
(テストしてみて…)
image.png
(よしっ!思ったとおりに動いてるし、出力も正しいぞ!)
(他のサンプルケースも一致したから提出しよう!)

(ポチッ)
image.png

wa (1).png

(あれっ!!??WA!!??あっっ!!デバッグのprint消すの忘れてた!!!)

ということやったことありませんか?僕は今までに5回くらいやった気がします()
このミスを標準エラー出力を使うことによって回避しようということです。

標準エラー出力でデバッグをしよう

まず標準エラー出力というのは簡単に言うとエラーが出力されるところです。
例えば先程の例の入力を数字以外にするとエラーは次のようになります。

image.png

AtCoderのコードテストは標準出力と標準エラー出力が別れていてどちらがどちらかわかりやすいですね。
このようにエラーが標準エラー出力に出力されていることがわかります。

AtCoderのジャッジでは標準出力のみを読み取っており標準エラー出力は無視されます。つまり、デバッグのための出力を標準エラー出力に出力すれば消し忘れによるWAは起こりえません(TLEはありえる)。

Pythonでの標準エラー出力のしかた

Pythonでは標準エラー出力は以下のようにします。

import sys
print("error", file=sys.stderr)

コードテストで確認できます。
image.png

記述しやすいように関数化しよう

毎回上記のように書くのはめんどくさい!と思ったので関数化しました。

import sys
def error(*args, end="\n"): print(*args, end=end, file=sys.stderr)

この関数をプログラムの一番上とかに入れておけばerror関数をprint関数を使用するのと同じ要領で標準エラー出力に記述することができます。


image.png

image.png

*argsってなんやねんって思った人。関数の引数の前に*をつけると引数何個投げてきてもいいよって意味になります。受け取った引数はタプルとして保持します。逆にprint(*args)のように関数を呼び出すときはタプルのargsの一つ一つの要素を引数として渡してねって意味になります。似ているものとして**kwargsというものもあります。もっと詳しく知りたい人はPythonの可変長引数(*args, **kwargs)の使い方 | note.nkmk.me等で調べてみてください。

追記、WAは大丈夫だけどTLEには気をつけなければいけない

この記事内でもTLEは起こり得ると書きましたが、コメントでも@YTOKさんに指摘されましたので実際どの程度気をつけなければいけないのかちゃんと調べました。
passするだけのforループと標準エラー出力をするforループを実行して実行時間を確認しました。

コードです(見なくていいです)

import sys
import time
def error(*args, end="\n"): print(*args, end=end, file=sys.stderr)

print("標準エラー出力なし")
for i in [5, 6, 7]:
    N = 10 ** i
    t1 = time.time()
    for _ in range(N):
        pass
    print(f"10^{i} : {time.time() - t1:.4f} s")

print("標準エラー出力あり")
for i in [5, 6, 7]:
    N = 10 ** i
    t1 = time.time()
    for j in range(N):
         error(j)
    print(f"10^{i} : {time.time() - t1:.4f} s")

結果(Python (3.8.2))
Screenshot from 2021-12-10 13-50-49.png

結果(Pypy3 (7.3.0))
Screenshot from 2021-12-10 13-50-34.png

この結果をまとめますと、
10^7以上 → 完全アウト
10^6   → 複数入れると高確率でTLE、消すほうが無難
10^5以下 → 気にする必要なし
という感じです!

最後に

需要があるのか謎ですが、便利!使お!って思ってくれたら幸いです。
あと、もっといい方法とか関連してやってること等あればぜひ教えて下さい。

60
21
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
60
21