【问题标题】:How to unwrap double optionals in switch statement -- Swift如何在 switch 语句中解开双选项——Swift
【发布时间】:2019-07-29 16:29:41
【问题描述】:

xCode 10.2.1,未指定 Swift 语言(4、4.2、5)

我有两个双可选布尔值。我想在 switch 语句中解开它们......

func evaluate() -> Bool? {
    let lhs = try? getLhs() // lhs = Bool??; getLhs() returns Bool?
    let rhs = try? getRhs() // rhs = Bool??; getRhs() returns Bool?
    switch (lhs ?? nil, rhs ?? nil) {
    case (nil, nil):
        return nil
    case (nil, rhs):
        return rhs ?? nil
    case (lhs, nil):
        return lhs ?? nil
    default:
        guard case let lhs?? = lhs, case let rhs?? = rhs  else { return nil }
        return lhs || rhs
    }
}

这行得通,但我很好奇为什么我需要双重解开默认情况? 我在想一旦 lhs 和 rhs 从开关输入中解开,默认情况下可以使用 Bool?对于左手和右手。

更新——

感谢您的回答。是的,双重选项有点矫枉过正,但我​​真正想了解的是为什么我的默认设置要求我再次打开包装。我不完全理解 switch 如何与选项一起工作。 Alexander 帮助我理解了所有的迭代,而 vacawama 给了我一个干净的实现。

【问题讨论】:

  • do/catch 中将try? 替换为try
  • @rmaddy 但是如果尝试失败,我希望它返回 nil,而不是抛出错误
  • 这就是catch 的用途。只需return nil 中的catch
  • ?? nil 什么都不做。
  • 我真的无法回答那些即使尝试编译也没有提供足够代码的问题。没有人知道你的 getLhsgetRhs 是什么样子,或者这段代码在哪里,或者我们应该返回什么。

标签: swift switch-statement


【解决方案1】:

这行得通,但我很好奇为什么我需要双重解开默认值 案子?我在想,一旦 lhs 和 rhs 从 开关输入,默认情况下可以使用 Bool?对于 lhs 和 右。

当您更改 lhs ?? nilrhs ?? nil 时,您不会更改 lhsrhs。你正在创造新的价值。所以当你遇到default 的情况时,lhsrhs 仍然是Bool??。您可以使用let lhslet rhs 来捕获展开的值,就像我在下面的解决方案中所做的那样。


这是另一种方法。一些模式匹配更简洁:

switch (lhs as? Bool, rhs as? Bool) {
case (nil, nil):
    return nil
case (nil, let rhs):
    return rhs
case (let lhs, nil):
    return lhs
case (let lhs?, let rhs?):
    return lhs || rhs
}

说明

使用as? Bool 铸造Bool?? 会留下Bool?。模式匹配中的let rhslet lhs 捕获Bool? 值,以便可以返回它。在最终的caselet lhs?let rhs? 中,解开Bool? 的值以获得Bool 的值,以便可以执行||


测试用例

test(nil, nil) // nil
test(nil, false) // false
test(nil, true) // true
test(nil, Optional(nil)) // nil
test(nil, Optional(false)) // false
test(nil, Optional(true)) // true

test(false, nil) // false
test(false, false) // false
test(false, true) // true
test(false, Optional(nil)) // false
test(false, Optional(false)) // false
test(false, Optional(true)) // true

test(true, nil) // true
test(true, false) // true
test(true, true) // true
test(true, Optional(nil)) // true
test(true, Optional(false)) // true
test(true, Optional(true)) // true

test(Optional(nil), nil) // nil
test(Optional(nil), false) // false
test(Optional(nil), true) // true
test(Optional(nil), Optional(nil)) // nil
test(Optional(nil), Optional(false)) // false
test(Optional(nil), Optional(true)) // true

test(Optional(false), nil) // false
test(Optional(false), false) // false
test(Optional(false), true) // true
test(Optional(false), Optional(nil)) // false
test(Optional(false), Optional(false)) // false
test(Optional(false), Optional(true)) // true

test(Optional(true), nil) // true
test(Optional(true), false) // true
test(Optional(true), true) // true
test(Optional(true), Optional(nil)) // true
test(Optional(true), Optional(false)) // true
test(Optional(true), Optional(true)) // true

【讨论】:

  • 这更干净——谢谢。你能解释一下为什么你需要一个 let lhs?, let rhs 吗?即使 lhs 和 rhs 应该已经是 Bool 了?
  • @Mocha case let lhs? 在一个模式中类似于case let .some(lhs)。它绑定到可选值中的值,而不是整个可选值。
  • 我不知道你可以使用as T? 来折叠这样的嵌套可选性。完美!
【解决方案2】:
  • Bool 有 2 个值:
    • true
    • false
  • Bool? 有 3 个值,.none(又名 nil)和一个 .some 案例,其中包含 Bool 的 2 个可能案例中的每一个:
    • .some(true)
    • .some(false)
    • .none
  • Bool?? 有 4 个值,.none 和一个 .some 案例,包含 Bool? 的 3 种可能案例中的每一个:
    • .some(.some(true))
    • .some(.some(false))
    • .some(.none)
    • .none

为了对解进行初步近似,我们可以枚举所有可能的值:

func getLhs() throws -> Bool? { return nil }
func getRhs() throws -> Bool? { return nil }

func f() -> Bool? {

    let lhs: Bool?? = try? getLhs()
    let rhs: Bool?? = try? getRhs()
    switch (lhs, rhs) {
        case (.some(.some(true)), .some(.some(true))): return true
        case (.some(.some(true)), .some(.some(false))): return true
        case (.some(.some(true)), .some(.none)): return true
        case (.some(.some(true)), .none): return true

        case (.some(.some(false)), .some(.some(true))): return true // result of OR
        case (.some(.some(false)), .some(.some(false))): return false
        case (.some(.some(false)), .some(.none)): return false
        case (.some(.some(false)), .none): return false

        case (.some(.none), .some(.some(true))): return true
        case (.some(.none), .some(.some(false))): return false
        case (.some(.none), .some(.none)): return nil
        case (.some(.none), .none): return nil

        case (.none, .some(.some(true))): return true
        case (.none, .some(.some(false))): return false
        case (.none, .some(.none)): return nil
        case (.none, .none): return nil
    }
}

所以现在这显然很疯狂,但我们可以通过绑定变量来折叠案例:

switch (lhs, rhs) {
    case let (.some(.some(l)), .some(.some(r))): return l || r
    case let (.some(.some(l)), .some(.some(r))): return l || r
    case let (.some(.some(l)), .some(.none)): return l
    case let (.some(.some(l)), .none): return l

    case let (.some(.some(l)), .some(.some(r))): return l || r
    case let (.some(.some(l)), .some(.some(r))): return l || r
    case let (.some(.some(l)), .some(.none)): return l
    case let (.some(.some(l)), .none): return l

    case let (.some(.none), .some(.some(r))): return r
    case let (.some(.none), .some(.some(r))): return r
    case (.some(.none), .some(.none)): return nil
    case (.some(.none), .none): return nil

    case let (.none, .some(.some(r))): return r
    case let (.none, .some(.some(r))): return r
    case (.none, .some(.none)): return nil
    case (.none, .none): return nil
}

我们可以删除重复的案例:

switch (lhs, rhs) {
    case let (.some(.some(l)), .some(.some(r))): return l || r
    case let (.some(.some(l)), .some(.none)): return l
    case let (.some(.some(l)), .none): return l

    case let (.some(.none), .some(.some(r))): return r
    case (.some(.none), .some(.none)): return nil
    case (.some(.none), .none): return nil

    case let (.none, .some(.some(r))): return r
    case (.none, .some(.none)): return nil
    case (.none, .none): return nil
}

然后我们可以对所有嵌套的nil 案例进行分组:

switch (lhs, rhs) {
    case let (.some(.some(l)), .some(.some(r))): return l || r

    case let (.some(.some(l)), .some(.none)): return l
    case let (.some(.some(l)), .none): return l

    case let (.some(.none), .some(.some(r))): return r
    case let (.none, .some(.some(r))): return r

    case (.some(.none), .some(.none)),
        (.some(.none), .none),
        (.none, .some(.none)),
        (.none, .none):
        return nil
}

然后我们可以在default 中捕获所有这些:

switch (lhs, rhs) {
    case let (.some(.some(l)), .some(.some(r))): return l || r

    case let (.some(.some(l)), .some(.none)): return l
    case let (.some(.some(l)), .none): return l

    case let (.some(.none), .some(.some(r))): return r
    case let (.none, .some(.some(r))): return r

    default: return nil
}

但这整件事太疯狂了

为什么你有一个throws AND 返回Optional 的函数?让函数返回一个非可选的布尔值会更好,并让nil 案例由异常之一处理。或者,您可以返回一个Result<Bool, Error>,其中一种错误情况编码为nil

如果您必须坚持使用这些类型,switch 是完全错误的做法。您可以使用Optional.mapOptional.flatMap,但它们也会变得毛茸茸。

【讨论】:

  • 感谢彻底的迭代。我没有创建抛出并返回可选的函数。我必须使用它,因为它是非常纠结的代码库..
  • @Mocha 不,你没有。您可以将它包装在一个函数中:1)如果它不是nil,则返回解包结果 2)如果抛出错误,则重新抛出错误 2)如果结果是 nil,则抛出另一种错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 2022-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多