3

したがって、これはもう 1 つの「優れた」プログラミング練習問題です。少し調べてみましたが、このようなものは、ほんの数語で定義するのが難しいことがよくあります.

質問に対して: 専門的な観点から、コードを合理化して短くする (必ずしも効率的であるとは限りません) か、インスタンス変数を明示的に定義してそれらを割り当ててすぐに返す方が良いプログラミング方法ですか? 例えば:

FILE * foo(){
    FILE * retVal; 
    foo2(); 
    retVal = foobar(); 
    return retVal;
}

foobar上記から、が a を返すことがすぐにわかりますFILE *。したがって、このスタイルのプログラミングから、重要な情報をより迅速に抽出できます。これは、次のようなものと比較して当てはまります。

FILE * foo(){
    foo2(); 
    return foobar(); 
}

もちろん、これは同じことを達成します。ただし、同じ情報を見つけるには、より深く調べる必要があります。私は単純に見栄えが良いという理由で、後者のスタイルのプログラミングを好む傾向があります。このプログラムの実行方法の性質上、どちらを選択してもメモリが必要なため、どちらを使用してもすぐにパフォーマンスが向上するとは思えません。違いは、ユーザーまたはコンパイラがメモリを割り当てるかどうかです。

コードを短く簡潔にする別の例:

int foo(){
    int i = 0;     
    while(foobar())
        i++:    
    return i;
}

TL:DR 質問>> 何が行われているかを明示的に示した方が良いですか、それとも簡潔さと簡潔さを優先して、同じタスクを実行するが必ずしもパフォーマンスが向上しないコードを短くしても問題ありませんか?

4

6 に答える 6

3

正確なコードと短縮されたコードのどちらを選択するかは、予測している理由により主観的です。メンテナンスに関して言えば、私たちの大半は短いコードを好みます。学習者でさえ、好むべきものとは逆であるという事実にもかかわらず、短いコードを好むでしょう。

C 言語は、人間が判読できるように設計されており、できるだけ少ない労力でコンパイルできるように設計されています。それは手続き型で、非常に華美なものではありません。読みやすさを優先し、時間の消費を抑えるためにコーディングするもう 1 つの理由。


この例で提供した両方の方法で、まったく同じ ASM コードが生成されます ( に注意してください-O)。

            .Ltext0:
                    .globl  foobar
                foobar:
                .LFB13:
                    .cfi_startproc
0000 B8000000       movl    $0, %eax
     00
0005 C3             ret
                    .cfi_endproc
                .LFE13:
                    .section    .rodata.str1.1,"aMS",@progbits,1
                .LC0:
0000 666F6F32       .string "foo2 called"
     2063616C 
     6C656400 
                    .text
                    .globl  foo2
                foo2:
                .LFB14:
                    .cfi_startproc
0006 4883EC08       subq    $8, %rsp
                    .cfi_def_cfa_offset 16
000a BF000000       movl    $.LC0, %edi
     00
000f E8000000       call    puts
     00
                .LVL0:
0014 B8000000       movl    $0, %eax
     00
0019 4883C408       addq    $8, %rsp
                    .cfi_def_cfa_offset 8
001d C3             ret
                    .cfi_endproc
                .LFE14:
                    .globl  foo
                foo:
                .LFB15:
                    .cfi_startproc
001e 4883EC08       subq    $8, %rsp
                    .cfi_def_cfa_offset 16
0022 B8000000       movl    $0, %eax
     00
0027 E8000000       call    foo2
     00
                .LVL1:
002c B8000000       movl    $0, %eax
     00
0031 4883C408       addq    $8, %rsp
                    .cfi_def_cfa_offset 8
0035 C3             ret
                    .cfi_endproc
                .LFE15:
                    .globl  main
                main:
                .LFB16:
                    .cfi_startproc
0036 4883EC08       subq    $8, %rsp
                    .cfi_def_cfa_offset 16
                .LBB8:
                .LBB9:
003a B8000000       movl    $0, %eax
     00
003f E8000000       call    foo2
     00
                .LVL2:
                .LBE9:
                .LBE8:
0044 B8000000       movl    $0, %eax
     00
0049 4883C408       addq    $8, %rsp
                    .cfi_def_cfa_offset 8
004d C3             ret
                    .cfi_endproc
                .LFE16:
                .Letext0:

..あなたのミニマルで取るに足らない簡潔な方法簡潔な方法の応答で.


このことを考えると、両方を正しく適用することが最善であると私は自由に言うことができます. そして、それは.. できるだけ簡潔で明確であり、

/* COMMENTED */
于 2015-06-12T13:22:06.513 に答える
2

免責事項: 以下に記載されているものは、標準からのものではありません。

通常、適切な最適化がオンになっていると、コンパイラは冗長部分またはデッド部分のほとんどを最適化し、バイナリを可能な限り効率的にします。

それを念頭に置いて、人間にとって簡単に理解できるコードを書くことをお勧めします。最適化部分は (ほとんど) コンパイラーに任せます。

人間にとってより理解しやすいコードを書くことで、

  • 他人にもっと受け入れられる
  • メンテナンスが容易
  • デバッグが容易
  • 最後になりましたが、あなたの命の恩人です (駄洒落楽しい意図)
于 2015-06-12T13:12:01.200 に答える
0

短縮版

明らかでない情報を追加する場合にのみ、新しい変数を導入します。通常、変数がやや複雑な式を置き換えるために使用される場合がこれに該当します。

ロングバージョン

すべての場合と同様に、それは依存します。これにアプローチする最善の方法は、状況の費用便益分析を行うことだと思います。

中間変数を使用するコスト(純粋にコードの品質/理解の観点から、適切な最新のコンパイラーはそれらを最適化すると確信しています)は、変数を解析し、コンテキストで定義を理解するなどの努力です。重要なのは、その変数の後者の使用を、コードを読んでいる人が心に留めているプログラムの作業モデルに関連付けることです。

利点は、新しい要素がより多くの情報を導入することで、読者がコード ベースのより単純なメンタル モデルまたはより正確なモデルを形成するのに役立つことです。また、新しい変数宣言の場合、ほとんどの情報は型または名前に含まれています。

たとえば、次の 2 つの例を考えてみましょう。

if(isSocial)
     return map[*std::min(d.begin(),d.end())].first;
else
     return map[*std::max(d.begin(),d.end())].first;
return idealAge;


if(isSocial)
     int closestPersonAge = map[*std::min(d.begin(),d.end())].first;
     idealAge = closestPersonAge
else
     int futhestPersonAge = map[*std::max(d.begin(),d.end())].first;
     idealAge = futhestPersonAge
return idealAge;

最初の例では、読者は std::min が何をするか、「d」と「map」とは何か、それらの型は何か、map の要素の型は何かなどを理解する必要があります。意味のある変数名を使用することで、基本的に読み手は計算を理解する必要がなくなり、同じ量の重要な情報をほぼ維持しながら、コードのより単純なメンタル モデルを持つことができます。

今それを比較してください:

int personAge = person.age();
return personAge;

この場合、personAge は意味のある情報を追加しないと思います (変数とメソッド名は同じ情報を伝えます)。

于 2015-06-12T14:30:56.280 に答える
0

コードを読みやすくすることに同意します。ただし、最初の方が読みやすく、維持しやすいという意見には同意しません。

  • 読みやすさ: 読んで理解できるコードが増えます。例の場合、これはそれほど難しいことではないかもしれませんが、より複雑な型の場合はそうかもしれません。
  • 保守性: 戻り値の型を変更する場合は、retval 宣言も変更する必要があります。

多くのコーディング スタイルでは、ブロックの先頭で変数を定義する必要があります。それらのいくつかは、最初は機能レベルでのみ許可しています。したがって、戻り値から遠く離れた関数宣言の近くで変数が宣言されています。

それが許されても、あなたは何を得たのですか?ほとんどの警告を有効にすると、コンパイラは間違った戻り値の型についても文句を言うため、戻り時に強制も非表示にする可能性があります。これにより、コードの品質が向上します (真剣に考えれば)。

于 2015-06-12T13:44:04.230 に答える
0

多くの人がデバッグ目的でそうしていることは理解していますが、私はこの種のスタイルには反対です。

おそらく、すぐに返される値を確認するのが難しいというのは、典型的なデバッガーの誤謬と見なすことができます。

あなたがこれをやりたいと思うなら、私は次の2つのことを非常に強くお勧めします。

  • C99 を使用して、後で宣言できるようにします。
  • を使用しconstます。

したがってfoo()、必要に応じて、次のように例を記述します。

FILE * foo(void)
{
  foo2();
  FILE * const retVal = foobar();
  return retVal;
}

アスタリスク ( )constの左側に移動できないことに注意してください。必要なのは定数ポインターであり、定数へのポインターではありません。const FILE *retVal = ...const FILE *

nessの目的は、const人間の読者に「ここでこの値に名前を付けていますが、これは私がいじるつもりの状態ではありません」と言うことです。

于 2015-06-12T13:24:24.943 に答える