2015-12-02 99 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

相關問題