考虑类型Foo:
class Foo {
var isBaz: Bool {
return false
}
func bar() {
print("some boring print")
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们说我想遍历一个类实例集合并在每个类实例上调用一些函数:
let someFoos: [Foo] = [Foo(), Foo(), Foo()]
someFoos.forEach { $0.bar() }
Run Code Online (Sandbox Code Playgroud)
这种语法非常紧凑,但感觉有点尴尬.此外,它无法在任何地方使用.例如,在if声明条件中:
if someFoos.contains { $0.isBaz } {
// compiler error: statement cannot begin with a closure expression
}
if someFoos.contains($0.isBaz) {
// compiler error: anonymous closure argument not contained in a closure
}
if someFoos.contains({ $0.isBaz }) {
// this is correct, but requires extra pair of parentheses
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,写一些类似的东西会很好
someFoos.forEach(Foo.bar)
Run Code Online (Sandbox Code Playgroud)
但是从Swift 2.1开始,这不是一个正确的语法.这种引用函数的方式类似于以下内容:
func bar2(foo: Foo) -> Void {
print("some boring print")
}
someFoos.forEach(bar2)
Run Code Online (Sandbox Code Playgroud)
有没有更好的方法来引用实例函数?你更喜欢写这样的表达方式?
这里有两个不同的问题.的拖尾闭合语法 可以在调用函数时,可以使用与最后一个参数是一个闭合,所以
let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }
Run Code Online (Sandbox Code Playgroud)
是完全相同的.但是,在if语句的条件下,尾随闭包语法可能会有问题:
if someFoos.contains({ $0.isBaz }) { } // OK
if someFoos.contains { $0.isBaz } { } // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke
Run Code Online (Sandbox Code Playgroud)
我们只能推测为什么第二个不起作用.可能是编译器将第一个{ 作为if-body的开头.也许这将在Swift的未来版本中发生变化,但可能不值得努力.
另一个问题是关于curried函数.
someFoos.forEach(bar2)
Run Code Online (Sandbox Code Playgroud)
compiles因为bar2有类型Foo -> Void,这正是forEach()方法所期望的.Foo.bar,在另一方面,是咖喱功能(参见http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/)这需要实例作为第一个参数.它有类型Foo -> () -> ().所以
Foo.bar(someFoo)
Run Code Online (Sandbox Code Playgroud)
是一个类型的闭包() -> (),和
Foo.bar(someFoo)()
Run Code Online (Sandbox Code Playgroud)
bar在someFoo实例上调用该方法.
(注意:以下内容并不是一个实际的推荐,而只是作为关于curried函数和闭包乐趣的演示!)
要Foo.bar直接作为参数传递,forEach()我们需要"交换"参数的顺序.Haskell为此目的有一个"翻转"函数,在Swift中也是可能的(参见例如如何在Swift 中编写一个翻转方法?):
func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C {
return { b in { a in f(a)(b) } }
}
Run Code Online (Sandbox Code Playgroud)
然后flip(Foo.bar)有类型() -> Foo -> (),因此bar可以应用方法的void参数
flip(Foo.bar)()
Run Code Online (Sandbox Code Playgroud)
得到一个Foo -> ()关闭,和
flip(Foo.bar)()(someFoo)
Run Code Online (Sandbox Code Playgroud)
bar在someFoo实例上调用该方法.现在我们可以打电话了
someFoos.forEach (flip(Foo.bar)())
Run Code Online (Sandbox Code Playgroud)
不使用闭包表达式{ .. }!!
如果isBaz是方法而不是属性
func isBaz() -> Bool { return false }
Run Code Online (Sandbox Code Playgroud)
然后你可以在if-expression中做同样的事情:
if someFoos.contains(flip(Foo.isBaz)()) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
同样,这只是一个示范.同样性质
不是令行禁止的功能,所以这不能与您进行isBaz财产.
| 归档时间: |
|
| 查看次数: |
467 次 |
| 最近记录: |