是否可以在Swift中创建“正数”类型?

时间:2015-12-14 05:15:02

标签: swift types static-analysis

很抱歉,如果这是一个愚蠢的问题,但我想知道在Swift中是否有办法创建一个专门保存严格大于零的数字的类型,并且在编译时强制执行值的“正面性”。

例如,我可以创建像

这样的代码
func divide(x: PositiveNumber, y: PositiveNumber){
    return x / y
}

这样

divide(1, 3)

有效但

divide(1, 0)

不会编译?

我能想出的最接近的结果是只有一个易错初始值设定项的结构,这个类型具有正值或为零:

struct PositiveNumber {
    let value: Float

    init?(value: Float){
        if value > 0 {
            self.value = value
        } else {
            return nil
        }
    }
}

func / (left: PositiveNumber, right: PositiveNumber) -> Float {
    return left.value / right.value
}

func divide(x: PositiveNumber?, y: PositiveNumber?) -> Float? {
    if let x = x, y = y {
        return x / y
    }
    return nil
}

let x1 = PositiveNumber(value: 1)
let y1 = PositiveNumber(value: 3)

let x2 = PositiveNumber(value: -1)
let y2 = PositiveNumber(value: 0)

divide(x1, y: y1)!  // .333
divide(x2, y: y2)!  // runtime error

这并不可怕,但我们仍然需要处理很多可选的处理/解包。我问这个问题,因为我的代码中有很多地方需要检查一个值是不是零,我很好奇是否有办法删除该代码并让编译器处理它。基于结构的解决方案需要几乎相同数量的代码。

1 个答案:

答案 0 :(得分:1)

Number Type build on Float

这个要点包含一个符合Float符合的几乎所有内容的结构。它只是Float的一个香草副本,可以根据自己的喜好进行更改。

您是否考虑过自定义运营商?

infix operator /+ { associativity left precedence 150 }

func /+(lhs:Float,rhs:Float) -> Float? {

    guard rhs > 0 else {
        return nil
    }
    return lhs / rhs
}

let test = 2 /+ -1 // nil
let test2 = 2 /+ 1 // 2

let test3 = 2 /+ 1 + 2 // warning

如果让它返回可选项或枚举值或不同的协议,那并不重要。你将不得不处理回报。但是这样你就会得到编译器警告。

有限数字类型只有一个操作员来处理分部:

您可以完全改变数学并创建一个PositiveNumber类型,在除以小于零的值时返回NaN。

public struct PositiveFloat {
    public var value: Float
    /// Create an instance initialized to zero.
    public init() {
        self.value = 0
    }
    /// Create an instance initialized to `value`.
    public init(_ value: Float) {
        self.value = value
    }

    public init(_ value: PositiveFloat) {
        self.value = value.value
    }
}

extension Float {
    public var positive : PositiveFloat {
        return PositiveFloat(self)
    }
}


public func /(lhs:Float,rhs:PositiveFloat) -> Float {
    if 0 > rhs.value {
        return lhs / rhs.value
    } else {
        return Float.NaN
    }
}
public func /(lhs:PositiveFloat,rhs:PositiveFloat) -> Float {
    if 0 > rhs.value {
        return lhs.value / rhs.value
    } else {
        return Float.NaN
    }
}
let testNormal : Float = 10
let testFloat : Float = -5

let test = testFloat / testNormal.positive
if test.isNaN {
    // do stuff
}