筆者はレート800前後の茶〜緑コーダ
競技プログラミングの際に使っているテンプレを友人に見せたら色々質問をもらったので、この場でまとめて回答する。
使ってるテンプレート
記述についての解説は以下のリンク参照(このアドベントカレンダーの1日目の記事です)
from bisect import bisect_left, bisect_right, insort_left, insort_right
from collections import defaultdict, Counter, deque
from functools import reduce, lru_cache
from itertools import product, accumulate, groupby, combinations
import sys
import os
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rI1(): return (int(sys.stdin.readline().rstrip())-1)
def rLI1(): return list(map(lambda a:int(a)-1,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
IS_LOCAL = int(os.getenv("ATCODER", "0"))==0
err = (lambda *args, **kwargs: print(*args, **kwargs, file=sys.stderr)) if IS_LOCAL else (lambda *args, **kwargs: None)
def main():
# N = rI()
# N, M = rLI()
# H, W = rLI()
# N, M, K = rLI()
# X, Y = rLI()
# A, B = rLI()
# N, X, Y = rLI()
# N, A, B = rLI()
# A, B, C, D = rLI()
# E, F, G, H = rLI()
# A = rLI()
# B = rLI()
# S = rS()
# for _ in range():
# for i in range(N):
# for i,a in enumerate(A,start=1):
# for i,a in enumerate(A):
# S = rS()
# X, Y = rLI()
# A, B = rLI()
# if True:
# print("Yes")
# print("No")
# else:
# print("Yes")
# print("No")
# ans =
# print(ans)
# print(*ans)
# print('Yes' if ans else 'No')
if __name__ == '__main__':
main()
VSCodeでスニペット登録する用
"atcoder": {
"prefix": "hdr",
"body": [
"from bisect import bisect_left, bisect_right, insort_left, insort_right",
"from collections import defaultdict, Counter, deque",
"from functools import reduce, lru_cache",
"from itertools import product, accumulate, groupby, combinations",
"import sys",
"import os",
"def rI(): return int(sys.stdin.readline().rstrip())",
"def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))",
"def rI1(): return (int(sys.stdin.readline().rstrip())-1)",
"def rLI1(): return list(map(lambda a:int(a)-1,sys.stdin.readline().rstrip().split()))",
"def rS(): return sys.stdin.readline().rstrip()",
"def rLS(): return list(sys.stdin.readline().rstrip().split())",
"IS_LOCAL = int(os.getenv(\"ATCODER\", \"0\"))==0",
"err = (lambda *args, **kwargs: print(*args, **kwargs, file=sys.stderr)) if IS_LOCAL else (lambda *args, **kwargs: None)",
"",
"def main():",
" $0# N = rI()",
" # N, M = rLI()",
" # H, W = rLI()",
" # N, M, K = rLI()",
" # X, Y = rLI()",
" # A, B = rLI()",
" # N, X, Y = rLI()",
" # N, A, B = rLI()",
" # A, B, C, D = rLI()",
" # E, F, G, H = rLI()",
" # A = rLI()",
" # B = rLI()",
" # S = rS()",
" ",
" # for _ in range():",
" # for i in range(N):",
" # for i,a in enumerate(A,start=1):",
" # for i,a in enumerate(A):",
" # S = rS()",
" # X, Y = rLI()",
" # A, B = rLI()",
" # if True:",
" # print(\"Yes\")",
" # print(\"No\")",
" # else:",
" # print(\"Yes\")",
" # print(\"No\")",
" ",
" # ans = ",
" # print(ans)",
" # print(*ans)",
" # print('Yes' if ans else 'No')",
" ",
"if __name__ == '__main__':",
" main()",
""
]
}
友人から見た感想(原文)
# 友人Aからみた感想
## 初心者度合い
- 競プロ?何それ?
- OSS文化もあまり分かってない
- 配列全部listで済ませちゃう
## ライブラリ
- `bisect`, `functools`, `itertools`初めてみた
- `collections`はギリギリ知ってた
- 二分探索できる`bisect`, 色々イテレータ返してくれる`itertools`強そう
でも、`functools`ってどう使うの?
## 関数まわり
- `rstrip()`や`split()`のデフォルト値、知らなかった。
- そういえば、`enumerate`ってどう読んでる? えぬれーと? いーなむれーと?
## コメントアウト部分
- 前半部分のN, M, H, W, X, Y とかへの代入が登場頻度多そうなのは分かる。
- `for _ in range():`と`for i in range(N):`分けてる理由は?
回答
競プロ?何それ?
競技プログラミングとは
決められた条件のもとで与えられた問題、課題をプログラミングを用いて解決し、その過程や結果を競うものを競技プログラミングといいます。
様々なジャンルの出題がされますが、プログラミングや思考力、数学力、知識を活用します。
個人的には「プログラミングの授業や教科書に出てくる演習課題」の高難易度版、みたいなイメージ。
OSS文化もあまり分かってない
自分もOSSにコミットした経験はほぼない。
便利なOSSがどうやって作られて回っているのか、仕組みはちゃんと追うと面白いよね。
配列を全部 list で済ませちゃう
わかる。
deque は両端からの追加・削除をよくやるときに便利けど、そうじゃないなら無理に使わなくてもいい。
それと、連想配列まわりだと defaultdict と Counter は覚えるとかなり快適
bisect, functools, itertools 初めてみた
結構便利なので、公式ドキュメントか解説記事を1回眺めるだけでも理解が進むと思う。
collections はギリギリ知ってた
どこで知ったのだろう?
deque はスタック/キューとして使えるから、たまに出番がある(気がする)。
追記:友人が回答してくれた。
型アノテーション用にcollections.abc.Iterableを使ってそこで知った。なんでもいいからループさせる関数の引数として使う。
もしかして、競プロだとこういうチェックはやらない?
だそうです。C++でやる場合は否が応でも型チェックは必要だと思うけどPythonだと型チェック不要だしtypeエラー系はその都度対応で事前チェックは基本的にしないかな。
二分探索できる bisect、色々イテレータ返してくれる itertools 強そう
itertools の accumulate は累積和を作れるので、区間和(L, R)をすばやく計算したいときに便利。
bisect は bisect_left と bisect_right の境界の取り方で混乱しがちなので、
自分は「尺取り法でいけないか?」を先に考えて、無理そうなら頑張って bisect を使う、という運用にしてる。
でも functools ってどう使うの?
lru_cache はメモ化再帰(同じ計算を繰り返さない)で使うことが多い。
reduce は「畳み込み(左から順にまとめる)」で、例えば配列の積をまとめて計算する、みたいな用途。
「引数の一部を固定して別の関数を作る」は functools.partial の役割なので、もしその話ならこっちが近い。
なお、どちらも出番は少なめ
rstrip() や split() のデフォルト値、知らなかった
デフォルト引数って、ドキュメントを見ないと分かりにくいよね。
enumerate ってどう読んでる? えぬれーと? いーなむれーと?
サイトより
「enumerate」の発音は、IPA表記では /ɪˈn(j)uːməreɪt/ であり、カタカナ表記では「イニューメレイト」となる。日本人が発音するカタカナ英語では、「イヌメレート」と読むことが一般的である。
個人的には「イナムレート」か「エナムレート」
前半部分の N, M, H, W, X, Y とかへの代入が登場頻度多そうなのは分かる
基本的に N と A しか使わないことが多いけど、たまに別の形も来るから置いてる。
使わないときは毎回消してる(コメントアウトのままでもいいけど、邪魔なら消す派)。
for _ in range(): と for i in range(N): を分けてる理由は?
前者はループ回数だけ回したいけど、何回目か(添字)を使わない場合。
例えばテストケースが複数ある問題、クエリが Q 回来る問題、などでよく使う。
後者は添字 i を使って A[i] を見たい場合。
「i番目とその前後を見る」「A[i], B[i] を使って何かする」みたいな問題で出番が多い。
感想
まずは、貴重な意見をくれた友人に感謝。
「何も知らない人はどこで引っかかるか」が見えて、かなりいい学びになった。
あと、説明しようとすると自分の理解の浅い部分とかでてくるので、いいチェックにもなった。