web-dev-qa-db-ja.com

純粋に美的な理由で入れ子関数を作成しますか?

純粋な美的機能を作成するという考えについて、他のプログラマーが何を考えているのか、いつも疑問に思っていました。

データのチャンクを処理する関数があるとします:Function ProcessBigData。いくつかの処理手順が必要で、そのデータに対してのみ有効であるとします:Step1Step2Step3

ソースコードで最もよく目にする通常のアプローチは、次のようにコメントを書くことです。

Function ProcessBigData:
    # Does Step1
    Step1..
    Step1..

    #Does Step2
    Step2..
    Step2..

私は通常何をしますが、仲間の仲間によるそのようなコーディングスタイルの欠如のためにいつも間違っていると感じました:

Function ProcessBigData:
    Function Step1:
        Step1..
        Step1..

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

JavascriptPythonでそのようなスタイルに欠点があるかどうか、私は主に心配しています

私が見ていない代替案はありますか?

16
Slytael

あなたが思うほど奇妙ではありません。たとえば、標準MLでは、ヘルパー関数のスコープを制限するのが慣例です。確かに、SMLにはそれを容易にする構文があります。

local
    fun recursion_helper (iteration_variable, accumulator) =
        ... (* implementation goes here *)
in
    fun recursive_function (arg) = recursion_helper(arg, 0);
end

1)小さな関数はプログラムについての推論を容易にし、2)これらの関数はその範囲外では使用されないことを読者に通知するので、私はこの良いスタイルを検討します。

外側の関数が呼び出されるたびに内側の関数を作成するときにオーバーヘッドが発生する可能性があると思います(JSまたはPython最適化するかどうかはわかりません)。 。

4
Doval

通常、可能な限りこれを行うのは良いことですが、この種の作業は「ステップ」ではなく、subtasksと考えるのが好きです。

サブタスクは、実行可能な特定の作業単位です。特定の責任があり、入力と出力が定義されています( "S" in [ 〜#〜]固体[〜#〜] )。サブタスクは再利用可能である必要はありません。一部の人々は、「これを他のものから呼び出す必要がないので、なぜ関数として記述するのか」と考える傾向があります。しかし、それは誤りです。

また、利点と、クラス内の他の関数だけでなく、ネストされた関数(クロージャー)にどのように適用されるかについても概説します。一般的に言えば、特に必要でない限り、クロージャーを使用しないことをお勧めします(多くの用途がありますが、コードを論理チャンクに分離することはその1つではありません)。

読みやすさ。

200行以上の手続き型コード(関数の本体)は読みにくいです。 2-20行の関数は読みやすいです。コードは人間用です。

ネストされているかどうかに関係なく、親スコープの変数を多数使用している場合を除いて、ほとんどの場合、読みやすさのメリットがあります。

変数のスコープを制限する

別の関数があると、変数のスコープを制限し、具体的には必要なものを渡す必要があります。

以前の「ステップ」からのある種の状態変数が必要な場合は、実際にその値を取得するために最初に記述して実行する必要のある別のサブタスクが実際に見つかる可能性があるためです。または、言い換えると、高度に結合されたコードのチャンクを書くことが難しくなります。

入れ子関数を使用すると、入れ子関数(クロージャー)内から親スコープの変数にアクセスできます。これは非常に便利ですが、関数の実行が記述された方法で実行されない可能性があるため、微妙で見つけにくいバグにつながる可能性もあります。これは、親スコープの変数を変更する場合にはさらに当てはまります(一般的に非常に悪い考えです)。

単体テスト

関数(またはクラス)を実装した各サブタスクは、スタンドアロンのテスト可能なコードです。 単体テスト[〜#〜] tdd [〜#〜] の利点については、他の場所で詳しく説明されています。

ネストされた関数/クロージャーを使用すると、ユニットテストは許可されません。私にとっては、これは取引のブレーカーであり、特別なクロージャーが必要でない限り、別の関数を使用する必要がある理由です。

チームでの作業/トップダウン設計

サブタスクは、必要に応じて、独立してさまざまな人が作成できます。

自分でも、メイン機能を構築しながら、まだ存在していないサブタスクを呼び出すコードを記述する場合、および必要な結果が得られることがわかった後でのみサブタスクを実際に実装することを心配する場合に役立ちます。意味のある方法。これは、トップダウン設計/プログラミングとも呼ばれます。

コードの再利用

さて、先ほど言ったことにも関わらず、実際には後でサブタスクを別の目的で再利用する理由になる場合があります。私はまったく「建築宇宙飛行士」主義を支持しているわけではありませんが、疎結合のコードを書くだけで、後で再利用することで利益を得ることになるかもしれません。

多くの場合、その再利用は何らかのリファクタリングを意味しますが、これは完全に予想されますが、入力パラメータを小さなスタンドアロン関数にリファクタリングすることは、作成されてから数か月後に200行以上の関数から抽出するよりもはるかに簡単です。

ネストされた関数を使用する場合、それを再利用することは、通常、とにかく別の関数にリファクタリングすることの問題です。これが、ネストされた方法ではうまくいかない理由です。

11
gregmac