商品等のご購入はAmazonがおすすめ

プログラミングの常識にとらわれると思わぬ罠にハマるかも?

プログラミング

プログラミングにはいくつかの常識?のようなものがあります。

時折それらがプログラマーの邪魔をすることがあるのでそれをいくつか紹介したいと思います。

ログ出力

ログは重要で必要なのは明白ですが、過ぎたログ出力はプログラムのパフォーマンスを落としたりします。たとえば、

#include <stdio.h>

unsigned long long display_sequentially(unsigned long long stop){
    unsigned long long sum = 0;
    for(unsigned long long i = 1; i <= stop; i++){
        sum += i;
        printf("%lld\r", sum);
    }
    puts("");
    return sum;
}

int main(){
    display_sequentially(1000000);
    return 0;
}

このように膨大な数の計算を逐次表示しようとすると、

異常に時間がかかります。しかし、以下のように必要最低限の表示のみにすると、

#include <stdio.h>

unsigned long long not_display_sequentially(unsigned long long stop){
    unsigned long long sum = 0;
    for(unsigned long long i = 1; i <= stop; i++){
        sum += i;
    }
    return sum;
}

int main(){
    printf("%lld\n", not_display_sequentially(1000000));
    return 0;
}

何十倍も早くなりました。

I/O処理はプログラムにとってかなり重い処理だというのがわかるかと思います。

コメント

個人かチームかに関わらず、コメント(何をするコードなのか等)を残すのはプログラミングにおける常識です。

しかし、何でもかんでもコメントで説明するのは可読性を下げる行為となります。

極端な例ですが、

{
    // aに1を足す
    a += 1;
    // bから1引く
    b -= 1;
    // cを2倍にする
    c *= 2;
    ...
}

このようなわかりきったことを書いているだけで何の説明にもなっていないコメントは不要です。

また、

int get_max_num(int *list)
{
    int max_num = -INFINITY;
    for (; *list; list++)
    {
        if(*list > max_num)
            max_num = *list;
    }
    
    return max_num;
}

このような関数があったとします。

  • 返り値が整数型
  • 関数名が「get_max_num」だから何かしらの最大値を得る関数っぽい
  • 引数が整数型のポインタで名前が「list」だからたぶん配列を受け取っている
  • for文で配列の要素を1つずつ確認してより大きい値を「max_num」に代入している
  • 結果、配列内の最大値を返している

といったことが関数名、変数名などから容易にわかるかと思います。

このような場合も私はコメント不要はだと思います。

自明なものの説明をわざわざ行うのは見る側にも書く側にも面倒です。

ただし、

int funcA(int *a)
{
    int n = -99;
    for (; *a; a++)
    {
        if(*a > n)
            n = *a;
    }
    
    return n;
}

このように関数名、変数名に意味がなく、下手に変更すると全体のどこに影響するかわからない、みたいな状況のときは丁寧なコメントを添えてあげましょう。

しかし、開発現場によってコーディング規約は異るので、それに従いましょう

goto文

goto文とはプログラムのあらゆる場所に何の制限もなく移動する処理のことです。

その性質上、乱用するとわけがわからなくなるため、ほとんどのエンジニアは使用を忌避し、現場によってはgoto文禁止のコーディング規約が設けてあったり、そもそもgoto文をサポートしていないプログラミング言語もあります。

しかし、私はわかりやすく書くのならOKだと思う派です。

例えば、多重ループ処理から抜け出す場合、

    bool flag = false;
    for(i = 0; i < 1000; i++){
        for(j = 0; j < 1000; j++){
            for(k = 0; k < 1000; k++){
                if(i == 50 && j == 400 && k == 222){
                    flag = true;
                    break;
                }
            }
            if(flag)
                break;
        }
        if(flag)
            break;
    }

このようにフラグを用いて抜け出さないと行けない場合があります。

わざわざループ毎にフラグの判定をするのも、誤差の範囲とはいえ判定処理の分速度に影響するするのもなんだかスマートじゃないと思いませんか?

私は思います。

これをgoto文を使うと、

    for(i = 0; i < 1000; i++){
        for(j = 0; j < 1000; j++){
            for(k = 0; k < 1000; k++){
                if(i == 50 && j == 400 && k == 222){
                    goto end;
                }
            }
        }
    }
end:

このようにスマートに書くことができます。

「関数化してreturnすればgoto使わなくても同じことできるんじゃない?」とかは言っちゃだめです。

まとめ

プログラミングにおける常識やタブーも経緯や内容を理解し、可読性可搬性可用性 などなどを保った上でメリットがあればヤッちゃってもいいと思います。

コメント

モバイルバージョンを終了
タイトルとURLをコピーしました