LoginSignup
9

More than 5 years have passed since last update.

簡潔なコードを書くために

Last updated at Posted at 2017-12-15

まえがき

 最近得た知見を書いていきます。簡潔なコードを書くことは競技プログラミングの本質とは離れますが、綺麗に書けた方が精神衛生上よろしいかと思います。内容は主にC++の標準ライブラリの紹介です。ABCのA, B問題早解きのための技術みたいなところがあるので、プロにとっては物足りない内容かもしれません。


2点間の距離

x, y平面上の点A(x1, y1)と点B(x2, y2)間の距離を求める。

void main() {
    int x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    // 方法1
    cout << sqrt( pow(x1 - x2, 2) + pow(y1 - y2, 2) ) << endl;

    // 方法2
    cout << hypot(x1 - x2, y1 - y2) << endl;

    return 0;
}
  • 方法1は、$ \sqrt{(x1 - x2)^2 + (y1 - y2)^2} $ をそのままコードにしたもの
  • 方法2は、hypot(x間の距離, y間の距離)のようにしている。処理内容は方法1と同じだがhypot()関数を使った方が楽に書ける。

文字カウント

文字列Sが与えられたとき、その文字列に文字'a'がいくつ含まれるかを求める

int main() {
    string s;
    cin >> s;

    // 方法1
    int aCount = 0;    
    for (char c : s) {
        if (c == 'a') {
            aCount++;
        }
    }
    cout << aCount << endl;

    // 方法2
    cout << count(s.begin(), s.end(), 'a') << endl;

    return 0;
}
  • 方法1は、文字列のループを回して'a'が出てきたらカウント変数をインクリメントしている。

  • 方法2は、count()関数を使用している。この関数を使えば1行で表現できる。

  • 更に、count()関数は以下 のように条件分岐にも組み込む事ができるので便利。

  if ( count(s.begin(), s.end(), 'a') >= 5) { 
    // 文字列sに文字'a'が5つ以上含まれるときの処理
  }
  • count()関数はstringだけでなく、vectorなどにも使える。

数値判定

ある文字が数字であるかを判定する。数字ならYes、そうでないならNoを出力させる。

int main() {
    char c;
    cin >> c;

    // 方法1
    cout << ((c >= '0' && c <= '9') ? "Yes" : "No") << endl;

    // 方法2
    cout << (isdigit(c) ? "Yes" : "No") << endl;

    return 0;
}
  • 方法1ではASCIIコードで数字が0, 1, 2, ..., 9 といった順番に並んでいることを利用して条件を書いている。
  • 方法2ではisdigit() 関数を使用している。行っている処理は方法1と同じだが、isdigit()を使った方が簡潔なコードが書ける。

複数の値の中から最小値を選ぶ

3つの値a, b, c の中の最小値を出力する。

int main() {
    int a, b, c;
    cin >> a >> b >> c;

    // 方法1
    cout << min(a, min(b, c)) << endl;

    // 方法2
    cout << min({a, b, c}) << endl;

    return 0;
}
  • 方法1はmin()関数の中にmin()関数を書いていて、なんだか見づらい。たまにこんな感じのコードを書いている人がいるので取り上げてみた。
  • 方法2について。min({a, b, c, ...}) を使えば、複数の値の中で最小値を見つけるコードを簡潔に書ける。
  • もちろん最大値を求めるためにmax({a, b, c, ...})もできる。

正規表現

最後の正規表現は、CODE FESTIVAL 2017 FinalのA問題を例に出して考えます。

問題文

文字列 S が与えられます。

高橋君はこの文字列の好きな位置に好きなだけ文字 'A' を挿入することができます。

S を 'AKIHABARA'に変えることはできるでしょうか?

パターンを考えてみる

上記の条件に当てはまるパターンは'AKIHABARA'、'AKIHABAR'、'AKIHABRA'...あー、これめんどくさいやつだーってなると思います。全パターンをコードに埋め込むこともできますが、ミスしやすいし、なにより同じような内容の文字列を書くのがクッソめんどくさいです。

正規表現を使う

Pythonとかを使ってる人にはなじみのあるものかもしれません。C++の正規表現はC++11から取り入れられました。正規表現を使って書くと以下のようなコードになります。

int main() {
    string s;
    cin >> s;

    if ( regex_match(s, regex("A?KIHA?BA?RA?")) ) {
        puts("YES");
    } else {
        puts("NO");
    }

    return 0;
}

regex_match(検索対象文字列, regex(正規表現のパターン文字列)) みたいに使います。上記コードの"A?KIHA?BA?RA?"中にある?が正規表現独自の文法のようなものです。?は直前にあるものが0回、もしくは1回出現した場合にマッチさせるようにします。なので、この問題を解くのに正規表現はうってつけだというわけです。

正規表現は奥が深いため、ここで詳細を説明するのは避けます。初めて正規表現を見る人はよく分らないかもしれませんが、正規表現がめちゃくちゃ便利なものだということはわかったと思います。

紹介した関数まとめ

関数名 機能
hypot(x, y) xの2乗+yの2乗の平方根を計算する
count(first, last, value) firstからlastの区間内のvalueの個数を計算する
isdigit(c) 文字cが数字であるかを判定する
min({a, b, c, ...,z}) a~zの中で一番小さい数値を求める
regex_match(str, regex(regex_str)) 指定した文字列str全体が正規表現文字列regex_strにマッチした場合、trueを返す。
マッチしなければfalseを返す。

詳しくはC++の公式リファレンスを見た方が早いです。

さいごに

いかがでしたでしょうか?記事を書くのが初めてのため、見づらい記事になってしまったかもしれません。。。「この記事のミスを見つけた!」とか、「もっと簡潔に書く方法があるよ!」などの意見をいただけるとうれしいです。
さいごに、ここまでお付き合い頂きありがとうございました!!気が向いたらまた記事を書いてみようと思います。

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
What you can do with signing up
9