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