困難な条件を作る

通常の形式の条件演算子は、比較的まれな問題の原因です。 ただし、条件自体は非常に複雑な場合があり、開発者の夢の邪魔になります。 もちろん、これは美しく読みやすいコードに関するものです。



おそらく私はそこを見ていなかったかもしれませんが、コード設計標準の困難な条件に対処する方法についての言及は一度もありませんでした。 それらに対処することがこの記事の目的です。



私は指を吸い出すのに問題があるので、GCC 4.8.2のソースコードの一部はソースの例として取り上げられており、その著者にとっては設計基準は空のフレーズではありません。 例を使用して、ファイルと開始行を引用して、すべてが正直であることを誰もが確認できるようにします。 例は実際のものであり、限られたソースから取られたものであるため、それらのいくつかは最も成功しないかもしれないことにすぐに注意します。



この記事のコンテキストでは、複数のサブ条件で構成され、1行に書き込むときに要件を満たさない難しい条件を検討します。 文字列の長さや読みやすさなどの要件を意味します。



通常、読みやすさは目で判断されます。一部のケースでは、コードのセクションを一見すると3〜4のサブ条件が区別されますが、2つの機能で脚が壊れる場合もあります。 たとえば、関数、型キャスト、ビット演算、またはネストの使用は、条件の読み取りを非常に複雑にします。



次の例はエッジのどこかにあります。



libgcc / fp-bit.c-205:

if (LARGEST_EXPONENT_IS_NORMAL (FRAC_NBITS) && (isnan (src) || isinf (src)))
      
      





しかし、時には単純な状態でさえ、しばらく考えさせることがあります。



libgcc / fp-bit.c-1579:

 if ((in.fraction.ll & (((USItype) 1 << F_D_BITOFF) - 1)) != 0)
      
      





当然、これが条件の一部である場合、それ(結果の条件)はほとんど単純とは言えません。



これは、この記事の範囲を超えたさらに複雑な条件に関するものであるため、意図的に条件内のプリプロセッサディレクティブの例を取り上げませんでした。



読みやすくするには、条件をブロックに分割する必要があります。 また、条件付きの文字列の長さが許容範囲(多くの場合、標準で指定されている)を超える場合にも実行する必要があります。 このパーティションの原則は、2つの主な理由から興味深いものです。 第一に、これは状況を悪化させないために行われなければなりません。 第二に、統一性の事実はすでに読みやすさのためにプラスであるため、暗黙的で標準的なものにする必要があります。



最初に頭に浮かぶのは、ネストされた条件ステートメントの使用です。



libgcc / fixed-bit.c-84:

 if ((((x ^ y) >> I_F_BITS) & 1) == 0) { if (((z ^ x) >> I_F_BITS) & 1) { ... } }
      
      





libgcc / fp-bit.c-361:

 if (exp < EXPMAX) if (low > unity || (low == unity && (high & 1) == 1)) { ... }
      
      





最初のケースですべてがそれほど悪いわけではない場合、2番目のケースで囲まれた条件は、分割する必要があるという事実に近いです。 一方、コードのネストは成長しています。 さらに、分離の場合、そのようなトリックは機能しません。



より興味深いオプションは、複数行の条件です。 多くの場所(同じC、PHP、Python)でサポートされていますが、このようなソリューションは予想外のようです。



分割の考え方は、各行に1つのサブ条件しか残らないということです。



libgcc / libgcc2.c-1980

 if (!recalc && (isinf (ac) || isinf (bd) || isinf (ad) || isinf (bc)))
      
      





この状態は読みやすく、簡単に理解できます。 ただし、前述のルールには準拠していません。 単調性の利点があります。 行ごとに1つのサブ条件を記述する必要性が指定されていない場合、この条件の読み取り時の分析は複雑になります。 それ以外の場合、 サブ条件としてlibgcc / fp-bit.c-1579のようなものに遭遇しても、複雑ではないことが事前に知られています。



それにもかかわらず、検討中のソースの複数行の条件のほとんどは、この考えに対応しています。 しかし、彼女だけに制限しないでください。 最後の例のインデントは、このアプローチによる条件のネストが視覚化できることを示唆しています。 疑念を生じさせた唯一のことは、そのような条件はすべて単調すぎることであり、それらは単なる「はしご」であり、単なる偶然であるという考えさえ作りました。



幸いなことに、美しく理解しやすい困難な状態の私の定義に間違いなく適合する全体の例が見つかりました。



libgcc / libgcov-driver.c-688:

 if (!all_prg->checksum && (cs_all->num != cs_prg->num || cs_all->runs != cs_prg->runs || cs_all->sum_all != cs_prg->sum_all || cs_all->run_max != cs_prg->run_max || cs_all->sum_max != cs_prg->sum_max))
      
      





ここでは、行ごとに1つのサブ条件の原則が観察され、ネストが明確に示されています。 この状態の分析は簡単で、これは楽しいです。 単一行またはネストされた3つまたは4つのifとして想像してください。



当然、これをネストされた条件ステートメントと組み合わせます( libgcc / libgcc2.c-1611(記事には例はありません )で行われます)。



このアプローチを実装するための別のオプションが頭に浮かぶが、それはもっと面倒であり、実際には見たことがない。 次のようなもの:

 if ( condition1 && ( condition2 || condition3 ) )
      
      





これは1つのオプションです。 目的は、閉じ括弧を別の行に置くことです。 これにより、たとえば次の例のように、「ジャンプ」インデントがなくなります。



libgcc / fixed-bit.c-1013:

 if ((BIG_SINT_C_TYPE) high > (BIG_SINT_C_TYPE) max_high || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) max_high && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low)) low = max_low; /* Maximum. */
      
      





さて、ブラケットの配置は、すべての通常の演算子ブラケットの配置に似ているため、明白で理解しやすいものです。 もちろん、ネストされた条件を強調表示するための括弧は、論理演算の優先順位に関連するエラーを回避できるため必要です。



結論は必要ないと思います。誰もが自分でやるでしょう。 まあ、それでもこの問題がいくつかの標準や文献で触れられていれば、リンク、タイトル、著者は傷つきません(引用は大歓迎です)。



UPD:別の良いオプションは、複雑な条件の一部を個別のブール変数に抽出することです。 適切な作業例に出くわしなかったため、最初に言及しませんでした。 指数コードlexasssをありがとう

 bool mustRdraw = (frame.isChanged() || target.isChanged()) || experiment.isRunning(); bool isFullScreen = frame.getSize().equal(screen.getSize()); if (isFullScreen && mustRedraw) { // redraw }
      
      





条件の適切なグループ化と変数の命名により、このアプローチには文書化機能もあります。



All Articles