你不能有一個純函數每次都返回一個不同的數字。它的輸出取決於它以前的調用,而不僅僅是它的參數。但是,您可以創建一個monad,它攜帶目前爲止生成的一組數字,然後重試生成一個隨機數,直到找到一個尚未生成的數爲止。對於以下示例,我使用了MonadRandom包。
import Control.Monad
import Control.Monad.Random
import Control.Monad.State
import Data.IntSet (IntSet)
import qualified Data.IntSet as IS
import System.Random (getStdGen)
type RandDistinct g a = StateT IntSet (Rand g) a
evalDistinct :: RandomGen g => RandDistinct g a -> g -> a
evalDistinct k = evalRand (evalStateT k IS.empty)
上述類型使用StateT
以增強隨機數發生器記住集生成的數字爲止。當我們想評估這個monad中的一個計算時,我們從一個空集合開始,並用evalRand
來評估內部計算。
現在我們可以寫,每次返回不同的數的函數:
nextDistinct :: RandomGen g => (Int,Int) -> RandDistinct g Int
nextDistinct range = loop
where
-- Loop until we find a number not in the set
loop = do
set <- get
r <- getRandomR range
if IS.member r set
then loop -- repeat
else put (IS.insert r set) >> return r
和測試它是如何工作的:
main = getStdGen >>= print . evalDistinct (replicateM 50 $ nextDistinct (10, 99))
注意nextDistinct
使用一個簡單的策略 - 重試生成新數字,如果它已經存在於集合中。只要碰撞次數很少,這就可以正常工作。
「因此,我可以期望函數每次都返回不同的數字」 - 野生假設。如果您只是生成隨機數字,則有重複的機會。事實上,這個概率比你的直覺所說的要糟糕。檢查生日悖論。 –
@KarolyHorvath謝謝。修正了語言。 – Dilawar
Karoly,雖然你說的是真的,但我不喜歡使用「悖論」這個詞,因爲它不是不合邏輯的或類似的東西:P就像說「蒙蒂霍爾悖論」,我更喜歡「蒙蒂霍爾問題」。 – Wes