【问题标题】:What are nested functions? What are they for?什么是嵌套函数?它们是干什么用的?
【发布时间】:2009-02-06 16:52:22
【问题描述】:

我从未使用过嵌套函数,但在几种语言中看到过对它们的引用(以及我认为相关的嵌套类)。

  • 什么是嵌套函数?
  • 为什么?!?
  • 您可以用其他任何方式都无法做到的嵌套函数做什么?
  • 如果没有嵌套函数,这很难或不优雅,您可以用嵌套函数做什么?

我假设嵌套函数只是将所有东西都视为对象的产物,如果对象可以包含其他对象,那么它就会遵循。

嵌套函数是否有范围(一般来说,我认为语言在这方面有所不同)就像函数内的变量有范围一样?

如果您不确定您的答案是否与语言无关,请添加您所引用的语言。

-亚当

【问题讨论】:

    标签: information-hiding nested-function


    【解决方案1】:

    嵌套函数的一种流行用法是closures。在具有一流函数的lexically scoped 语言中,可以使用函数来存储数据。 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
    

    在这个例子中,我们将函数 counter 嵌套在函数 make-counter 中,通过返回这个内部函数,我们可以访问在定义 counter 时可用的数据。此信息对于这个 mycounter 实例是私有的——如果我们要创建另一个计数器,它将使用不同的位置来存储内部计数。继续上一个例子:

    (define mycounter2 (make-counter))
    
    (mycounter2)                      ; returns 1
    
    (mycounter)                       ; returns 3
    

    【讨论】:

      【解决方案2】:

      当只有一种方法会调用它时,它对递归很有用

      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 方法。

      【讨论】:

        【解决方案3】:

        嵌套函数只是另一个函数中的一个函数。

        是的,这是一切都是对象的结果。因为你可以让变量只在函数的作用域中可见,并且变量可以指向函数,所以你可以拥有一个被局部变量引用的函数。

        我不认为你可以用嵌套函数做任何你绝对不能没有的事情。不过,很多时候它是有道理的。也就是说,只要一个函数是某个其他函数的“子函数”。

        对我来说,一个常见的用例是当一个函数执行很多复杂的逻辑,但是对于逻辑所决定的所有情况,函数计算/返回的内容很容易抽象出来。

        【讨论】:

          【解决方案4】:

          (C#) : 我用它来简化对象浏览器视图,并更好地构建我的类。 作为类 Wheel 嵌套在 Truck 类中。

          不要忘记这个细节: “嵌套类型可以访问包含类型的私有和受保护成员,包括任何继承的私有或受保护成员。”

          【讨论】:

            【解决方案5】:

            嵌套函数允许您封装仅与该函数中的一个函数的内部工作相关的代码,同时仍允许您将该代码分离出来以提高可读性或泛化性。在一些实现中,它们还允许访问外部范围。在 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 来处理,但这只是一个简单的例子。

            【讨论】:

              【解决方案6】:

              如果您需要将一个函数作为参数传递给另一个函数,它们也很有用。它们还可以用于为工厂函数制作工厂函数(在 Python 中):

              >>> def GetIntMaker(x):
              ...   def GetInt():
              ...     return x
              ...   return GetInt
              ... 
              >>> GetInt = GetIntMaker(1)
              >>> GetInt()
              1
              

              【讨论】:

                【解决方案7】:

                嵌套函数只是在另一个函数体内定义的函数。为什么?我能想到的唯一原因是辅助函数或实用函数。

                这是一个人为的例子,但请耐心等待。假设您有一个函数必须对两个查询的结果进行操作,并用其中一个查询的值填充一个对象。您可以执行以下操作。

                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;
                   }
                
                
                }
                

                它让事情变得更干净一些。它必须是嵌套函数吗?不,但如果流程函数是唯一需要执行此副本的函数,您可能希望这样做。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-01-17
                  • 2013-03-18
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多