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

在Swift中将可变结构转换为不可变结构,swift,function,oop,design-patterns,functional-programming,Swift,Function,Oop,Design Patterns,Functional Programming,我已经搜索了很多,阅读了函数式编程,似乎找不到一个简单的答案来解决我的问题。我正在用Swift创建一个游戏实用程序库,我理解限制可变性的概念,但我正在努力为一个简单的PRNG找到一个好的实现。如果我有以下代码 public struct SplitMix64 { var seed: UInt64 public mutating func nextUInt64() -> UInt64 { seed = seed &+ 0x9E3779B97F4A7C

我已经搜索了很多,阅读了函数式编程,似乎找不到一个简单的答案来解决我的问题。我正在用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,但不关心更新的状态时,会导致编译器错误,如果将其传递到一个没有inout的函数中,则会将其丢弃。
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(¤tState)
,因为只有&告诉您它可能会更新currentState,并且仍然需要用户跟踪变化的值,所以我不确定这是一个干净的设计

当然,在这种情况下,我可以只使用一个类,但这样我就失去了Swift中值类型的优点,而且大多数引擎都使用带有值类型的数组——当您有从值结构到类的链接时,这会变得很混乱

我的问题是-有没有更好的方法在Swift中实现它-我知道它不是函数式语言,我对OOP没有任何问题。非常感谢您的任何想法/建议

如果将(!)方法分为两部分:

  • 一个
    next()
    方法,返回一个新的
    SplitMix64
    值和更新的种子, 及
  • 返回随机数的
    属性
然后,您可以创建随机数,而无需改变值:

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

表达式。

感谢您的快速回复,这非常简洁且实用-我曾读到拆分函数很好,但直到您的示例:)才有清晰的理解。我必须再仔细考虑一下,还有一些更复杂的例子,比如Xoroshiro128Plus,它从当前状态生成值,然后对当前状态进行变异(与SplitMix64相反)。因此,现实地说,要将C版本与我编写的类似于您的示例的版本进行比较,我将领先一个-.next().value将得到序列中的值2。