从Haskell的列表中选择一个随机元素 [英] Pick a random element from a list in Haskell
问题描述
我的代码旨在创建一个单词搜索难题.有一个名为Orientation的数据表示拼图中每个单词的方向.
My code aims to create a word search puzzle. There is a data called Orientation representing the direction of each word in the puzzle.
data Orientation =
Forward | Back | Up | Down | UpForward | UpBack | DownForward | DownBack
deriving (Eq, Ord, Show, Read)
现在给定输入的字符串是 [String]
,我想为每个字符串随机分配一个方向,例如 [(Orientation,String)]
Now given a input of strings which is [String]
, I want to randomly assign each string an orientation like [(Orientation, String)]
assignWordDir :: [String] -> [(Orientation, String)]
assignWordDir [] = []
assignWordDir (s:strs) = (ori, s) : assignWordDir
where ori = pickOri [Forward, Back, Up, Down, UpForward, UpBack, DownForward, DownBack]
pickOri :: [a] -> IO a
pickOri xs = do
i <- randomRIO (0, len)
pure $ xs !! i
where len = length xs - 1
我无法编译,因为 pickOri
的输出是 IO Orientation
,是否有关于如何修改代码的建议?非常感谢
I cannot compile because the output of pickOri
is IO Orientation
, is there any suggestions on how to modify my code? Thanks a lot
Couldn't match expected type ‘[(IO Orientation, String)]’
with actual type ‘[String] -> [(Orientation, String)]’
推荐答案
您可以考虑通过使用 RandomGen
参数来修改函数,以使它们保持纯净.例如,可以这样修改 pickOri
函数:
You might consider modifying the functions so that they stay pure by taking a RandomGen
parameter. The pickOri
function, for example, might be modified thusly:
pickOri :: RandomGen g => g -> [a] -> (a, g)
pickOri rnd xs =
let len = length xs - 1
(i, g) = randomR (0, len) rnd
in (xs !! i, g)
有必要返回新的 RandomGen
值 g
以及所选的列表元素,以便下次生成另一个伪随机数.
It's necessary to return the new RandomGen
value g
together with the selected list element, so that it'll generate another pseudo-random number the next time around.
同样,您可以像这样修改 assignWordDir
:
Likewise, you can modify assignWordDir
like this:
assignWordDir :: RandomGen g => g -> [b] -> [(Orientation, b)]
assignWordDir _ [] = []
assignWordDir rnd (s:strs) = (ori, s) : assignWordDir g strs
where (ori, g) =
pickOri rnd [Forward, Back, Up, Down, UpForward, UpBack, DownForward, DownBack]
请注意,当递归到 assignWordDir
时,递归函数调用将使用从 pickOri
接收到的 g
.
Notice that when recursing into to assignWordDir
, the recursive function call uses the g
it receives from pickOri
.
您可以使用 mkStdGen
或 newStdGen
生成 RandomGen
值.这是使用 newStdGen
的示例:
You can use mkStdGen
or newStdGen
to produce RandomGen
values. Here's an example using newStdGen
:
*Q65132918> rnd <- newStdGen
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(UpBack,"foo"),(Up,"bar"),(UpBack,"baz")]
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(UpBack,"foo"),(Up,"bar"),(UpBack,"baz")]
请注意,当您使用相同 RandomGen
值时,将获得相同的序列.这是因为 assignWordDir
是一个纯函数,所以可以预期.
Notice that when you use the same RandomGen
value, you get the same sequence. That's because assignWordDir
is a pure function, so that's expected.
但是,您可以通过创建或获取新的 StdGen
值来产生新的随机序列:
You can, however, produce a new random sequence by creating or getting a new StdGen
value:
*Q65132918> rnd <- newStdGen
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(Up,"foo"),(Up,"bar"),(Forward,"baz")]
如果要在已编译的模块中使用此功能,则可以保留此处显示的这些功能,然后将它们与 newStdGen
-生成的 StdGen
组合在一起.主要
入口点.
If you want to play with this in a compiled module, you can keep these functions as presented here, and then compose them with a newStdGen
-generated StdGen
in the main
entry point.
这篇关于从Haskell的列表中选择一个随机元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!