見出し画像

プログラマーがプログラム中に考えていること

先日書いたコードです。
と言っても Python チュートリアルをC言語に焼き直しただけですど。

プログラムは、次のようなもの。

1000 より小さい素数を全て列挙して標準出力に出力する

こじんまりとしたプログラムです。
Python で書くとコメント込みで 20 行。
C言語で 33 行( { や } だけの行が 12 行!)。

これが初版です。

#include <stdio.h>
#include <stdbool.h>

bool is_prime(int number)
{
    int i;
    for (i = 2; i <= (number/2); i++)
    {
        if ((number % i) == 0) {return false;}
    }
    return true;
}

int iter_primes(int prime)
{
    prime++;
    while (!is_prime(prime))
    {
        prime++;
    }
    
    return prime;
}

int main()
{
    int prime = 2;
    while (prime < 1000)
    {
        printf("%d\n", prime);
        prime = iter_primes(prime);
    }
    
    return 0;
}

例え小さな関数でも関数の設計であることにかわりはありません。この「関数の設計」というのは極めて重要です。保守性、再利用性、汎用性、堅牢性、可読性、等々、あらゆる品質に関係していると言ってもいい。

そこで!

私がこれらを設計するときに考えていたことを書きつらねてみようと思います。思考の文字化(笑)。

「is_prime」の引数「number」

型は「int」でいいかな。「unsigned」(符号なし)にすれば倍の大きさの数値まで対応できるけど。「unsigned」は文字列が長いんだよなぁ。今回はそんなに大きな素数はいらないし、そもそも「見せる」コードだし、見やすさ優先! 「int」にしておこう。

ループカウンタ「i」

「i」! C言語で書くとループカウンタはなんでも「i」になっちゃって。あー、でもここは意味ある単語にするのは難しいか。i でもループすることはわかるし。何より短いし。「i」のままにしよ。

(number/2)

うーん、for 文に入れるときはいつも悩む。上限チェックするたびに除算するんだろうか。何回割り算すんねんっていう感じやけど。先に計算済ませておく? うー、そんなことしたらまた変数いるし。最適化でなんとかしてほしいなぁ。今回は「シンプルコード優先」につきこのまま。

素数判定

割りきれる数値があるかどうか、総当たりチェック? 自分より小さい「素数だけ」をチェックするのでもいいねんけど。素数リストを作りながらやる? でも、それだと自分より小さい数の素数判定が終わっていないといけない。制約大きいな。他に使われへんやん。そもそも、それで処理速度が速くなるのか。チェック回数は減るだろうけど。昨今はコンパイラの最適化だったりキャッシュだったりで予測し難い。まずはシンプルコードで。

{return false;}

途中リターン。お片付け忘れない? ないね。OK。

関数「iter_primes」

引数の値からチェックするか、1つ加算した値からチェックするか。こういうところで結構悩む。素数を順に列挙する場合は、1つ加算がいいかな。作った素数をどんどん入れていけばいいだけやし。

「is_prime」のチェック

どれがいいやろ。
(1)!is_prime
(2)is_prime == false
(3)is_prime != true
最近の静的解析は、「==」や「!=」などを使えと、やたらにうるさいのよね。
(3)は、「true = 0以外」だったらなんとなくややこしいな。<stdbool.h> では true=1 のようだけど。とりあえず、(3)はなし。
(1)も(2)も大差ないか。ちょっと静的解析に逆らってみる? なら(1)。

引数「prime」

そのまま使うか。const つけるか。ただの「int」型変数に「const」つける意味、あるんかな。「const」も使うとコードが長くなって。ま、いいや、これで。

関数「main」

1000以上の素数を表示したくないのよ。

「main」その1

    while (prime < 1000)
    {
        prime = iter_primes(prime);
        printf("%d\n", prime);
    }

こうしたいけど、こうすると1000以上の素数が1つ表示されてしまうのよ。

だからこうした。

「main」その2

    while (prime < 1000)
    {
        printf("%d\n", prime);
        prime = iter_primes(prime);
    }

でもこうすると、最初の素数は無条件に出力することになるし。最初の素数が「2」はわかってるけど、これは「iter_primes」を通さずに自己判定。うーん。「2」だけ特別扱い。好きじゃないけど。ならこうする?

「main」その3

    int prime = 1;
    while (true)
    {
        prime = iter_primes(prime);
        if (1000 <= prime)
        {
            break;
        }
        printf("%d\n", prime);
    }

・・・。
なんか・・・。
長い・・・。

「main」その2にしよっと。

(「2」を特別扱いしたその歪さが後々に不具合の元となる。だが、この時の私には知るよしもなかった・・・)

この記事が気に入ったらサポートをしてみませんか?