【问题标题】:What is the practical use of nested functions in swift? [duplicate]swift中嵌套函数的实际用途是什么? [复制]
【发布时间】:2016-01-03 06:18:06
【问题描述】:

嵌套函数的实际用途是什么?它只会使代码更难阅读,并且不会使特定情况变得容易。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}

Source

【问题讨论】:

  • 你的意思是,与闭包相比?
  • 你检查我的答案了吗?
  • 是的,我期待更多答案。您展示了如何“可以”使用此功能。我问的是如何使用“它”。如果你的项目中有一部分代码是你实际使用的,那对我理解这个概念会更有帮助。谢谢
  • 我宁愿不同意你的观点。看起来不错。根据输入参数将函数返回给调用者已经非常实用了。
  • 我用它们来封装

标签: swift function nested-function


【解决方案1】:

在您的示例中,您可以创建一个variable,它也是一个function,如下所示:

var myStepFunction = chooseStepFunction(true)

myStepFunction(4)

嵌套函数的好处非常好。例如,假设您正在为计算器构建一个应用程序,您可以将所有逻辑都放在一个函数中,如下所示:

func doOperation(operation: String) -> ((Double, Double) -> Double)? {
    func plus(s: Double, d: Double) -> Double {
        return s + d
    }
    func min(s: Double, d: Double) -> Double{
        return s - d
    }
    switch operation {
        case "+":
            return plus
    case "-" :
        return min
    default :
        return nil
    }
}

var myOperationFunction = doOperation("-")?(4, 4) // 0
var myOperationFunction2 = doOperation("+")?(4, 5) //9

在某些情况下,您不允许查看某些功能的实现,或者不对其负责。然后将它们隐藏在其他函数中确实是一个好方法。例如,假设您的同事负责开发 plusmin 函数,他/她会这样做,而您只需使用外部函数。

它不同于闭包,因为在 clouser 中,将你的逻辑传递给其他逻辑,它会调用你的。它是一种插件。例如,调用 http 请求后,您可以传递您希望应用在收到服务器响应时执行的代码

【讨论】:

  • 为什么不直接使用私有函数呢?您的 func("+")?(4,5) 示例非常丑陋且难以阅读。这些都不是很好的实际用途,因为我认为这个任务会以其他方式更好地完成
  • @Esqarrouth 更好的是,只需使用闭包,或者更好的是,直接使用(+)(-) 作为闭包
【解决方案2】:

一个用例是递归数据结构上的操作

例如,考虑以下用于在二叉搜索树中搜索的代码:

func get(_ key: Key) -> Value? {
    func recursiveGet(cur: Node) -> Value? {
        if cur.key == key {
            return cur.val
        } else if key < cur.key {
            return cur.left != nil ? recursiveGet(cur: cur.left!) : nil
        } else {
            return cur.right != nil ? recursiveGet(cur: cur.right!) : nil
        }
    }

    if let root = self.root {
        return recursiveGet(cur: root)
    } else {
        return nil
    }
}

当然,您可以将递归转换为循环,取消嵌套函数。不过,我发现递归代码通常比迭代变体更清晰。 (权衡与运行时成本!)

您也可以将recursiveGet 定义为get 之外的(私有)成员,但这将是糟糕的设计(除非recursiveGet 用于多种方法)。

【讨论】:

    【解决方案3】:

    有“代码可以是数据”的原则(从Objective-C开始)。您可以传递代码,将其存储在变量中,将其与其他代码组合。这是一个非常强大的工具。坦率地说,如果我没有将代码视为数据的能力,那么我编写的大部分代码都会难写十倍,阅读难十倍。

    Swift 中的函数只是闭包,因此嵌套函数非常有意义,因为当您不想使用众多可用快捷方式之一时,这就是您编写闭包的方式。

    【讨论】:

      【解决方案4】:

      我认为您问题的核心是:为什么不使用私有函数而不是丑陋的嵌套函数?

      简单地说,嵌套函数可以简化可读性封装

      同样有人会问,(函数的)局部变量与实例变量的实际用途是什么?对我来说,这确实是同一个问题。只是嵌套函数不太常见。

      可读性

      仍然可以从类中的其他函数访问私有函数。嵌套函数并非如此。您是在告诉您的开发人员,这仅属于此函数(与它们属于整个类的私有函数相反)。退后一步,不要乱用它,如果您需要类似的功能,请自己编写!

      当你看到一个私有函数时,你必须思考,哪个函数会调用它。它是第一个函数还是最后一个函数?让我搜索一下。然而,使用嵌套函数,您不必上下查找。已经知道哪个函数会调用它。

      此外,如果您有 5 个私有函数,其中 3 个在单个公共函数中调用,那么通过将它们全部嵌套在同一个公共函数下,您就可以与其他开发人员沟通这些私有函数是相关的。

      简而言之,就像您不想污染公共命名空间一样,您也不想污染私有命名空间。

      封装

      另一个方便之处是它可以访问其父函数的所有本地参数。您不再需要传递它们。这最终意味着要测试的函数减少了,因为您已经将一个函数包装在另一个函数中。此外,如果您在非嵌套函数块中调用该函数,则不必将其包装到 self 或考虑创建泄漏。这是因为嵌套函数的生命周期与其包含函数的生命周期相关联。

      另一个用例是当你的类中有非常相似的函数时,比如说你有:

      extractAllHebrewNames() // right to left language
      extractAllAmericanNames() // left to right language
      extractAllJapaneseNames() // top to bottom language
      

      现在,如果您有一个名为 printName 的私有函数(用于打印名称),如果您根据语言切换大小写,它会起作用,但如果您不/不能这样做怎么办。相反,您可以在每个单独的提取函数中编写自己的嵌套函数(它们现在都可以具有完全相同的名称。因为每个函数都位于不同的命名空间中。)并打印名称。

      你的问题有点类似,为什么不使用“if else”而不是“switch case”。

      我认为这只是提供了一种便利(对于可以在不使用嵌套函数的情况下处理的事情)。

      注意:嵌套函数应该在函数中的调用点之前编写。

      错误:在声明之前使用局部变量“嵌套”

      func doSomething(){
          nested()
          
          func nested(){
              
          }
      }
      

      没有错误:

      func doSomething(){
          func nested(){
              
          }
          
          nested()
      }
      

      类似地,嵌套函数中使用的局部变量必须在嵌套函数之前声明


      注意:

      如果您使用嵌套函数,则编译器不会强制执行 self 检查。

      编译器不够聪明。它不会抛出以下错误:

      Call to method 'doZ' in closure requires explicit 'self.' to make capture semantics explicit 
      

      这可能导致难以发现内存泄漏。例如

      class P {
          var name: String
          
          init(name: String) {
              print("p was allocated")
              self.name = name
          }
          
          func weaklyNested() {
              weak var _self = self
              func doX() {
                  print("nested:", _self?.name as Any)
              }
              
              DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                  doX()
              }
          }
          
          func stronglyNested() {
              func doZ() {
                  print("nested:", name)
              }
              
              DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                  doZ() // will NOT throw error of: `Call to method 'doZ' in closure requires explicit 'self.' to make capture semantics explicit`
              }
          }
          
          deinit {
              print("class P was deinitialized")
          }
      }
      
      class H {
          var p: P
          
          init(p: P) {
              self.p = p
          }
      }
      
      var h1: H? = H(p: P(name: "john"))
      h1?.p.weaklyNested()
      h1 = nil // will deallocate immediately, then print nil after 2 seconds
      
      var h2: H? = H(p: P(name: "john"))
      h2?.p.stronglyNested()
      h2 = nil // will NOT deallocate immediately, will print "john" after 2 seconds, then deallocates
      

      tl;dr 解决方案:

      • 使用weak var _self = self 并在嵌套函数引用中使用selfweak 引用。
      • 根本不要使用嵌套函数 :( 或者不要在嵌套函数中使用实例变量。

      这部分是感谢Is self captured within a nested function?的原帖写的

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-25
        • 1970-01-01
        • 2021-04-14
        • 2011-10-29
        • 2019-07-24
        • 2011-03-14
        • 2016-08-06
        • 1970-01-01
        相关资源
        最近更新 更多