变换列表(Generator A)到发电机(列表A)中



这是我问题的简化版本:生成一个随机值列表,其中每个连续值取决于上一个值。

例如,生成一个随机Int的列表,其中每个连续值将在下一步中确定最小值。假设启动value = 0和最大值始终为currentValue + 5

  1. 第一步:Random.int 0 5 => 3
  2. 下一步: Random.int 3 8 => 4
  3. 下一步: Random.int 4 9 => 8
  4. 等。

这是我的方法:

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),但通过类型检查需要。我想以某种方式跳过此部分或用更通用的东西替换。是否可能或我的实现是错误的?

这是一种使用andThenmap的解决方案。第一个参数是您在列表中想要的元素数量。第二个参数是起始值。

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

constantelm-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将列表放在一起。

最新更新