如何添加两个Optionals?

时间:2017-12-21 21:13:48

标签: swift optional

我来自C ++背景,但我正在为MetalKit学习Swift 4。由于我只习惯使用C ++,因此全部关注"选项"对我来说有点陌生。在阅读Apple发布的Swift 4书的同时,我遇到了以下问题:

说我有两个"字符串" s a和b:

let a = "12"
let b = "24"

我想将这两者加在一起。编写

是不好的做法和非法行为
let c = Int(a) + Int(b)

因为a和b都是字符串,所以它们在Int中的类型转换是可选的:转换可能失败了。解决方案似乎是

if let c = Int(a), let d = Int(b)
{
   let e = c + d
}

但这有点麻烦:我复制的方式比在C程序中更多,我可以简单地添加a和b,然后测试结果是否有值。是否有更有效,更好的方法来执行此添加?

5 个答案:

答案 0 :(得分:1)

正如@rmaddy在评论中所说,这是选择的全部意义。它应该让你工作,从而产生更安全的代码。

如果您愿意进行一些前期工作,可以创建一个自定义运算符,如果结果转换为Int nil,则会抛出错误:

enum Err: Error {
    case nilValue
}

func +(lhs: String, rhs: String)throws -> Int {
    guard let c = Int(lhs), let d = Int(rhs) else {
        throw Err.nilValue
    }
    return c + d
}

(您可能需要在此代码段中添加infix operator +。)

然后您可以像这样使用它:

do {
    let i: Int = try a + b
    print(i)
} catch {
    // Catch error here
}

或使用try?。如果抛出错误,这将返回nil

let i: Int? = try? a + b

如果您不想使用类型注释,您可以给操作员一个不同的名称,即:

infix operator +?: AdditionPrecedence
func +?(lhs: String, rhs: String)throws -> Int

答案 1 :(得分:1)

如果您正在寻找一种方法来写一行,您可以利用mapflatMap变体来获得选项:

let a = "12"
let b = "24"
let c = Int(a).flatMap { aʹ in Int(b).map { bʹ in aʹ + bʹ }}

c的类型为Optional<Int>nilInt(a)失败时为Int(b)。外部地图操作必须是flatMap才能获得正确的结果类型。如果您将flatMap替换为map,则c的类型将为Optional<Optional<Int>>Int??

您是否认为这种可读性至少部分是因为熟悉映射选项的概念。根据我的经验,大多数Swift开发人员更喜欢使用if let进行展开,即使这会产生多行。

另一种选择:包装这种展开两个选项的模式,并将函数应用于泛型函数中的展开值:

func unwrapAndApply<A, B, Result>(_ a: A?, _ b: B?, _ f: (A, B) -> Result) -> Result? {
    guard let a = a, let b = b else { return nil }
    return f(a, b)
}

此函数适用于所有输入,无论基础类型如何。现在你可以写这个来执行添加:

let d = unwrapAndApply(Int(a), Int(b), +)

unwrapAndApply函数仅适用于两个输入参数。如果您需要三个,四个,五个......输入的相同功能,则必须编写额外的unwrapAndApply重载,这些重载将获取相应数量的参数。

答案 2 :(得分:0)

除了您展示的if let方法之外,还有几种方法可以处理选项。

一种是在您使用选项的代码块的开头放置一个guard let语句。这样可以避免大量嵌套if let语句:

guard let c = Int(a), let d = Int(b) else { return }

// use `c` and `d` as you please
// ...

您还可以使用nil coalescing运算符来定义默认值(例如0):

let c = (Int(a) ?? 0) + (Int(b) ?? 0)

在这种情况下,如果Int(a)Int(b)失败,它们将分别替换为0。现在cInt而不是Int?,可以自由使用而无需解包。这可能适合也可能不适合,具体取决于使用默认值而非预期值时可能发生的情况。

进一步阅读:What is an optional value in Swift?

或者,您可以创建一个自定义运算符,以允许您以数字方式“添加”两个字符串:

infix operator +++: AdditionPrecedence

func +++(_ a: String, _ b: String) -> Int? {
  if let intA = Int(a), let intB = Int(b) {
    return intA + intB
  } else {
    return nil
  }
}

// use it like so:
let c = "12" +++ "24"

// now c is Int? and you can check if the result is optional
if let d = c {

}

有关Swift文档中自定义运算符的更多信息:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html

答案 3 :(得分:0)

您可以通过多种方式执行此操作。如果你经常这样做,我会告诉你一个是合适的:

extension Int {

    static func addStrings(_ firstString: String, _ secondString: String) -> Int? {
        guard let firstNumber = Int(firstString) else { return nil }
        guard let secondNumber = Int(secondString) else { return nil }

        return firstNumber + secondNumber
    }
}

你应该像这样使用它:

let number = Int.addStrings("1", "2")

我正在使用Swift的一个很棒的功能 - 扩展。使用它们,您可以将方法和计算变量添加到您想要的每个类。在Swift开发方面,可选项和扩展是非常重要的事情。您应该仔细阅读Apple文档。

答案 4 :(得分:0)

您可以定义自己的运算符以将两个选项组合在一起:

let a = "12"
let b = "24"

infix operator ?+: AdditionPrecedence
func ?+ (left: Int?, right: Int?) -> Int? {
  guard let left = left,
    let right = right else {
      return nil
  }
  return left + right
}

let c = Int(a) ?+ Int(b)

结果是可选的。如果您不希望结果是可选的,则需要提供合适的默认值。例如,如果你认为0是合适的:

infix operator ?+: AdditionPrecedence
func ?+ (left: Int?, right: Int?) -> Int {
  guard let left = left,
    let right = right else {
      return 0
  }
  return left + right
}