swift中嵌套函数的实际用途是啥? [复制]

Posted

技术标签:

【中文标题】swift中嵌套函数的实际用途是啥? [复制]【英文标题】: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

【问题讨论】:

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

【讨论】:

以上是关于swift中嵌套函数的实际用途是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Java内部类的用途是啥?嵌套类和内部类是一样的吗? [复制]

人们想要使用嵌套类的原因是啥? [复制]

嵌套函数的性能开销是多少? [复制]

检查 JavaScript 中是不是存在深度嵌套对象属性的最简单方法是啥? [复制]

嵌套函数有啥好处(一般/在 Swift 中)

python的init中self的实际用途是啥? [复制]