2017-06-11 59 views
1

我已經搜索了很多,閱讀了函數式編程,並且似乎無法找到對我的問題的直接答案。我在Swift中創建了一個遊戲實用程序庫,並且我理解限制可變性的概念,但是我正在努力爲簡單的PRNG找到一個好的實現。如果我有下面的代碼在Swift中將可變結構轉換爲不可變

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裝飾隨處可見(按引用傳遞值),這意味着其他結構函數變成變異(它通過你的代碼像病毒一樣傳播),並且當你想使用變異的函數時會導致編譯器錯誤,但不關心更新的狀態,並且如果它被傳遞到函數中而沒有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(&currentState)如只有&告訴你,它可能會更新currentState,並且仍然要求用戶跟蹤變異值,所以我不確定它是乾淨的設計。

當然,我可以在這種情況下,只使用一個類,但是然後我失去了價值類型在Swift中的好處,並且大部分引擎都在使用具有值類型的數組 - 當您有鏈接時會變得混亂從值結構到類。

我的問題是 - 是否有更好的方法來實現這個在Swift中 - 我知道它不是一個函數式語言,我沒有OOP的問題。任何想法/指針都非常感謝!

回答

1

如果拆分的方法分爲兩個獨立的部分(!):

  • 一個next()方法,用更新的種子, 和
  • 一個value屬性返回隨機數返回一個新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 表達式。

+0

感謝您的快速回復,這是非常乾淨和功能 - 我讀過,分裂功能是好的,但直到你的例子沒有一個清晰的認識:)。我必須多想一想,還有一些更復雜的例子,比如Xoroshiro128Plus,它從當前狀態生成值,然後改變當前狀態(SplitMix64的反轉類型)。所以現實地說,爲了比較C版本和我的版本,就像你的例子一樣,我會提前一個 - .next()。值將在序列中獲得值2。 – Schwifty

相關問題