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

时间:2015-04-16 23:34:47

标签: swift optional

我有两种不同的场景,我需要测试"选项"可选类型。如果变量是.None.Some而不是使用笨重的switch语句,我无法计算如何显式测试。如何使用Someness语句测试if

场景1

我正在写一个地址格式化程序,我的输入是一些字符串?类型。在此示例中,(str != 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 $0! } //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

这是零测试无法运行的地方。如果有什么东西是String?它可以是.None.Some(.None).Some(.Some(String))中的任何一个。在我的例子中,变量携带来自api调用的recordID,该调用可能完全缺失(.None),值(.Some(.Some("ABDEFG"))或显式NULL({{1} })。

.Some(.None)

通过另一篇SO帖子,我发现了一个像这样的解决方法,但是对于一个随意的读者发生了什么并不是很清楚:

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
}

1 个答案:

答案 0 :(得分:3)

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

如果.Some没有删除,那么使用.Noneif let开关实际上是处理选项的第二种方法。但它几乎总是这样,特别是现在你可以在一个语句中执行多个if let,跟随最新版本的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 let与{{where一起使用1}}子句:

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

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