首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >初始化elm应用程序的正确方法是什么?

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

Stack Overflow用户
提问于 2015-02-19 20:20:26
回答 4查看 3.3K关注 0票数 18

Elm的Random模块的文档说明:

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

然而,我没有看到一个很好的例子来说明如何在FRP应用中执行这样的初始化逻辑。我应该对什么信号做出反应?如何用最少的代码和最大的清晰度做到这一点。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-02-20 05:03:29

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

1)添加计时器输入

你可以做的一件事就是给程序的输入增加时间。一个使用当前每秒时间作为随机数的小程序示例:

代码语言:javascript
复制
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

如果您无论如何都需要时间作为输入,并基于此时间对其他输入进行采样,这是最简单的方法。(如果您已经使用Time.fps完成此操作,请使用Time.timestamp获取实际使用时间)

2)在启动时使用信号

如果您通常不需要时间作为程序的输入,那么前面的解决方案并不理想。您可能更喜欢使用程序的开始时间来初始化程序状态,而不必在程序运行的其余时间内忽略计时器。

使用signal-extra package*可能最容易做到这一点。使用Signal.Time.startTime来获得一个信号,该信号不会滴答作响,但只有程序的开始时间作为初始值。使用Signal.Extra.foldp',这样就可以使用输入的初始值。

代码语言:javascript
复制
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

*我可能有偏见,因为我是链接包的作者。但我不知道有没有其他包提供同样的功能。

3)使用端口启动时

如果您发现前面的解决方案不令人满意,因为您有一个不变的Signal要添加到您的输入中,那么这个解决方案就适合您。在这里,我们使用JavaScript interop来获取程序的启动时间,Elm将把它作为一个常量值(无Signal)接受。Elm代码如下所示:

代码语言:javascript
复制
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

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

代码语言:javascript
复制
<script>Elm.fullscreen(Elm.<YourModule>)</script>

,则需要在html文件中包含类似以下内容:

代码语言:javascript
复制
<script>Elm.fullscreen(Elm.<YourModule>, {startTime: Date.now()})</script>

如果您选择这种方式,那么使用JavaScript中的随机数作为初始种子可能是个好主意。我读到过这在密码上更安全(免责声明:我对crypto了解不多)。所以你会有一个port aRandomNumber : Int{aRandomNumber: Math.floor((Math.random() - 0.5) * 4294967295)}

票数 27
EN

Stack Overflow用户

发布于 2015-12-06 10:43:16

我修改了上面@Apanatshka中的第三个示例,试图获得更简单的代码,使其感觉更像标准架构,至少在Mike Clark的培训视频中可以看到,并在Elm 0.16下运行。下面是我想出来的重构版本:

代码语言:javascript
复制
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)

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

代码语言:javascript
复制
<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>
票数 5
EN

Stack Overflow用户

发布于 2015-10-07 00:07:01

如果您使用的是StartApp,那么您需要使用一个自定义的

代码语言:javascript
复制
<script type="text/javascript">
    var yourPgm = Elm.fullscreen(Elm.Main, {startTime: Date.now()});
</script>

然后使用startTime作为种子:

代码语言:javascript
复制
startTimeSeed : Seed
startTimeSeed = Random.initialSeed <| round startTime

app =
  StartApp.start
    { init = (init startTimeSeed, Effects.none)
    , update = update
    , view = view
    , inputs = []
    }

然后在代码中你会做一些类似这样的事情

代码语言:javascript
复制
init : Seed -> List Int
init seed = fst <| Random.generate intList seed

其中,例如:

代码语言:javascript
复制
intList : Random.Generator (List Int)
intList =
    Random.list 5 (Random.int 0 100)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28606248

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档