在Swift中,我使用if let声明来检查我的对象是否不是nil
if let obj = optionalObj
{
}
Run Code Online (Sandbox Code Playgroud)
但有时,我必须面对连续的if let声明
if let obj = optionalObj
{
if let a = obj.a
{
if let b = a.b
{
// do stuff
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找避免连续if let声明的方法.
我会尝试类似的东西:
if let obj = optionalObj && if let a = obj.a && if let b = a.b
{
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
但swift编译器不允许这样做.
有什么建议吗?
Gab*_*lla 27
在swift 1.2中你可以做到
if let a = optA, let b = optB {
doStuff(a, b)
}
Run Code Online (Sandbox Code Playgroud)
在您的特定情况下,您可以使用可选链接:
if let b = optionaObj?.a?.b {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
现在,如果你需要做类似的事情
if let a = optA {
if let b = optB {
doStuff(a, b)
}
}
Run Code Online (Sandbox Code Playgroud)
你运气不好,因为你不能使用可选的链接.
你喜欢酷的单线吗?
doStuff <^> optA <*> optB
Run Code Online (Sandbox Code Playgroud)
继续阅读.对于它看起来多么吓人,这真的很强大,并不像看起来那么疯狂.
幸运的是,这是一个使用函数式编程方法轻松解决的问题.您可以使用Applicative抽象并提供一种apply方法来组合多个选项.
以下是一个例子,摘自http://robots.thoughtbot.com/functional-swift-for-dealing-with-optional-values
首先,我们需要一个函数,仅在函数包含某些内容时才将函数应用于可选值
// this function is usually called fmap, and it's represented by a <$> operator
// in many functional languages, but <$> is not allowed by swift syntax, so we'll
// use <^> instead
infix operator <^> { associativity left }
func <^><A, B>(f: A -> B, a: A?) -> B? {
switch a {
case .Some(let x): return f(x)
case .None: return .None
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以使用apply组合多个选项,我们会调用它,<*>因为我们很酷(我们知道一些Haskell)
// <*> is the commonly-accepted symbol for apply
infix operator <*> { associativity left }
func <*><A, B>(f: (A -> B)?, a: A?) -> B? {
switch f {
case .Some(let value): return value <^> a
case .None: return .None
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以重写我们的例子
doStuff <^> optA <*> optB
Run Code Online (Sandbox Code Playgroud)
这将是有效的,只要doStuff是以咖喱形式(见下文),即
func doStuff(a: A)(b: B) -> C { ... }
Run Code Online (Sandbox Code Playgroud)
整个事物的结果是一个可选值,nil或者是结果doStuff
这是一个完整的例子,您可以在操场上尝试
func sum(a: Int)(b: Int) -> Int { return a + b }
let optA: Int? = 1
let optB: Int? = nil
let optC: Int? = 2
sum <^> optA <*> optB // nil
sum <^> optA <*> optC // Some 3
Run Code Online (Sandbox Code Playgroud)
最后要注意的是,将函数转换为curried形式非常简单.例如,如果您有一个带两个参数的函数:
func curry<A, B, C>(f: (A, B) -> C) -> A -> B -> C {
return { a in { b in f(a,b) } }
}
Run Code Online (Sandbox Code Playgroud)
现在你可以讨论任何双参数函数,+例如
curry(+) <^> optA <*> optC // Some 3
Run Code Online (Sandbox Code Playgroud)
我前段时间写了一篇关于替代品的小文章:https://gist.github.com/pyrtsa/77978129090f6114e9fb
其他答案中尚未提及的一种方法,我有点喜欢,是添加一堆重载every函数:
func every<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)): return .Some((a, b))
default: return .None
}
}
func every<A, B, C>(a: A?, b: B?, c: C?) -> (A, B, C)? {
switch (a, b, c) {
case let (.Some(a), .Some(b), .Some(c)): return .Some((a, b, c))
default: return .None
}
}
// and so on...
Run Code Online (Sandbox Code Playgroud)
这些可以在if let语句,case表达式以及optional.map(...)链中使用:
// 1.
var foo: Foo?
if let (name, phone) = every(parsedName, parsedPhone) {
foo = ...
}
// 2.
switch every(parsedName, parsedPhone) {
case let (name, phone): foo = ...
default: foo = nil
}
// 3.
foo = every(parsedName, parsedPhone).map{name, phone in ...}
Run Code Online (Sandbox Code Playgroud)
必须添加重载every是样板,但只需要在库中完成一次.类似地,使用Applicative Functor方法(即使用<^>和<*>运算符),您需要以某种方式创建curried函数,这也会在某处产生一些样板.
| 归档时间: |
|
| 查看次数: |
3010 次 |
| 最近记录: |