在Swift中将可变结构转换为不可变结构

时间:2017-06-11 11:03:49

标签: swift function oop design-patterns functional-programming

我经常搜索,阅读函数式编程,似乎无法找到解决问题的简单方法。我正在Swift中创建一个游戏实用程序库,我理解限制可变性的概念,但我很难为简单的PRNG找到一个好的实现。如果我有以下代码

public struct SplitMix64 {
    var seed: UInt64

    public mutating func nextUInt64() -> UInt64 {
        seed = seed &+ 0x9E3779B97F4A7C15
        var z: UInt64 = (seed ^ (seed >> 30)) &* 0xBF58476D1CE4E5B9
        z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
        return z ^ (z >> 31)
    }
}

这有效,但如果我必须将其作为函数参数传递,则会导致问题,要么我必须在任何地方使用inout装饰(通过引用传递值),这意味着其他结构函数变异(通过病毒传播)当你想要使用变异的func而不关心更新的状态时会导致编译器错误,并且如果它被传递到没有in let random=SplitMix64(SplitMix64(1).nextUInt64())的函数中就会抛弃它。

我知道我可以用更实用的方式实现它,例如

    public static func nextUInt64(state: UInt64) -> (state: UInt64, value: UInt64) {
        let newState = state &+ 0x9E3779B97F4A7C15
        var z: UInt64 = (newState ^ (newState >> 30)) &* 0xBF58476D1CE4E5B9
        z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
        return (state: newState, value: z ^ (z >> 31))
    }

但这只会给我的库用户带来更多问题,而不是调用函数并获取值,他们现在负责必须跟踪状态,传入状态,并将其存储在返回并提取从元组生成的数字。这只是一个简单的功能,其他发电机有更多的状态数据。我也可以在状态上使用inout来保存元组返回,如此

public static func nextUInt64(state: inout UInt64) -> UInt64 {
    state = state &+ 0x9E3779B97F4A7C15
    var z: UInt64 = (state ^ (state >> 30)) &* 0xBF58476D1CE4E5B9
    z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
    return z ^ (z >> 31)
}

但是,当从外面打电话时,可能不会发生什么事情 例如let rand=nextUInt64(&currentState)仅作为&告诉你它可能会更新currentState,并且仍然要求用户跟踪变异值,所以我不确定它是一个干净的设计。

当然,在这种情况下,我可以使用一个类,但是后来我失去了Swift中值类型的好处,并且大多数引擎都使用了具有值类型的数组 - 当你有来自的链接时,它会变得混乱将值结构化为类。

我的问题是 - 是否有更好的方法在Swift中实现它 - 我知道它不是一种函数式语言,我对OOP没有任何问题。任何想法/指示都非常感谢!

1 个答案:

答案 0 :(得分:1)

如果将方法拆分为两个独立的部分:

  • 一个next()方法,它返回带有更新种子的新SplitMix64值, 和
  • 一个value属性,它返回随机数

然后你可以创建随机数而不改变值:

public struct SplitMix64 {
    let seed: UInt64

    public func next() -> SplitMix64 {
        return SplitMix64(seed: seed &+ 0x9E3779B97F4A7C15)
    }

    public var value: UInt64 {
        var z: UInt64 = (seed ^ (seed >> 30)) &* 0xBF58476D1CE4E5B9
        z = (z ^ (z >> 27)) &* 0x94D049BB133111EB
        return z ^ (z >> 31)
    }
}

let random = SplitMix64(seed: 1).next().next().value

如果您不关心更新状态。 如果你关心 然后您仍然可以传递SplitMix64 inout值 表达