如何在这个elm effects示例中添加第二个die?

17

我刚接触 Elm,一直在研究以下示例(请注意此示例是在新的 0.17 架构下编写的,其中 Action 现在称为 Command):http://elm-lang.org/examples/random

这个示例有一个后续挑战,就是添加第二个骰子,这样单击按钮就可以为每个骰子掷出新值。我的想法是更改模型以容纳两个分开的值,每个骰子一个值,类似于

type alias Model =
       { dieFace1 : Int
       , dieFace2 : Int
       }

在更新块之前,这个方法都能够正常工作。但是我不确定如何更新随机数生成器以创建两个值。这个函数对我来说有点混乱。

type Msg
  = Roll
  | NewFace Int Int


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      **(model, Random.generate NewFace (Random.int 1 6))** <-- not sure what to do here

    NewFace newFace1 newFace2 ->
      (Model newFace1 newFace2, Cmd.none)

Random.generate函数的文档有点简略 -

generate : (a -> msg) -> Generator a -> Cmd msg

创建一个命令,用于生成随机值。

这是处理两个骰子的正确方法吗?还是有更好的方式?我是elm新手,请友善一点 :)

3个回答

17

Random.int是一个原始的生成器,它给出一个随机整数。您需要一个能够给您两个随机整数的生成器。

可以使用更基本的生成器来构建随机数生成器以创建更复杂的生成器。幸运的是,Elm刚好有这样一个函数Random.pair,它允许您指定每个元组部分所需的两个生成器。

让我们将骰子生成器提取到自己的函数中,以避免重复:

dieGenerator : Random.Generator Int
dieGenerator =
  Random.int 1 6

现在我们可以构建另一个生成器,它会给我们一个骰子对的随机值:

diePairGenerator : Random.Generator (Int, Int)
diePairGenerator =
  Random.pair dieGenerator dieGenerator

既然处理的是包含整数的元组,让我们将你的Msg定义中的NewFace Int Int更新为NewFaces (Int, Int)。 这可以使你的Roll处理程序变得简洁明了:

Roll ->
  (model, Random.generate NewFaces diePairGenerator)

如果你想尝试超越这个,考虑一下允许任意数量的骰子被掷出所需的条件。采用从更基本的生成器构建复杂的生成器的想法,并使用Random模块的文档作为指南。


太棒了,感谢您指出正确的文档部分。看起来制作n个骰子的投掷可能涉及将Random.list参数化为所需骰子数量的Random.ints。感谢您的答案和新作业。 - TonyM
1
感谢 Chad 的详细解释。非常有效。我已经为懒人设置了一个代码片段:https://gist.github.com/dotcs/3b3626cfbe5b0744134f7af8edcb32c5 - dotcs
1
如果我们不期望一个 Int 元组,而是 两个 Int 参数,解决方案会是什么样子呢?也就是说,如果 Msg 被定义为 NewFaces Int Int,这是由 OP 所推测的? - Lars Blumberg
1
如果你有消息 NewFaces2 Int Int,那么你的 update 处理程序可能如下所示:(model, Random.generate (uncurry NewFaces2) diePairGenerator)。函数 uncurry 将获取元组参数并将其值作为两个参数传递到 NewFaces2 中。 - Chad Gilbert

1

一种方法是像这里https://gist.github.com/davidchase/40c27042bccfb00d786af0360b5bc3ea一样使用batch

另一种方法是使用Random.pairRandom.list,如果你需要超过2个:

import Html exposing (..)
import Html.App as Html
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import Random


main : Program Never
main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }

-- MODEL

type alias Model =
  { dieFaces : (List Int)
  }

-- https://dev59.com/8GAg5IYBdhLWcg3wx9XG#T62gEYcBWogLw_1b0--l
get : Int -> List a -> Maybe a
get n xs = List.head (List.drop n xs)

-- http://rundis.github.io/blog/2016/elm_maybe.html
getOrOne : Int -> List Int -> Int
getOrOne n xs = Maybe.withDefault 1 (get n xs)

init : (Model, Cmd Msg)
init =
  (Model [1, 1], Cmd.none)

-- UPDATE

type Msg
  = Roll
  | NewFace (List Int)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Roll ->
      (model, Random.generate NewFace (Random.list 2 (Random.int 1 6)))

    NewFace newFace ->
      (Model newFace, Cmd.none)

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none

-- VIEW

view : Model -> Html Msg
view model =
  div []
    [ img [ src ("/img/Alea_" ++ toString (getOrOne 0 model.dieFaces) ++ ".png")] []
    , img [ src ("/img/Alea_" ++ toString (getOrOne 1 model.dieFaces) ++ ".png")] []
    , button [ onClick Roll ] [ text "Roll" ]
    ]

还有另一个 https://github.com/jcollard/random-examples/blob/master/src/Dice.elm


0
除了@ChadGilbert提供的答案中描述的更改之外,我还必须更改NewFaces更新用例(使用elm 0.18.0)。
NewFaces newFaces ->
  let
    (newFace1, newFace2) = newFaces
  in
    (Model newFace1 newFace2, Cmd.none)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接