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


16

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

データのチャンクを処理する関数があるとしますFunction ProcessBigData。たとえば、そのデータにのみ有効ないくつかのプロセスステップが必要Step1Step2としStep3ます。

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

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

    #Does Step2
    Step2..
    Step2..

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

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

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

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

表示されない代替手段はありますか?


3
Pythonについては何も言えませんが、Javascriptの場合、ネストされた関数のパフォーマンスコストがかかります。ほとんどのJavaScriptエンジンは、リンクリストのような構造を使用して変数スコープを表現します。したがって、関数のレイヤーを追加すると、変数を解決するときに、エンジンはより長い/より大きなデータ構造を検索する可能性があります。一方、すべての悪の根源はもちろん、時期尚早な最適化です。:)
マルコ14

回答:


4

思っているほど奇妙ではありません。たとえば、標準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が最適化を行っているかどうかはわかりません)、時期尚早な最適化についての彼らの言うことは知っています。


11

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

それは特定の責任を有する、と定義された入力(S)と出力(S)(考える:サブタスクは特定の可能仕事の単位である「S」SOLID)。サブタスクは再利用可能である必要はありません:一部の人々は「私はこれを他から呼び出す必要は決してないので、なぜそれを関数として書くのか?」と考える傾向があります。しかし、それは誤りです。

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

読みやすさ。

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

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

変数スコープを制限する

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

多くの場合、これによりコードの構造も改善されます。以前の「ステップ」から何らかの状態変数が必要な場合、実際にその値を取得するために最初に作成および実行する別のサブタスクがあることがわかるからです または、言い換えると、高度に結合されたコードチャンクの記述が難しくなります。

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

単体テスト

関数(またはクラス)を実装した各サブタスクは、スタンドアロンのテスト可能なコードです。単体テストTDDの利点は、他の場所で十分に文書化されています。

ネストされた関数/クロージャーを使用すると、単体テストが許可されません。私にとって、これは契約を破る要因であり、閉鎖が特に必要でない限り、別の機能を実行する必要がある理由です。

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

サブタスクは、必要に応じて、異なる人々が独自に作成できます。

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

コードの再利用

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

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

ネストされた関数を使用する場合、それを再利用することは一般に別の関数にリファクタリングすることの問題であり、それがネストされた関数を使用する方法ではないと主張する理由です。


2
これらは一般に関数を使用するためのいくつかの本当に有効なポイントですが、ネストされた関数が良い考えだと思う場合、私はあなたの答えから得られませんでした。または、上流のスコープで関数を取得していますか?
スリテール14

申し訳ありませんが、その部分に対処するのを忘れていた他の利点に巻き込まれました。:)編集。
グレックマック14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.