web-dev-qa-db-ja.com

常にコードの行数が少ない方が良いですか?

これらのプログラミングスタイルのどちらが優れていますか?

var result = methodOne(methodTwo(a, methodThree(b)), c, d);

または

var result3 = methodThree(b);
var result2 = methodTwo(a, result3);
var result = methodOne(result2, c, d);
88
Mike Bryant

簡単に言えば:

重要なのは行数ではなく、コードの読みやすさです。

どんな愚か者も、コンピューターが理解できるコードを書くことができます。優れたプログラマーは、人間が理解できるコードを作成します。 ( M。ファウラー

あなたが挙げた例では、2番目の例は間違いなく読みやすくなっています。

ソースコードは人々が読むためのものです。

さらに、中間値はコードのデバッグを容易にします。

一方、ワンライナーのコードは、自分が「賢い」であり、気にしないことを他の人に示すのに役立ちます。

217

それは場合によって異なり、あなたの例は決定を下すのに役立ちません。

alwaysでないコード行は少なくなりますが(ある時点で難読化につながります)、通常になるのは、単に理解しようとするときに追跡することが少ないためです。コード。

あなたの具体的な例では:

中間値のnamesが実際に、それらの中間値の計算に使用されるメソッドとパラメーターの名前では明らかにされない意味を伝える場合、それらはコードの理解に役立ちます。

ただし、中間値の名前によって情報が追加されない場合は、追跡するための追加の状態であるため、コードがわかりにくくなります。それらが不変であっても、それらが1回だけ使用されるか、別の場所で使用されるかを検討するように強制します。

102
Code with as few lines as possible is definitely the best code and every semi-colon you see is basically the developer admitting they weren't clever enough to use advanced constructions like the comma operator or short-circuiting operators to keep the line going as long as possible like you can say `(x++ && false) || y += 2` instead of `x++; y += 2` and the first variant is just so much more concise and clearer as you can tell because it contains no semi-colons honestly I almost threw up writing the second one I mean who wants to use a character whose name contains a Word related to your butt that's just dumb and anyways semi-colons introduce lots of vulnerabilities like what if you typed `while(true) ;` that would just keep running forever and so its clearly better to have no semi-colons to avoid problems like that overall I'd say programmer salaries should be inversely proportional to the number of newlines and semi-colons in their code because everyone knows as soon as you start using a metric like that it starts working even better than before you incentivized it
60
Craig Gidney

コード行でプログラミングの進捗状況を測定することは、航空機の建造の進捗状況を重量で測定することに似ています。 (ビルゲイツ)

もちろん、行が少ないほどnotの方が常に優れています。しかし、私の経験では、読みやすさと保守性の点で、多くの行よりも少ない行の方が良い場合がよくあります。もちろん例外はあり、あなたの例はそのような例外の1つかもしれません。

航空機を組み立てるには重量が必要です。しかし、それは正しい場所にあるはずです。

34
Martin

最初のバージョンでは、どのパラメーターがどのメソッドに渡されるかを確認するのは困難です。したがって、2番目のバージョンの方が明らかに優れています。ただし、他のケースではそれほど明白ではない場合があります。

2番目のバージョンの主な違いは、それが中間値に名前を付けることができるであることです。この名前がコードを理解するために重要な場合があります。

ただし、中間値がとにかくvarOneのように呼ばれる場合は、インライン化することで省略できます。

11
oberlies

JavaScriptを使用しているようです。これにより、パラメーターがわずかに変更されます。

これらのvarを保存する理由がない限り、それらをまったく初期化しない方がよいでしょう。ただし、空白とコメントを追加して、単一ステートメントのバージョンをできるだけ読みやすくする必要があります。

//Do X, Y, Z....
var result = methodOne(
    methodTwo(
        a, 
        methodThree(b)
    ), 
    c, 
    d
);
11
DougM

常にコードの行数が少ない方が良いですか?

ショートは単にnoです。 依存するであるため、長い方のno

  • 短くしたいコードの長さはどれくらいですか?コードが画面に合わない場合は、コードを短くすると読みやすくなります
  • 単一行でどのくらい行われますか? 10個のオブジェクトを介したアクセスの場合は、それを多くの行に分割することをお勧めします
  • 何が良いですか?長い標準バージョンを書くか、単一のトリッキーな構文シュガーまたはライブラリ関数を使用しますか?その砂糖/機能をどれだけの人が知っていますか?良い例が正規表現です。単一の正規表現を使用すると、多くのコード行と同じくらい多くを達成できますが、単一の正規表現を理解するのに数時間かかる場合があります。ただし、広く使用され、Google検索可能な正規表現がいくつかあります。

これらのプログラミングスタイルのどちらが優れていますか?

さて、2つ目は、1つの面ではるかに優れています。何が起こっているのかをデバッグする必要がある場合、検査する変数とブレークポイントを配置するためのより多くの可能性があります。

一方、ローカル変数の名前を作成する必要があるため、あまり多くのローカル変数を使用したくありません。しばらくすると、どちらが何のためのものかわからないので、result1/2/3のような名前は嫌いです。しかし、名前の作成にあまり時間をかけたくありません...最初の例は(私にとって)2番目の例の変数名が間違っているため、実際にはより読みやすくなっています。

通常、最初のコードから始めて、デバッグが必要なときに最初に2番目のコードに変換します。

ただし、式がより複雑な場合は、ローカル変数に中間結果を保持することをお勧めしますが、それらのローカル変数自己説明的な名前が必要です。そうして初めて、コードが読みやすくなります。

9
Danubian Sailor

私の経験則では、各操作を独自の行に配置します。これは、デバッガーでコードをステップ実行するときに特に役立ちます。また、操作の結果を変数に渡してから渡すことで、その内容を壊して調べられるようにします。そうの代わりに

var result = methodOne(methodTwo(a, methodThree(b)), c, d);

私は行くだろう

var result3 = methodThree(b);
var result2 = methodTwo(a, result3);
var result = methodOne(result2, c, d);

私が間違っていなければ、Javaおよび.Net JITコンパイラは2つの例を同じ方法でコンパイルします。

または、行の前に非常に詳細なコメントを書いて、何をしているのかを説明します。

8

誰もが聞きたいことは「いいえ」だと思います。

問題の事実は、あなたの例が明確な答えを与えるのに十分に明確ではないということです。

resultThirdMethodは恐ろしい名前です。最初のバージョンを見つけますmuch 2番目のバージョンより明確です。

ただし、変数に名前を付けることができれば(データがどこから来たのかだけではなく、より多くの情報を提供できる名前)、その方が良いでしょう。それはすべて実際の状況に依存します。

5
user541686

この特定の例では、最初のバージョンが読みにくいとは思いません。これは、(a + f(b)) * c * d。 2番目のバージョンは、それを理解するのに役立つ情報を追加しません(変数に複雑なプロセスを理解するのに役立つ意味のある名前がない限り)。

2番目のバージョンで少し問題があると思うのは、コードを分析するときに複雑さが増すことです。コードを読み取るプログラマは、resultThirdMethodまたはresultSecondMethodresultを計算するために他の場所で使用されているかどうかを考える必要があります。そして、そのようなステートメントでいっぱいのメソッドを書く場合、明確な計算の行がないので、何からどこから何が計算されたかを常にチェックする必要があります。したがって、この場合、より詳細なバージョンが必要な場合は、別の関数にカプセル化することをお勧めします。

function methodCombined(a, b, c, d) 
    var resultThirdMethod = methodThree(b);
    var resultSecondMethod = methodTwo(a, resultThirdMethod);
    return methodOne(resultSecondMethod, c, d);
}
// ...
result = methodCombined(a, b, c, d);

または、少なくともコードブロックで囲んで、変数のスコープがすぐに見えるようにします。

var result;
{
    var resultThirdMethod = methodThree(b);
    var resultSecondMethod = methodTwo(a, resultThirdMethod);
    result = methodOne(resultSecondMethod, c, d);
}
4
Petr Pudlák

それは、読者がこれらのオペレーターとその問題領域にどの程度精通しているかによって異なります。すべてのメソッドが算術関数であっても、それを分解するのに役立ちます。

大きな数式を小さなステップに分割すると、読みやすさがかなり損なわれます。

(let* ((term1 (* b b))
       (term2 (* 4 a c))
       (discriminant (- term1 term2))
       (twoa (* 2 a))
       (top1 (+ b (sqrt discriminant)))
       (top2 (- b (sqrt discriminant)))
       (root1 (/ top1 twoa))
       (root2 (/ top2 twoa)))
  (list root1 root2))

versus

(let* ((root1 (/ (+ b (sqrt (- (* b b) (* 4 a c)))) 
                 (* 2 a))) 
       (root2 (/ (- b (sqrt (- (* b b) (* 4 a c))))
                 (* 2 a)))
  (list root1 root2))

数式を小さなステップに分割することは、コンパイラーが中間コードを生成するときに行うことです。 (Fortran:FORmula TRANslator)。

読みやすくするために式を分解することは役立ちますが、「Fortran it」(やりすぎ)は避けてください。

4
Kaz

最良のスタイルは、コードを最も読みやすいにするものです(=人間にとって、つまり-コンピュータは気にしません)。しかし、「読み取り可能」はたくさん依存します誰が読み取りを行うかに依存するので、これはコンテキストに依存します。しかし、これはあなたの正確な質問に答えるのに十分です:「[何でも]always良い」?次にno、それはコンテキストに依存します。

コードを読まなければならない最初の人は開発者自身です。あなたがyourselfが4週間後にそれを読んで理解できるようにコードを書く場合、あなたはすでに良い仕事をしているでしょう-業界の大多数の開発者(alas)よりもはるかに良い仕事です。

ただし、いくつかの一般性を追加できます。コードを読むことは、目と脳を使用することを意味します。人間の目は、多くの場合、特に垂直方向に移動する必要がある場合に、より速く疲れます(横方向の移動はそれほど問題ではありません)。他のすべての条件が同じであれば、コードの行数を少なくすることで、読者は垂直方向の目の動きを抑えてコードを把握できるようになるため、多少良いです。

一方、脳内で行われる構文解析では、可能な限り多くの「ビジュアルアンカー」を使用する必要があり、特定のアクティビティ、特に括弧の数を数えることになります。インデントを使用してコードを複数行に広げることで、脳を助けます。それは本当に目の疲労と脳の疲労のトレードオフであり、正しいバランスポイントは誰にとっても同じではないと私は主張します。

結局、それは「美味しさ」の問題です。醜いコードを書く人は、悪いピザを食べて幸せな人でもあります。

3
Thomas Pornin

綴りは、読みやすさだけでなく、書きやすさにも役立ちます。

これは馬鹿げたインタビューの質問のように思えるかもしれませんが、インタビューで一度、画像を90度回転させる関数を書くように求められました。ソースと宛先が提供され(メモリ割り当てを行う必要はありません)、ソースの幅と高さが提供されます。はい、それは簡単な問題です。続行する前にそれを実行し、問題があるかどうかを確認することをお勧めします。

しましたか?テストしましたか?インタビューで質問されたので、インタビューでも質問を使用しました。多くのプログラマーが混乱することがわかりました。彼らはこのようなコードを書くでしょう

 void Rotate90(const Pixel* src, Pixel* dst, int width, int height) 
 {
    for (int y = 0; y < height; ++y) 
    {
      for (int x = 0; x < width; ++x) 
      {
         dst[???] = src[???];

そして、それは彼らが行き詰まるところです。彼らは時々ホワイトボードに20分も費やして、何が起こっているのかを理解しようとするでしょうか?範囲。多くの場合、彼らは自分が答えを持っていると思い、次のようなものを書きます

         dst[x * width + (height - 1 - y)] = src[y * width + x];

widthdstとともに使用するのは間違っているという事実を理解する。あるいは、しばしばxyがどちらか一方に混ざってしまうでしょう。

問題は、xywidth、およびheightがすべて入れ替わることです。これらの変数を参照すると混乱します。このように、プログラマーが変数をいくつか追加して、自分で明確にできるようにしたいと常に思っていました

 void Rotate90(const Pixel* src, Pixel* dst, int src_width, int src_height)
 {
   int dst_width = src_height;
   int dst_height = src_width;

   for (int src_y = 0; src_y < src_height; ++src_y) 
   {
     for (int src_x = 0; src_y < src_width; ++src_x) 
     {
       int dst_x = src_height - src_y - 1;
       int dst_y = src_x;
       dst[dst_y * dst_width + dst_x] = src[src_y * src_width + src_x];
     }
   }
 }

物事を明示的な意味に分解すると、コードの記述がはるかに簡単になり、間違いを回避するのがはるかに簡単になるように思えました。プログラマーがこれを行うのを見た場合、私はそれらをwiseと考えて、より高く評価するように感じました。

はい、できるいくつかの最適化があります。それはポイントではありません。はい、最後のステートメント内の数学も別の行に分割できます。それでも結構です。ポイントは、行を追加することで、後でコードを読む人だけでなく、コードを書く人にとっても理解しやすくなったということです。この場合、xywidthおよびheightの意味は、dstsrcそれぞれに別々の変数を作成すると、コードが読みやすくなりますAND書き込み

続けて...同様に、関数が呼び出される前に値を検査できると便利です。特に、ライブラリや関数に入ることができないシステムの場合はそうです。このようなコードを書くと

ctx.lineTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);

これに対して

var circleX = x + Math.cos(angle) * radius;
var circleY = y + Math.sin(angle) * radius;
ctx.lineTo(circleX, circleY);

2番目のスタイルでは、ctx.lineTo行でデバッガーを停止し、circleXcircleYを検査できます。あなたはそれらがNaNであることを発見するかもしれません、おそらくあなたはxangle、およびradiusが実際に正しい名前であることを確認するでしょう。デバッガーで実行できます。 ( "strict"モードはおそらくそれをキャッチします)。また、値を簡単に記録できます

console.log("cx: " + circleX = ", cy: " + circleY);

さらに別の例

class Person 
{
public:
   ...
   const String& name() const { return m_name; }
   int age() const { return m_age; }

private:
   string m_name;
   int m_age;
};

class Person 
{
public:
   ...
   const String& name() const 
   { 
     return m_name; 
   }

   int age() const 
   { 
     return m_age; 
   }

private:
   string m_name;
   int m_age;
};

あなたの個人的なスタイルはゲッターとセッターを1行にすることを望んでいるので、この例で悲鳴を上げる人がいることは知っていますが、ageまたはnameデバッガーはthism_agem_nameも検査できないため、関数が何を返すかを確認する方法はありません。それが返すのは、それが参照しているオブジェクトです。 2番目のスタイルでは、return行にブレークポイントを配置でき、すべてを検査できます。

この回答の要点は、多くの行は読みやすさだけでなく、また、書き込み可能性やデバッグにも適しています。

0
gman

これは、個別のケースごとにどの方法が有効であるかを決定できるようになったと私が思うこれらの問題の1つです。特定の結果を中間変数に格納するかどうかを決定することは、プログラマーの一部であり、技術の一部です。取得したスキルをコーディング基準とともに職場で使用します。

以下の2つの工夫された例では、読みやすさ(1Bでのノイズと冗長性が多すぎ、2Bでの匿名配列の間接的使用が長すぎる)のため、おそらく1Aと2Aを使用します。しかし、実際にパターンの完全なリストを考え出すことはできません。パターンを他の方法で規定することはできません。プログラミングの技術が進歩するにつれて、常に新しい状況が発生します。

1A:
startManufacture(loadProductName(), isProductOk(loadProductState()));

vs.

1B:
String productName = loadProductName();
ProductState state = loadProductState();
boolean productIsOk = isProductOk(state);
startManufacture(productName, productIsOk);


2A:
boolean antennaCount = storage.getCount(currentDealer, items[ANTENNA_INDEX]);
boolean chassisCount = storage.getCount(currentDealer, items[CHASSIS_INDEX], EXTERNAL_SHIPPING);
orderManufacture(RADIO, antennaCount, chassisCount);

vs.

2B:
orderManufacture(RADIO, storage.getCount(currentDealer, items[ANTENNA_INDEX]), storage.getCount(currentDealer, items[CHASSIS_INDEX], EXTERNAL_SHIPPING));
0
TV's Frank

どちらの例も等しくbadだと思いますが、それは単に例のコードが不十分なためです。 2つのうち1つが優れているかどうかを判断するには、実際の例を提供する必要があります。

ソースコードの行数とコードの品質の間には絶対的な相関関係はありません。

コードが想定されていることを実行する必要があるという明白な事実を無視すると仮定すると、最も重要な側面は、読者がコードを簡単に理解できることです。

これを行う「クラシック」な方法は、コードの機能を説明するコードコメントを追加することです。私の個人的な経験では、メソッド、変数、および定数には説明的な識別子を使用する方が良いです。

たとえば、次のようなソースコードシーケンスを見ると、それが何をすべきかがすぐに理解でき、個々のメソッドと変数を調べて(うまくいけば)それらが想定されていることを実行していることを簡単に判断できます。

if (a_new_game_is_starting && hand_not_dealt_yet()) {
  deal_new_hand_to(all_players_at_table);
}

上記の例では、コメントを追加する必要はまったくありません。実際のコードからは簡単に理解できないコメントは何でしょうか?これは、コードに何を行うかを説明するコメントをコードに追加するために-不要を目指して努力するものです。

これは難しいことではありません。必要なことは、実際に達成しようとしていることについて考え、機能的な観点から識別子に名前を付けることだけです。

0
Boise

読みやすさよりも重要なのはメンテナンス性です。コードを読む主な理由は、コードを維持するためです。多くの場合、維持とは、要件の小さな変更または大きな変更のためにコードに変更を加えることを意味します。

そのため、要件の変更について考え、可能な限り簡単に行えるようにしてください。メンテナーを適切なコードに導くことができ、最小限の編集で変更を行わせることができれば、彼らはあなたが素晴らしいプログラマであると考えます!

OTOH、コードが正しいことを確認するために人々がコードを読んでいる場合は、コードが正しい理由を明確にしてください。人々があなたのコードの正確さを疑うならば、彼らはそれを書き直す可能性が高く、それはそれを破壊する可能性があります。これを防ぐには、コードがそれ自体を説明していることを確認してください。

また、パフォーマンスに関連する決定を必ず説明してください。良いコードが破壊される1つの方法は、それをより速くするための誤った試みによって書き直されることです。

0
Mike Dunlavey
return ($this->main->context != "ajax" || in_array($this->type, $this->definition->ajax)) ? eval('return method_exists($this,"Show'.ucfirst($this->type).'") ? $this->Show'.ucfirst($this->type).'('.(count($args) ? join(",",array_map(create_function('$a','return (is_numeric($a) || preg_match("/^array/",$a)) ? $a : "\"".$a."\"";'),$args)) : "").') : null;') : '';

先ほど継承したプロジェクトでこのコードにぶつかった。できるだけ少ない行数で試してみるのはなぜ良い考えではないのかがよくわかると思います。完全に読み取り不可能であり、デバッグは不可能です。

0
Julius

あなたは悪い例を挙げます。

問題をよりよく解決するために提供するソリューションは、理解しやすく、コードを短く保つのに十分な優れた設計を備えています。

長くすることでより理解しやすいコードを作ることができれば、それはやるべきことですが、限界があります。

優れたデザインと短いコードの両方を備えたコードは、短くなるほど理解しやすくなります。

優れたデザインに焦点を当て、コードを十分に短くして、それを読む人がスクロールせずに理解できるようにする方法を見つける必要があります。

要約すると、これに対する真の良い答えはありません。重要なのは、コードを関数とセクションにどの程度うまく分割するか、そしてどのように短くてわかりやすくすることができるかです。

ビルゲートの見積もりは良いですが、完璧なコードについて述べている見積もりもあります。これは、何も取り出すことができないコードです。

0
jokoon