这是我问题的简化版本:生成一个随机值列表,其中每个连续值取决于上一个值。
例如,生成一个随机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)
,但通过类型检查需要。我想以某种方式跳过此部分或用更通用的东西替换。是否可能或我的实现是错误的?
这是一种使用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))
要匹配您的尺寸10列表的示例,从0作为第一个低值开始,您将其称为intGen 10 0
。
constant
是elm-community/random-extra
的生成器,也可以像这样定义它(因为它在核心ELM Codebase中没有暴露):
constant : a -> Rnd.Generator a
constant a = Rnd.map (_ -> a) (Rnd.int 0 1)
关于您的示例,我认为您不想使用List (Rnd.Generator Int)
,因为这意味着一系列没有以任何方式捆绑在一起的发电机列表。这就是为什么我们需要使用andThen
来拔出刚刚生成的随机值,请递归减去intGen
,然后使用map
将列表放在一起。