以下是我的问题的简化版本:生成一个随机值列表,其中每个连续值取决于前一个值。
例如,生成一个随机Int
列表,其中每个连续值将为下一步建立最小值。我们假设从value = 0
开始,最大值始终为currentValue + 5
:
Random.int 0 5
=> 3 Random.int 3 8
=> 4 Random.int 4 9
=> 8 这是我的方法:
intGen : Int -> List (Rnd.Generator Int) -> List (Rnd.Generator Int)
intGen value list =
if length list == 10 then
list
else
let newValue = Rnd.int value (value + 5)
newList = newValue :: list
in intGen newValue newList
让我们将其转换为Rnd.Generator (List Int)
:
listToGen : List (Rnd.Generator Int) -> Rnd.Generator (List Int)
listToGen list =
foldr
(Rnd.map2 (::))
(Rnd.list 0 (Rnd.int 0 1))
list
我不喜欢这部分:(Rnd.list 0 (Rnd.int 0 1))
。它生成类型Rnd.Generator (List Int)
的初始值,其中(Rnd.int 0 1)
实际上从未使用过,但是类型检查需要它。我想以某种方式跳过这部分或用更通用的东西替换它。是可能还是我的实施是错误的?
答案 0 :(得分:3)
以下是一种使用andThen
和map
的解决方案。第一个参数是列表中所需的元素数。第二个参数是起始值。
intGen : Int -> Int -> Rnd.Generator (List Int)
intGen num value =
if num <= 0 then
constant []
else
Rnd.int value (value + 5)
|> Rnd.andThen (\i -> intGen (num-1) i
|> Rnd.map (\rest -> i :: rest))
要匹配以0开头的大小为10的列表示例作为第一个低值,您可以将其称为intGen 10 0
。
constant
是来自elm-community/random-extra
的生成器,或者可以像这样定义它(因为它没有在核心Elm代码库中公开):
constant : a -> Rnd.Generator a
constant a = Rnd.map (\_ -> a) (Rnd.int 0 1)
关于你的例子,我不认为你会想要使用List (Rnd.Generator Int)
,因为这意味着一系列不以任何方式捆绑在一起的生成器。这就是为什么我们需要使用andThen
来提取刚刚生成的随机值,递归调用intGen
减1,然后使用map
将列表放在一起。