简化Swift枚举

时间:2020-04-12 05:14:27

标签: swift enums functional-programming

在Swift中尝试使用curring之后,我想到了下面的代码。我想看看是否有可能简化此枚举Operate。目前,我需要这样初始化:

let multiply = Operate.Multiply.op

我希望每种情况都具有一个相关联的值,该值可以直接返回一个闭包,而不必执行此switch块。这可能吗?

以下是您可以在Swift游乐场中运行的一些代码:

import Foundation

enum Operate {
    case Plus
    case Minus
    case Multiply
    case unsafeDivide

    var op: (Double) -> (Double) -> Double {
        get {
            switch self {
            case .Plus:
                return { n in
                    return { n + $0}
                }
            case .Minus:
                return { n in
                    return { n - $0}
                }
            case .Multiply:
                return { n in
                    return { n * $0}
                }
            case .unsafeDivide:
                return { n in
                    return { n / $0 }
                }
            }
        }
    }
}

let multiply = Operate.Multiply.op
let plus = Operate.Plus.op
let unsafeDivide = Operate.unsafeDivide.op
// 3 + (16 * 2) -> 35
plus(3)(multiply(16)(2))

奖金:我如何以“迅速”的方式处理unsafeDivide的错误,也就是说,要避免这种情况:

let unsafeDivide = Operate.unsafeDivide.op
unsafeDivide(2)(0)

1 个答案:

答案 0 :(得分:2)

您似乎正在做的是currying。您可以通过提取curry函数来删除很多重复的代码:

func curry<A,B,C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
    return { a in { b in f(a, b) } }
}

// ...

var op: (Double) -> (Double) -> Double {
    switch self {
    case .plus: // please follow Swift naming conventions, enum cases start with a lowercase
        return curry(+)
    case .minus:
        return curry(-)
    case .multiply:
        return curry(*)
    case .unsafeDivide:
        return curry(/)
    }
}

这看起来好多了。您似乎不喜欢switch语句,因此使用字典的方法如下:

var op: (Double) -> (Double) -> Double {
    let dict: [Operate: (Double, Double) -> Double] =
        [.plus: (+), .minus: (-), .multiply: (*), .unsafeDivide: (/)]
    return curry(dict[self]!)
}

事实上,您可以在Swift 5.2中使用新的callAsFunction功能,甚至在调用方也省略op一词:

func callAsFunction(_ a: Double) -> (Double) -> Double {
    op(a)
}

这允许您执行以下操作:

Operator.multiply(2)(3)

使用关联值的另一种方式:

enum Operate {
    case plus(Double)
    case minus(Double)
    case multiply(Double)
    case unsafeDivide(Double)

    func callAsFunction(_ b: Double) -> Double {
        switch self {
        case .plus(let a):
            return a + b
        case .minus(let a):
            return a - b
        case .multiply(let a):
            return a * b
        case .unsafeDivide(let a):
            return a / b
        }
    }
}

但是我个人不喜欢它,因为拥有关联值意味着您不能简单地使用==来比较枚举值以及其他限制。


不可能在编译时除以0,因为您传入的值可能不是编译时常数。如果只想检查编译时间常数,则可能需要像SwiftLint这样的静态代码分析器。在运行时,无论如何,Double 0的划分都是由IEEE标准定义的。它不会崩溃或什么。

相关问题