【发布时间】:2019-07-19 12:09:44
【问题描述】:
在 swift 中,您可以将函数作为参数传递给接受闭包的函数。这对于避免在使用运算符时在语法上污染代码特别有用。例如,你可以写一个总和如下:
let values = 0 ..< 10
let sum = values.reduce(0, +)
不幸的是,当 Swift 的推理无法从其他参数中确定预期闭包的类型时,重载函数可能会导致模棱两可的情况。例如,考虑下面的代码。最后一行无法编译,因为 Swift 无法确定我所指的 + 的“版本”。
func castAndCombine<T, U>(_ pair: (Any, Any), with fn: (T, T) -> U) -> U? {
guard let first = pair.0 as? T, let second = pair.1 as? T
else { return nil }
return fn(first, second)
}
// The following line cannot compile.
let x = castAndCombine((1, 2), with: +)
不幸的是,没有(或者至少我不知道)任何方式来指定我的意思是哪个+。尽管如此,我还是想出了两个解决这个问题的方法:
- 为函数添加参数以消除歧义:
func castAndCombine<T, U>(_ pair: (Any, Any), toType: T.Type, with fn: (T, T) -> U) -> U? {
// ...
}
let x = castAndCombine((1, 2), toType: Int.self, with: +)
- 保持函数签名不变,并使用带有显式类型注释的闭包:
func castAndCombine<T, U>(_ pair: (Any, Any), with fn: (T, T) -> U) -> U? {
// ...
}
let x = castAndCombine((1, 2), with: { (a: Int, b: Int) in a + b })
我个人不喜欢第一种解决方案,因为我觉得它不美观且使用起来不自然。但是,我想知道第二个是否会增加任何性能开销,因为创建了一个基本上包装单个函数而不添加任何行为的闭包。
有谁知道这种性能开销是否确实存在和/或在某种程度上很重要?
【问题讨论】:
-
另一个解决歧义的选项是
let x = castAndCombine((1, 2), with: +) as Int?——如果你真的必须定义一个采用Any操作数的函数。 (实际用例是什么?可能有更好的解决方案。) -
let x = castAndCombine((1, 2), with: (+) as (Int, Int) -> Int)也可以解决歧义。 -
用例远比我在这里发布的代码复杂,因为我试图提取一个最小的例子。我实际上正在为一种语言编写一个解释器,它的指令应该与 Swift 的指令匹配(以实现语言内置)。
标签: swift performance closures