2015-12-02 71 views
3

考虑Foo类型:调用SequenceType.forEach时,有没有办法引用实例函数?

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) 

但雨燕2.1的,这​​是不是一个正确的语法。这种参考函数的方式类似于以下内容:

func bar2(foo: Foo) -> Void { 
    print("some boring print") 
} 

someFoos.forEach(bar2) 

有没有更好的方法来引用实例函数?你更喜欢写这样的表达方式?

+0

不清楚问题是什么。什么是不喜欢'someFoos.forEach {$ 0.bar()}'?还不清楚你的'if'结构是否有意完成。 – matt

+0

@matt'someFoos.forEach {$ 0.bar()}'很好,但IMO像'someFoos.forEach(Foo.bar)'会更好(更容易阅读)。 'if'语句只是为了说明在某些情况下需要额外的括号来使用'someFoos.forEach {$ 0.bar()}'语法,这使得代码不易读。 –

+0

你并没有真正回答这个问题。 “IMO”不是问题,Stack Overflow拒绝基于意见的问题。你有什么问题?严重的是,你希望从这些“if”例子中得到什么样的行为?也许还有另一种方法,如果你只是说你想要的代码_do_。 – matt

回答

7

这里有两个不同的问题。所述尾随闭合语法 可以调用一个函数和最后一个参数时,可以使用一个封闭件, 所以

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中改变 ,但可能它不值得 的努力。


另一个问题是关于咖喱功能

someFoos.forEach(bar2) 

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

Foo.bar(someFoo) 

() ->()型封闭,并

Foo.bar(someFoo)() 

呼吁someFoo实例bar方法。

注:下面是不是意味着作为一个实际的建议, 但只是作为一个关于咖喱的功能和乐趣 与封示范)

直接传递Foo.bar作为参数传递给forEach()我们需要到 “交换”参数的顺序。 Haskell有用于该目的, “倒装”功能,并且还可能在夫特(参见例如How to write a flip method in Swift?):

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

然后flip(Foo.bar)具有类型() -> Foo ->(),所以 的bar方法的空隙参数可以是应用

flip(Foo.bar)() 

获得Foo ->()关闭,并

flip(Foo.bar)()(someFoo) 

someFoo实例上调用bar方法。 现在我们可以称之为

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

不使用闭合式{ .. }

如果isBaz是一个方法而不是财产

func isBaz() -> Bool { return false } 

那么你 可以做同样的,如果表达式:

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

同样,这只是意味着作为示范。另外属性 不是curried函数,所以这个不能用 你的isBaz属性来完成。

+2

感谢您花时间写出如此详尽的答案!有很多食物需要考虑! –

1

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

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

我的书概述了如何在Swift中缩写匿名函数:http://www.apeth.com/swiftBook/ch02.html#_anonymous_functions如果您不想使用缩写,则不必使用_any_。 – matt

相关问题