如何在递归中使用随机数?

3

我正在尝试从列表中选择一个随机元素,但这将使函数不纯,因此无法编译。该如何使递归函数接受IO操作?

build :: Jabberwocky Integer String Syllables -> String
build (Jabberwocky 0 body syl) = body
build (Jabberwocky len body syl)
    | syl == Middle     = build (Jabberwocky (len - 1) (body ++ (rand middle)    ) Consonant)
    | syl == Consonant  = build (Jabberwocky (len - 1) (body ++ (rand consonant)) Vowel)
    | syl == Vowel      = build (Jabberwocky (len - 1) (body ++ (rand vowel)     ) Consonant)
    | syl == Ending     = build (Jabberwocky (len - 1) (body ++ (rand ending)    ) Vowel)
        where 
            rand = getStdRandom (randomR (1,6))

1
你可以以纯粹的方式生成随机数,完全不需要使用 IO。你只需传递自己的生成器对象,而不是使用全局对象。如果你坚持使用 IO,那么你的签名应该显示它(但我不会这样做)。 - Bartek Banachewicz
1
你能详细说明一下吗?到目前为止,我找到的答案都说相反的话。https://dev59.com/mXA85IYBdhLWcg3wJf4Z - Ricky Han
不是相反,而是我想表达的意思。Jabberwocky ... -> String 是一个纯函数。你不能在其中使用 IO。你需要将该函数更改为不纯的函数 (Jabberwocky ... -> IO String) 或停止使用全局随机数生成器。 - Bartek Banachewicz
抱歉问一个新手问题。我还是很困惑。如何使用单子编写函数? - Ricky Han
1个回答

2
你必须将生成器带入纯过程中(链接新的随机生成器状态)。
randomR_nTimes_rec :: (RandomGen g, Random a) => Int -> (a, a) -> g -> ([a], g)
randomR_nTimes_rec 0 _ g = ([], g)
randomR_nTimes_rec n i g = (x:xs, g'') where ( x, g' ) = randomR i g
                                             (xs, g'') = randomR_nTimes_rec (n - 1) i g'

用法

*Main> getStdGen  >>= return . randomR_nTimes_rec 5 (0,5)
([2,5,3,1,3],1206240749 652912057)

如果您需要在一个复杂的过程中使用随机状态,可以使用 Control.Monad.Random。这里有一个示例


我认为这个回答没有超越重复问题中的回答。考虑投票关闭并在下一次回答原始问题。 - Bartek Banachewicz
@BartekBanachewicz(好的,这是重复的,让我感到困惑“说相反的话”...)。 - josejuan
我只想补充一点,如果你手动线程生成器,请非常小心,不要重复使用同一个生成器。这比你想象的要容易得多。 - kqr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接