ネストされた関数(Python and Dなど)をサポートする言語で複雑なアルゴリズムを扱う場合、(アルゴリズムが複雑なため)巨大な関数を頻繁に記述しますが、ネストされた関数を使用して複雑なコード:巨大な(100行以上)関数は、ネストされた関数を使用して内部で適切に構造化されていても、依然として悪と見なされていますか?
編集:PythonまたはDに精通していない方のために、これらの言語のネストされた関数は、外部関数スコープへのアクセスも許可します。Dでは、このアクセスにより、外部スコープ内の変数の変更が可能になります。 Pythonこれは読み取りのみを許可します。Dでは、ネストされた関数の外部スコープへのアクセスをstatic
と宣言して明示的に無効にすることができます。
常にルールを覚えておいてください。関数は1つのことを行い、それをうまく実行します。可能であれば、ネストされた関数を避けてください。
読みやすさとテストの妨げになります。
短い関数は長い関数よりもエラーが発生しやすい であると主張する人もいます。
Card and Glass(1990)は、設計の複雑さには実際には2つの側面が関係していると指摘しています。各コンポーネント内の複雑さとコンポーネント間の関係の複雑さです。
個人的には、よくコメントされた直線のコードは、他の場所では決して使用されない複数の関数に分割する場合よりも(特に、最初にコードを記述したコードではない場合)従う方が簡単であることがわかりました。しかし、それは本当に状況に依存します。
主なポイントは、コードのブロックを分割すると、ある種類の複雑さを別の種類の複雑さと交換することだと思います。おそらく、真ん中のどこかにスイートスポットがあります。
理想的には、スクロールせずに関数全体を表示できる必要があります。時々、これは不可能です。しかし、それを細かく分割できれば、コードの読み取りがずっと簡単になります。
ページを上/下に押すか、コードの別のセクションに移動するとすぐに、前のページの7 +/- 2のことしか覚えられないことを知っています。そして残念ながら、これらの場所のいくつかは、新しいコードを読み取るときに使用されます。
私はいつも、コンピューターのレジスター(RISCではなく、CISC)のような短期記憶について考えたいと思っています。関数全体が同じページにある場合は、キャッシュに移動して、プログラムの別のセクションから必要な情報を取得できます。関数全体がページに収まらない場合、それはすべての操作の後に常にメモリをディスクにプッシュすることと同じです。
通常の外部関数ではなく、入れ子関数を使用するのはなぜですか?
以前は大きな関数であった外部関数のみが使用されている場合でも、全体の混乱が読みやすくなります。
DoThing(int x){
x += ;1
int y = FirstThing(x);
x = SecondThing(x, y);
while(spoo > fleem){
x = ThirdThing(x+y);
}
}
FirstThing(int x){...}
SecondThing(int x, int y){...}
ThirdThing(int z){...}
現在、この本はありません(引用するには)が、コードコンプリートによると、彼の調査によると、関数の長さの「スイートスポット」は約25〜50行のコードでした。
長い機能を持っても問題ない場合があります。
長い機能を持つのが良くない時:
要点は、保守性がリストの最優先事項の1つであることです。別の開発者があなたのコードを見て、5秒未満でコードが何をしているかの「要点」を取得できない場合、urコードは何をしているかを伝えるのに十分な「メタデータ」を提供しません。他の開発者は、100行以上のコードを読み取る代わりに、選択したIDEでオブジェクトブラウザを見るだけで、クラスが何をしているかを知ることができます。
小さい関数には次の利点があります。
リストは続きます...
答えは、状況によって異なりますが、おそらくそれをクラスに変える必要があります。
ほとんどのネストされた関数は好きではありません。ラムダはそのカテゴリに分類されますが、30〜40文字を超えない限り、通常はフラグを立てません。
基本的な理由は、それが内部の意味的再帰を伴う非常に局所的に密な関数になることです意味私の頭を包み込むのは難しいことであり、ヘルパー関数にいくつかのものをプッシュするのは簡単ですコードスペースが乱雑になることはありません。
私は関数がそのことを行うべきだと思います。他のことをすることは他の機能がすることです。したがって、200行の関数「Doing its Thing」を実行してすべてがフローする場合は、問題ありません。
いいえ、マルチページ関数は望ましくないため、コードレビューに合格するべきではありません。以前は長い関数を記述していましたが、Martin Fowlerのリファクタリング、停止しました。長い関数は、正しく記述したり、理解したり、テストしたりすることが困難です。 50行でさえも、一連の小さな関数に分割すると簡単に理解およびテストできない関数を見たことはありません。マルチページ関数では、ほぼ確実に、クラス全体を除外する必要があります。より具体的にするのは難しい。多分あなたはあなたの長い関数の一つを Code Review に投稿するべきです、そして誰か(多分私)がそれを改善する方法をあなたに示すことができます。
受け入れられますか?それは本当にあなたしか答えられない質問です。関数は、必要なことを達成していますか?メンテナンス可能ですか?あなたのチームの他のメンバーに「受け入れられる」でしょうか?もしそうなら、それは本当に重要なことです。
編集:入れ子関数については見ていません。個人的には使用しません。代わりに通常の関数を使用します。
長い「直線」関数は、特定のシーケンスで常に発生する長いステップシーケンスを指定する明確な方法です。
ただし、他の人が述べたように、このフォームは、全体的なフローがあまり明白ではない複雑なローカルストレッチを持っている傾向があります。局所的な複雑さをネストされた関数(つまり、長い関数内の他の場所、おそらく上部または下部に定義されている)に配置すると、メインラインフローを明確に復元できます。
2番目の重要な考慮事項は、長い関数の局所的なストレッチでのみ使用することを目的とした変数のスコープを制御することです。この種の間違いはコンパイルまたはランタイムエラーとして表示されないため、コードの1つのセクションで導入された変数が他の場所(たとえば、編集サイクルの後)で知らずに参照されることを避けるために、警戒が必要です。
一部の言語では、この問題は簡単に回避できます。「{...}」などのローカルブロックのコードを独自のブロックにラップすると、新しく導入された変数はそのブロックにのみ表示されます。 Pythonなどの一部の言語にはこの機能がありません。その場合、ローカル関数はより小さなスコープ領域を強制するのに役立ちます。
Pythonでプログラミングしているときは、関数を記述した後、「Zen of Python」に準拠しているかどうかを自問します(python =通訳):
醜いよりも美しい方がいいです。
明示的は暗黙的より優れています。
シンプルは複雑よりも優れています。
複雑なものより複雑なものの方が優れています。
フラットはネストよりも優れています。
疎は密よりも優れています。
読みやすさは重要です。
特別なケースは、ルールを破るほど特別ではありません。
実用性は純粋さを上回りますが。
エラーは黙って渡ってはなりません。
明示的に沈黙させない限り。
曖昧な状況に直面して、推測する誘惑を拒否してください。
それを行うには、1つ、できれば1つだけの明白な方法が必要です。
ただし、オランダ人でない限り、その方法は最初は明らかではないかもしれません。
今よりも今より良いです。
今のところrightより優れていることはほとんどありませんが。
実装が説明しにくい場合は、悪い考えです。
実装が説明しやすい場合、それは良いアイデアかもしれません。
名前空間は面白そうな素晴らしいアイデアの1つです。もっと多くのことをしましょう!
それらを別のモジュールに入れます。
ソリューションが必要以上に肥大化していないと仮定すると、オプションが多すぎません。関数はすでにさまざまなサブ関数に分割されているので、問題はどこに配置するかです。
現在、2番目と3番目の選択肢はどちらもある意味で入れ子になっていますが、2番目の選択肢は一部のプログラマーにとっては悪くないようです。 2番目の選択肢を除外しない場合、3番目の選択肢を除外する理由はあまりありません。