2

私はネストされた関数を使用したことはありませんが、いくつかの言語でそれらへの参照を見たことがあります(また、ネストされたクラスは関連していると思います)。

  • 入れ子関数とは何ですか?
  • なぜ?!?
  • 他の方法ではできない入れ子関数で何ができるでしょうか?
  • 入れ子関数を使用すると、これは困難またはエレガントではありません。入れ子関数なしでは何ができますか?

ネストされた関数は、すべてをオブジェクトとして扱うことのアーティファクトであると思います。オブジェクトに他のオブジェクトを含めることができる場合は、それに従います。

関数内の変数がスコープを持っているのと同じように、ネストされた関数はスコープを持っていますか(一般的に、言語はこれで異なると思います)?

答えが言語に依存しないかどうかわからない場合は、参照している言語を追加してください。

-アダム

4

7 に答える 7

4

入れ子関数の一般的な使用法の1つは、クロージャです。第一級関数を使用する字句スコープの言語では、関数を使用してデータを格納することができます。Schemeの簡単な例はカウンターです:

(define (make-counter)
  (let ((count 0))                ; used to store the count
    (define (counter)             ; this is the counter we're creating
      (set! count (+ count 1))    ; increment the count
      count)                      ; return the new count
    counter))                     ; return the new counter function

(define mycounter (make-counter)) ; create a counter called mycounter

(mycounter)                       ; returns 1

(mycounter)                       ; returns 2

この例では、関数make-counter内に関数カウンターをネストし、この内部関数を返すことで、定義時にカウンターに使用できるデータにアクセスできます。この情報は、mycounterのこのインスタンスにプライベートです。別のカウンターを作成する場合、内部カウントを格納するために別のスポットを使用します。前の例からの続き:

(define mycounter2 (make-counter))

(mycounter2)                      ; returns 1

(mycounter)                       ; returns 3
于 2009-02-06T17:00:27.210 に答える
2

これを呼び出すメソッドが1つしかない場合に、再帰に役立ちます。

string[] GetFiles(string path)
{
  void NestedGetFiles(string path, List<string> result)
  {
    result.AddRange( files in the current path);
    foreach(string subPath in FoldersInTheCurrentPath)
      NestedGetFiles(subPath, result);
  }

   List<string> result = new List<string>();
   NestedGetFiles(path, result);
   return result.ToArray();
}

上記のコードは完全に構成されていますが、私が何を意味するのかを理解するためにC#に基づいています。NestedGetFilesを呼び出すことができる唯一のメソッドは、GetFilesメソッドです。

于 2009-02-06T17:00:57.893 に答える
2

ネストされた関数は、別の関数内の単なる関数です。

はい、それはすべてがオブジェクトである結果です。関数のスコープ内でのみ変数を表示でき、変数は関数を指すことができるため、ローカル変数によって参照される関数を使用できます。

ネストされた関数がなければ絶対にできないことは何もないと思います。ただし、多くの場合、それは理にかなっています。つまり、関数が他の関数の「サブ関数」である場合はいつでも。

私にとっての一般的なユースケースは、関数が多くの複雑なロジックを実行する場合ですが、関数が計算/返すものは、ロジックによって指示されるすべてのケースで簡単に抽象化できます。

于 2009-02-06T17:05:52.533 に答える
1

関数を引数として別の関数に渡す必要がある場合にも役立ちます。また、ファクトリ関数のファクトリ関数を作成する場合にも役立ちます(Pythonで)。

>>> def GetIntMaker(x):
...   def GetInt():
...     return x
...   return GetInt
... 
>>> GetInt = GetIntMaker(1)
>>> GetInt()
1
于 2009-02-06T17:06:47.493 に答える
1

ネストされた関数は、単に別の関数の本体内で定義された関数です。なんで?私が頭のてっぺんから考えることができた唯一の理由は、ヘルパーまたはユーティリティ関数です。

これは不自然な例ですが、我慢してください。2つのクエリの結果に基づいて動作し、クエリの1つからの値をオブジェクトに入力する必要がある関数があるとします。次のようなことができます。

function process(qryResult q1, qryResult q2) {

   object o;
   if (q1.someprop == "useme") {
       o.prop1 = q1.prop1;
       o.prop2 = q1.prop2;
       o.prop3 = q1.prop3;
   } else if (q2.someprop == "useme") {
       o.prop1 = q2.prop1;
       o.prop2 = q2.prop2;
       o.prop3 = q2.prop3;
   }

   return o;

}

20個のプロパティがある場合は、コードを複製してオブジェクトを何度も設定すると、巨大な関数になります。単純なネストされた関数を追加して、クエリからオブジェクトへのプロパティのコピーを実行できます。このような:

function process(qryResult q1, qryResult q2) {

   object o;
   if (q1.someprop == "useme") {
       fillObject(o,q1);
   } else if (q2.someprop == "useme") {
       fillObject(o,q2);
   }

   return o;

   function fillObject(object o, qryResult q) {
       o.prop1 = q.prop1;
       o.prop2 = q.prop2;
       o.prop3 = q.prop3;
   }


}

それは物事を少しきれいに保ちます。入れ子関数である必要がありますか?いいえ。ただし、このコピーを実行する必要があるのがプロセス関数だけである場合は、この方法で実行することをお勧めします。

于 2009-02-06T17:11:12.803 に答える
1

(C#):これを使用して、オブジェクトブラウザーの表示を簡素化し、クラスをより適切に構造化します。クラスとしてトラッククラスにネストされたホイール。

この詳細を忘れないでください:「ネストされたタイプは、継承されたプライベートまたは保護されたメンバーを含む、包含タイプのプライベートおよび保護されたメンバーにアクセスできます。」

于 2009-02-06T17:00:08.957 に答える
1

ネストされた関数を使用すると、その関数内の 1 つの関数の内部動作にのみ関連するコードをカプセル化できますが、読みやすさや一般化のためにそのコードを分離することもできます。一部の実装では、外部スコープへのアクセスも許可します。D:

int doStuff() {
    int result;
    void cleanUpReturn() {
        myResource1.release();
        myResource2.release();
        return result * 2 + 1;
    }

    auto myResource1 = getSomeResource();
    auto myResource2 = getSomeOtherResource();
    if(someCondition) {
        return cleanUpReturn();
    } else {
        doSomeOtherStuff();
        return cleanUpReturn();
    }
}

もちろん、この場合は RAII で処理することもできますが、これは単純な例です。

于 2009-02-06T17:03:09.527 に答える