このプログでは、AtCoder で出題された問題を紹介しています。この記事では、AtCoder の問題を解く際に採用している C++ と Python について、コーディング方針をまとめました。
AtCodet については、こちらで紹介しています。
コーディングの考え方
読みやすさを配慮する
AtCoder は、競技プログラミングのサイトです。課題を解くプログラムを1ファイルで書く必要があります。また、時間内にコーディングしなければなりません。
そのため、プログラムの可読性、再利用性は犠牲になりやすい傾向があります。そのような制約がある中で、読みやすいプログラムにするため、以下の方針を取っています。
- 独自のマクロは採用しない。
競技性を高めていくと、標準にはないコードパターンをあらかじめ書いておいて、解答時に冒頭に貼り付けるような対応が必要になってきます。わたしが解説する範囲は、そこまでの競技性が求められていないと考えています。 - 行数やタイプ量が増えることを気にしない。
タイプ量を減らすコーディングをする場合があります。C/C++ では、for 文の本体は複文にする必要がない場合は、複文(波カッコで囲む)にしない、などです。このブログでは、タイプ量が多少増えても可読性が高くなる書き方を選ぶことにします。前述の for 文については、本文の量に関係なく複文で書いています。 - ただし、コメントは記さない。
制限時間がある競技プログラミングで、コメントを記すのは現実的ではありません。このブログでは、コメントではなく記事として解説するため、コードにコメントを記さない方針とします。
[2023/5/2追記] 記事本体に書くと冗長になる場合、コメントを許容することにします。
命名規則について
採用している命名規則を紹介します。
[2023/5/2追記] 2点変更しました(GとLの扱い)。
- 関数名や変数名には、小文字と下線で表現するスネークケースを採用する。ただし、グラフを表す配列は、G を使うことにする(ある時期まではeを使っていた)。
- 変数名は、可能な限り問題文の表現を使う。ただし、問題文で大文字で表現されている文字は、小文字にする。例)N → n
ただし、L は小文字にすると 1 と誤認する可能性があるため、L は許容する。 - ループカウンタは、支障がない限り、i、j、k を使う。もし、4重ループ以上となる場合は、l(Lの小文字)ではなく、m 以降を使う。これは、l(Lの小文字)は、数字の 1 と誤認しやすいためである。
- 最後に出力する変数名は、自然な限り result とする。ただし、この命名規則には例外が多い。
C++ コーディング方針
冒頭で、bits/stdc++.h をインクルードする。
bits/stdc++.h をインクルードしています。これにより、CおよびC++の標準ライブラリに含まれるヘッダファイルをすべてインクルードすることになります。
AtCoder の GCC 環境では、このヘッダファイルを使うことができます。また、AtCoder が提供している C++入門でも、このヘッダファイルをインクルードしています。
関係するヘッダファイルに限ってインクルードするほうが好ましいという考えもありますが、競技プログラミングにおける利便性を優先しました。
using namespace std; を記載する。
大規模なソフトウェア開発では、using namespace std; は、好ましくないと考えられています。一方、AtCoder の問題を解くプログラムは、1ファイルであるため、デメリットが少なくなります。std:: を省略できるため、すっきりと書けるというメリットを優先しました。
rep マクロは使わない。
for 文をミスなく簡潔に書くために、以下の rep マクロを定義して使う場合があります。
#define rep(i, n) for (int i = 0; i < (int)(n); ++i)
このマクロはよくできています。一方、マクロで構文拡張を行うのは、好ましくないことと、C++ としての標準ではないため、このブログのプログラムでは採用しないことにしました。(C++ の標準としては、C++11 から範囲 for 文を導入しています。)
32ビットより大きな整数は、long long int 型とする。
AtCoder の C/C++ 環境では、long int がすでに64ビットになっていました。これは、コードテストのページでプログラムを実行することにより調べることができます。
このブログの方針で、32ビットより大きな整数を使いたい場合は、long long int を使うことにしています。その場合、型名が長くなるため、以下の typedef で型名の宣言をします。プログラムでは、型名として ull(符号無の場合)もしくは ll(符号有の場合)を使います。
typedef unsigned long long int ull;
typedef long long int ll;
C++の整数の大きさは、多くの場合Cと同じです。C の整数の大きさについてのまとめ記事は、こちらにあります。
main の書き方
main については、以下の書き方をしています。
- main 関数開始の左波カッコは、独立な行に書く。
Java 風に、main と同じ行に左波カッコを書く流儀もありますが、C/C++ で伝統的に使われている書き方を採用しました。 - return を必ず記す。
C++ では、main 関数で returm が省略できます(return 0 と同じ振る舞いをします)。わたしは、戻り値の型を宣言している関数で、return を書かないことが、非常に気持ち悪く感じていて、reutrn を記しています。競技プログラミングでは、エラーを検出することはないため、返す値は0となります。
ここまで紹介したコーディング方針を盛り込んだ C++ プログラムは以下となります。
#include <bits/stdc++.h>
using namespace std;
int main()
{
return 0;
}
Python コーディング方針
C++ と異なり、Python はコーディングガイドラインが整備されているため、書き方のばらつきは少ないです。
PEP 8 に従う。
Python では、コーディングガイドラインとしてPEP 8(日本語版)が普及しています。主なガイドは以下です(詳細はリンク先をご覧ください)。
- インデントは、タブではなく半角スペース4個を使う。
- 演算子の前後、コンマの後は、半角スペースを1個入れる。
- import は、ファイル冒頭で記して、プログラム本体とは1行の空行で分ける。
- def で関数を定義する場合、前後に2行の空行を入れる。
Python はガイドラインのチェックツールも充実しています。わたしは、flake8 と pylint を使っています。flake8 については、指摘が出力されないことを確認しています。pylint については、指摘 0 件を実現することが難しい場合、可能な指摘のみ対応するようにしています。
ファイル単位でのみ docstring を付ける
docstring については、競技イベントの前に記載することができるファイル冒頭のみ記載しています。関数冒頭に docstring を記載することは、競技イベント中では難しいと判断しました。
ここまで紹介したコーディング方針を盛り込むと、次のようなプログラムになります。Practice contest 問題Aの解答例です。
"""AtCoder practice contest A"""
a = int(input())
b, c = map(int, input().split())
s = input()
print(a + b + c, s)
最後に
AtCoder の記事で使っているコーディング方針を紹介しました。
競技プログラミングの場合、速く書くことが求められています。一方、読みにくいプログラムを書くことも避けたいと考えています。
ここで紹介したのは、速く書くことと後から読んで分かることを両立させたマイルールです。マイルールなので、方針を変えることがあるかもしれません。その場合は、この記事を更新して紹介することにします。