我经常搜索,阅读函数式编程,似乎无法找到解决问题的简单方法。我正在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(¤tState)
仅作为&告诉你它可能会更新currentState,并且仍然要求用户跟踪变异值,所以我不确定它是一个干净的设计。
当然,在这种情况下,我可以使用一个类,但是后来我失去了Swift中值类型的好处,并且大多数引擎都使用了具有值类型的数组 - 当你有来自的链接时,它会变得混乱将值结构化为类。
我的问题是 - 是否有更好的方法在Swift中实现它 - 我知道它不是一种函数式语言,我对OOP没有任何问题。任何想法/指示都非常感谢!
答案 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
值
表达