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

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

https://qiita.com/advent-calendar/2021/pre-competitive

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

前の日の記事は[こちら](https://qiita.com/malleroid/items/ab83b5ffb8ddfd58a4d3)
次の日の記事は[こちら](https://qiita.com/Luke02561/items/abfb7b010dafd0c76665)

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

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

https://qiita.com/superrino130/items/951ce53ada31f6dfe124

https://qiita.com/uhooi/items/f490d14602a7bc114948#_reference-b3c3f3b9f0ab47b60972

# こんなやらかししたことない…？
～AtCoderコンテスト中～
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/fcc35ee5-d71a-e937-ef3a-42f24d381f68.png)
([ABC228B問題より](https://atcoder.jp/contests/abc228/tasks/abc228_b))

(ふむふむ…こんな感じかな…？）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/8a799e43-d9a9-300b-b349-7b41fb6d6794.png)

(よし、テストケースを試してみよう)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/d6b2615f-5915-b0f4-ad44-d36e1c2995d7.png)
(おりゃ！)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/6e992f51-411c-4ac9-1fd8-eba66654adbf.png)

(あれ！？出力例と一致しないぞ！)
(printを挿入して処理が思い通りか確認してみよう)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/cfcf8ac2-b380-380e-fdae-264a88164caf.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/0db68a7f-f0e3-adb7-0250-8a5f216ec14f.png)

(えーっと…2つ目のインデックスが最初に参照されて…って問題文は1-indexで実装は0-indexなのにindexを変換していない！)
(入力を0-indexに変換しよう)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/7816ecf3-c49e-2c16-e9df-4ab8701c8c71.png)
(テストしてみて…)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/394b8684-42b6-583f-393e-2d2142db7476.png)
(よしっ！思ったとおりに動いてるし、出力も正しいぞ！)
(他のサンプルケースも一致したから提出しよう！)

(ポチッ)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/d9343d6a-d14c-55b4-7e20-53664853b105.png)

![wa (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/d91b535f-7f64-0c74-51a5-8f9e5ad97354.png)

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

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


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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/ed4eee49-840a-6bb2-7899-9f6413d1e76b.png)

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

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

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

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

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

コードテストで確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/0649d8ed-a68b-8213-3f4f-cbf2a0f73800.png)



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

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

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

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

例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/4d769500-9129-2edc-df9f-fca901492790.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/f7d1d694-aa23-0965-b4b3-05e6991bccaa.png)

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

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

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

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

```python
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/58140d0f-8e43-5f8f-071a-9351371565bf.png)

結果(Pypy3 (7.3.0))
![Screenshot from 2021-12-10 13-50-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/907208/c5f40ec8-da34-dbe3-46ec-dff8f278fd46.png)

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

# 最後に

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