如何测试字符串的可选性?

How to test the Optionality of a String?

我有两种不同的场景需要测试可选类型的 "optionality"。除了使用笨拙的 switch 语句之外,我无法弄清楚如何 显式 测试变量是 .None 还是 .Some 。如何使用 if 语句测试 Someness

场景 1

我正在写一个地址格式化程序,我的输入是一些字符串?类型。在此示例中,(str != nil) 的简单测试将起作用。但是,由于我的另一个需要是在处理 'double optional' 和 nil 测试时无法区分 .Some(.None).None 解决此问题的方法也将解决该问题。

这是一个使用 switch

的版本
let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"

let components = [address1, address2, apt].filter( { (c) -> Bool in
    switch c {
    case .Some: return true
    case .None: return false
    }
}).map { return [=10=]! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"

我想看到的是 if:

let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
    return (c == .Some)
}
print(", ".join(nice))

场景 2

这是 nil 测试不起作用的地方。如果某物是字符串??它可以是 .None.Some(.None).Some(.Some(String)) 中的任何一个。在我的例子中,该变量携带来自 api 调用的 recordID,该调用可能完全缺失 (.None)、一个值 (.Some(.Some("ABDEFG")),或显式 NULL ( .Some(.None))。

let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"

if teamNoneNone == nil {
    print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
    print("teamNoneNone is not nil")    
}

if teamSomeNone == nil {
    print("teamSomeNone is nil")
} else {
    print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}

if teamSomeSome == nil {
    print("teamSomeSome is nil but is it .None? We don't know!")
} else {
    print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}

通过另一个 SO post 我找到了这样的解决方法,但不太清楚临时 reader:

发生了什么
if let team: String? = teamSomeNone {
    print("teamSomeNone is Some(.None)") //prints
} else {
    print("teamSomeNone is .Some(.Some())")
}

if let 测试一个值是否为 .None,如果不是,则解包并将其绑定到 if 语句中的局部变量。

将 switch 与 .Some.None 一起使用实际上是处理可选值的次要方法,如果 if let 没有削减它的话。但它几乎总是如此,尤其是现在您可以在一条语句中执行多个 if lets,在最新发布的 Swift 1.2 投入生产之后。

想要过滤掉集合中的 nils 是一项很常见的任务,Haskell 有一个标准函数用于它,称为 catMaybe。这是一个版本,我将其称为 catSome,它可以在 Swift:

中发挥作用
func catSome<T>(source: [T?]) -> [T] {
    var result: [T] = []
    // iterate over the values
    for maybe in source {
        // if this value isn’t nil, unwrap it
        if let value = maybe {
            // and append it to the array
            result.append(value)
        }
    }
    return result
}

let someStrings: [String?] = ["123 Main St", nil, "101"]

catSome(someStrings)  // returns ["123 Main St", "101"]

双重包装的可选值有点麻烦,所以最好的解决方案是首先避免它们——通常是通过使用可选链或 flatMap.

但是如果你确实发现自己有一些,而你只关心内在价值,你可以使用双 if let:

展开它们
// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
    println("Fully unwrapped team is \(teamName)")
}

如果您想明确知道双可选是否在外部值内有一个内部 nil,但不是 nil 本身,您可以将 if letwhere 子句:

if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
    // this will be executed only if it was .Some(.None)
    println("SomeNone")
}

where 子句是一个额外的条件,可以应用于展开的值。