从Haskell的列表中随机选择一个元素
我的代码旨在创建一个单词搜索难题。有一个叫做 Orientation 的数据表示拼图中每个单词的方向。
data Orientation =
Forward | Back | Up | Down | UpForward | UpBack | DownForward | DownBack
deriving (Eq, Ord, Show, Read)
现在给定一个字符串输入[String],我想随机分配每个字符串的方向,例如[(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
我无法编译,因为pickOriis的输出IO Orientation,是否有关于如何修改我的代码的建议?非常感谢
Couldn't match expected type ‘[(IO Orientation, String)]’
with actual type ‘[String] -> [(Orientation, String)]’
回答
您可能会考虑修改函数,以便通过接受RandomGen参数来保持纯函数。的pickOri功能,例如,可能正是如此修改:
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与所选列表元素一起返回,以便下次生成另一个伪随机数。
同样,您可以assignWordDir像这样修改:
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递归到 to 时,递归函数调用使用g它从 接收到的pickOri。
您可以使用mkStdGen或newStdGen来生成RandomGen值。这是一个使用示例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是一个纯函数,所以这是意料之中的。
但是,您可以通过创建或获取新StdGen值来生成新的随机序列:
*Q65132918> rnd <- newStdGen
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(Up,"foo"),(Up,"bar"),(Forward,"baz")]
如果你想在一个编译的模块中使用它,你可以保留这里介绍的这些函数,然后在入口点用newStdGen-generated组合它们。StdGenmain