初始化elm应用程序的正确方法是什么

zef*_*ciu 18 random functional-programming frp elm

Elm Random模块的文档说明:

获得意外种子的好方法是使用当前时间. http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random

然而,我没有看到如何在FRP应用程序中执行此类初始化逻辑的良好示例.我应该对哪个信号做出反应?如何使用最少的代码和最大的清晰度来做到这一点.

Apa*_*hka 27

有不同的方法来做到这一点.每个人都有自己的好处.我会给你三个我知道的,每个都有类似的例子.

1)添加时间自动收报机输入

您可以做的一件事是为您的程序输入添加时间.一个小程序的例子,每秒使用一个随机数的当前时间:

import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal
import Signal (Signal, (<~), (~))
import Random
import Random (Seed)
import Graphics.Element (Element)

randomInt : Seed -> Int
randomInt seed = seed |> (Random.generate <| Random.int 1 10) |> fst

otherInput : Signal (Int,Int)
otherInput = Mouse.position

timeSeed : Signal Seed
timeSeed = Random.initialSeed << round <~ Time.every second

inputs : Signal (Seed,(Int,Int))
inputs = (,) <~ timeSeed ~ otherInput

update : (Seed, (Int,Int)) -> (Int,Int) -> (Int,Int)
update (seed,(x,y)) (x',y') =
  let num = randomInt seed
  in (x-x'-num,y'-y+num) -- this update function is nonsense

main : Signal Element
main = asText <~ Signal.foldp update (0,0) inputs
Run Code Online (Sandbox Code Playgroud)

如果您还需要时间作为输入,并根据此时间对其他输入进行采样,这是最简单的方法.(如果您已经使用Time.fps过,请用它Time.timestamp来获取实际时间)

2)在启动时发出信号

如果您通常不需要时间作为程序的输入,则以前的解决方案并不理想.您可能更喜欢使用程序的开始时间初始化程序状态,而不必在程序运行的其余时间忽略时间标记.

使用信号额外包*可能最容易做到这一点.使用Signal.Time.startTime得到不打勾,但只有该程序作为初始值的开始时间的信号.使用,Signal.Extra.foldp'这样您就可以使用输入的初始值.

import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal
import Signal (Signal, (<~), (~))
import Random
import Random (Seed)
import Graphics.Element (Element)
import Signal.Extra as SignalE
import Signal.Time as Time

randomInt : Seed -> (Int,Seed)
randomInt seed = (Random.generate <| Random.int 1 10) |> fst

otherInput : Signal (Int,Int)
otherInput = Mouse.position

startTimeSeed : Signal Seed
startTimeSeed = Random.initialSeed << round <~ Time.startTime

inputs : Signal (Seed,(Int,Int))
inputs = (,) <~ startTimeSeed ~ otherInput

update (x,y) (seed,(x',y')) =
  let (num,seed') = randomInt seed
  in (seed',(x-x'-num,y'-y+num))

main : Signal Element
main = asText <~ SignalE.foldp' (snd >> update) identity inputs
Run Code Online (Sandbox Code Playgroud)

*我可能有偏见,因为我是链接包的作者.但我不知道其他提供相同功能的软件包.

3)在启动时使用端口

如果您发现以前的解决方案不能令人满意,因为您无需更改Signal即可添加到输入中,此解决方案适合您.这里我们使用JavaScript互操作来获取程序启动时间,而Elm将接受它作为常量值(否Signal).Elm代码如下所示:

import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal (Signal, (<~))
import Random
import Random (Seed)
import Graphics.Element (Element)

port startTime : Float

randomInt : Seed -> (Int,Seed)
randomInt seed = (Random.generate <| Random.int 1 10) |> fst

startTimeSeed : Seed
startTimeSeed = Random.initialSeed <| round startTime

update (x,y) (seed,(x',y')) =
  let (num,seed') = randomInt seed
  in (seed',(x-x'-num,y'-y+num))

main : Signal Element
main = asText <~ Signal.foldp update (startTimeSeed, (0,0)) Mouse.position
Run Code Online (Sandbox Code Playgroud)

那么这里的缺点是什么呢?你需要写一些JavaScript.而不是标准

<script>Elm.fullscreen(Elm.<YourModule>)</script>
Run Code Online (Sandbox Code Playgroud)

,你需要在你的html文件中这样的东西:

<script>Elm.fullscreen(Elm.<YourModule>, {startTime: Date.now()})</script>
Run Code Online (Sandbox Code Playgroud)

如果你选择这种方式,也许最好使用JavaScript中的随机数作为初始种子.我已经读过那个更加加密的安全性(免责声明:我对加密技术不太了解).所以你有一个port aRandomNumber : Int{aRandomNumber: Math.floor((Math.random() - 0.5) * 4294967295)}.


Dan*_*ton 5

我从上面的@Apanatshka重新编写了第三个例子,尝试使用更简单的代码,感觉更像标准体系结构,至少在Mike Clark的培训视频中看到,并在Elm 0.16下运行.这是我提出的重构版本:

module PortBasedRandom where

import Mouse
import Signal exposing (Signal, map)
import Random exposing (Seed)
import Graphics.Element exposing (Element, show)

port primer : Float


firstSeed : Seed
firstSeed =
  Random.initialSeed <| round primer


type alias Model =
  { nextSeed : Seed
  , currentInt : Int
  }


initialModel : Model
initialModel =
  { nextSeed = firstSeed
  , currentInt = 0
  }


randomInt : Model -> Model
randomInt model =
  let
      (i, s) = Random.generate (Random.int 1 10) model.nextSeed
  in
      { model | nextSeed = s, currentInt = i }


update : (Int, Int) -> Model -> Model
update (_, _) model =
  randomInt model


main : Signal Element
main =
  Signal.foldp update initialModel Mouse.position
    |> map (\m -> show m.currentInt)
Run Code Online (Sandbox Code Playgroud)

这需要HTML文件中的特殊帮助,所以这里有一个包含两个示例的文件:

<html>
  <head>
    <title></title>
    <script src="port_based_random.js"></script>
  </head>
  <body>
    <p>Move your mouse to generate new random numbers between 1 and 10 inclusive.</p>
    <script>Elm.fullscreen(Elm.PortBasedRandom, {primer: Date.now()})</script>
    <script>Elm.fullscreen(Elm.PortBasedRandom, {primer: Math.floor((Math.random() - 0.5) * 4294967295)})</script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)