Swift 的 falsiness

做者:Soroush Khanlou,原文連接,原文日期:2016-06-21
譯者:Crystal Sun;校對:千葉知風;定稿:CMBpython

在 Python 中,零和 None,以及空列表、字典和字符串,都有 falsy 值。 若是有 falsy 值,意味着能夠它在 if 語句中使用,且可使用 else。 例如,在 Python 中:git

python
if []:
    # will not be evaluated
else:
    # will be evaluated
    
if 0: 
    # will not be evaluated
else:
    # will be evaluated

而在 Swift 中,只有真正的布爾值才能夠用在 if 語句裏。 nil、空數組,或者其餘類型的值出如今在 if 語句中會致使編譯失敗:github

if Array<String>() {
// error: type 'Array<String>' does not conform to protocol 'BooleanType'

Python 和 Swift 都具備一致性,這是好事。在 Swift 裏,只有布爾值能夠做爲 if 語句的判斷條件。而在 Python 中,每一個集合類型、整數、nil 值(None)和布爾均可以做爲 if 語句的判斷條件。一致性看起來彷佛簡單,直到你注意到其餘語言有多糟糕。swift

JavaScript 纔是真正的垃圾秀。 falsenullundefined 是 falsy,這或多或少還能忍受。可是 0「」 也是 falsy,即便 []{} 是 truthy。 (還有不要嘗試解釋 JavaScript 的相等行爲數組

雖然我很愛 Objective-C,但它仍然是不具備一致性的語言。 nilNOfalse0 是 falsy,而 @ []@ {}@ 0@NO 是 truthy。 Ruby 在這方面是最好的,只容許 nilfalse 是 falsy。比起 Ruby,我更喜歡 Swift 的絕對嚴格。app

一致性雖好,但效用更重要。Swift 的 falsiness 規則雖然好,不過仍是 Python 的規則實用。this

我有兩個證據能夠證實,爲何 Python 的規則比 Swift 的(以及幾乎全部其餘語言的)更實用。lua

第一個證據是 Rails 的 ActiveSupport 裏的 present? 方法。我以前寫過 present? 的方法,見這裏 。簡而言之,可規避 Ruby 運行時惟一不可變的 falsiness。每一個對象均可以規定若是 #present?.nil 不存在,包括空數組和字符串。能夠給對象重寫 present?,若是有自定義集合或空對象。這樣會致使你的代碼這樣:翻譯

if myObject.present? {
    
} else {

}

重寫 Ruby 頑固的 falsiness。 ActiveSupport 只是單純有用,這也是爲何調用 .present? (和 .blank? )老是散佈在 Rails 的代碼庫裏。code

第二個證據是,Swift 在條件句中處理可選值時是多麼尷尬。 留意一下想在代碼中檢查一個東西是 empty 仍是 nil ,得進行多少操做。而這種檢查對我而言很是常見。例如,UITextField 的 text 屬性是可選的,若是想檢查 text 是否存在,就要進行相似尷尬的操做了:

if let text = textField.text where !text.isEmpty {
    // we have some text
} else {
    // the string is either empty or nil
}

若是這樣的代碼都讓你不以爲尷尬,那來嘗試顛倒條件。 我會等你噠。 (提示:不能只刪除 not 運算符)

很快,你會專門爲字符串添加 Optional 方法

protocol TextContaining {
    var isEmpty: Bool { get}
}

extension String: TextContaining { }

extension Optional where Wrapped: TextContaining {
    var isEmpty: Bool {
        switch self {
        case let .Some(value):
            return value.isEmpty
        case .None:
            return true
        }
    }
}

你沒必要這樣生活! 你值得擁有美好的事情!(好比 Python 的 falsiness)。

不像我提到的其餘語言,Swift 是一個高度動態的語言。 容許添加代碼來更改編譯器的 falsiness 行爲。

這是 BooleanType 的文檔:

符合布爾型協議的類型能夠用於條件語句(if,while,C-style for)和其餘邏輯值上下文(例如,guard 語句)。

是的的的的的。

Swift 只有三個類型符合 BooleanType 類型, Bool,DarwinBoolean和ObjCBool。 不建議將此集合擴展爲包含表示多個簡單布爾值的類型。

Swift 只有三個類型符合 BooleanType 類型, Bool,DarwinBoolean和ObjCBool。 不建議將此集合擴展爲包含表示多個簡單布爾值的類型。

對不起,Chris Lattner,我要放手一搏了:

extension String: BooleanType {
    public var boolValue: Bool {
        return !self.isEmpty
    }
}

完成了!如今咱們能夠在條件語句中使用字符串了,代碼如期編譯成功:

if "" {
    // this code will not be executed
} else {
    // this code will be executed    
}

能夠對 DictionaryArray 作一樣的事情。對可選值來講,咱們應該檢查一下 Wrapped 類型是否遵循 BooleanType

extension Optional: BooleanType {
    public var boolValue: Bool {
        switch self {
        case .None:
            return false
        case .Some(let wrapped):
            if let booleanable = wrapped as? BooleanType {
                return booleanable.boolValue
            }
            return true
        }
    }
}

如今,若是有個拆包的布爾值,實現效果會如你所願,不會拋出編譯報錯,不須要在處理奇怪的 optionalBool ?? true 了。

最大的問題是「我應該在生產代碼中這樣作嗎?」。答案是...也許能夠吧。若是你在寫一個開源庫,可能被其餘的開發者使用,那絕對不要這樣作。若是是在本身寫的 App 裏添加這樣的代碼,那不要牽扯任何第三方代碼,否則會編譯不過的。Swift 是強類型的語言,甚至沒法編譯 myArray == false 這樣的代碼。

我以爲 Swift 這樣作很是棒:建構在一個個小的可組合的塊(好比 BooleanType)上,標準庫能夠在語言中進行自定義(像是 ArrayLiteralConvention 這樣的類型都遵循類似的模式)。使人驚訝的是,沒有一個「動態」語言容許這種基本語言結構的突變。同時,我不得不決定是否要在全部的地方進行這樣的嘗試。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索