调用SequenceType.forEach时有没有办法引用实例函数?

考虑typesFoo

 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) 

有没有更好的方法来引用实例function? 你更喜欢写这样的expression式?

这里有两个不同的问题。 所以在调用函数时可以使用尾随闭包语法 ,而最后一个参数是闭包

 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中发生变化,但可能是不值得的。


另一个问题是关于咖喱饭的function

 someFoos.forEach(bar2) 

编译是因为bar2的types是Foo -> Void ,而这正是forEach()方法所期望的。 另一方面, Foo.bar是一个curried函数(参见http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/ ),它将实例作为第一个参数。 它有typesFoo -> () -> () 。 所以

 Foo.bar(someFoo) 

是一个封闭types() -> () ,和

 Foo.bar(someFoo)() 

调用someFoo实例上的bar方法。

注意:以下内容不是实际的build议,而只是作为一个关于咖喱function和closures乐趣的示范!)

要直接将Foo.bar作为parameter passing给forEach()我们需要“交换”参数的顺序。 Haskell为此目的有一个“翻转”function,在Swift中也是可能的(请参阅如何在Swift 中编写翻转方法? ):

 func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C { return { b in { a in f(a)(b) } } } 

然后, flip(Foo.bar)具有type () -> Foo -> () ,所以可以应用bar方法的void参数

 flip(Foo.bar)() 

得到一个Foo -> ()闭包,并且

 flip(Foo.bar)()(someFoo) 

调用someFoo实例上的bar方法。 现在我们可以打电话了

 someFoos.forEach (flip(Foo.bar)()) 

不使用闭包expression式{ .. }

如果isBaz是一种方法而不是属性

 func isBaz() -> Bool { return false } 

那么你可以在ifexpression式中做同样的事情:

 if someFoos.contains(flip(Foo.isBaz)()) { // ... } 

再一次,这只是一个示范。 另外属性不是curried函数,所以这不能用你的isBaz属性来完成。

$0语法可以帮助你创build一个快捷方式,但是如果你不喜欢,你可以使用更完整的表单:

 someFoos.forEach { thisFoo in thisFoo.bar() }