Подтвердить что ты не робот

Каков правильный способ инициализации приложения вяза

Документация для модуля Elm Random гласит:

Хорошим способом получить неожиданное семя является использование текущего времени. http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random

Я не вижу, однако, хорошего примера того, как выполнить такую ​​логику инициализации в приложении FRP. На какой сигнал я должен реагировать? Как это сделать с минимальным кодом и максимальной ясностью.

4b9b3361

Ответ 1

Существуют разные способы сделать это. У каждого есть свои преимущества. Я дам вам три, которые я знаю с похожим примером для каждого.

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

Если вам нужно время как вход в любом случае, и пробуйте другие ваши входы, основанные на этом времени, это самый простой способ. (Если вы уже используете 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

* Я могу быть предвзятым, потому что я автор связанного пакета. Но я не знаю других пакетов, предлагающих ту же функциональность.

3) При запуске с портом

Если вы нашли предыдущее решение неудовлетворительным, потому что у вас есть этот не изменяющийся Signal, чтобы добавить к вашему входу, это решение для вас. Здесь мы используем JavaScript interop, чтобы получить время запуска программы, и Elm примет его как постоянное значение (no 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

Итак, что здесь внизу? Вам нужно написать JavaScript. Вместо стандартного

<script>Elm.fullscreen(Elm.<YourModule>)</script>

вам нужно что-то подобное в вашем html файле:

<script>Elm.fullscreen(Elm.<YourModule>, {startTime: Date.now()})</script>

Если вы выберете этот способ, возможно, это хорошая идея использовать случайное число из JavaScript в качестве исходного семени. Я читал, что это более криптографически безопасно (отказ от ответственности: я мало знаю о криптографии). Таким образом, у вас будут port aRandomNumber : Int и {aRandomNumber: Math.floor((Math.random() - 0.5) * 4294967295)}.

Ответ 2

Я переработал третий пример из @Apanatshka выше, пытаясь получить более простой код, который больше похож на стандартную архитектуру, по крайней мере, как показано в учебных видео Майк Кларк, и работает под 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)

Это требует специальной помощи в файле 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>

Ответ 3

Если вы используете StartApp, вам нужно будет использовать собственный HTML файл с

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

Затем использовать startTime как семя:

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

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

И тогда в коде вы будете делать что-то вроде

init : Seed -> List Int
init seed = fst <| Random.generate intList seed

где, например:

intList : Random.Generator (List Int)
intList =
    Random.list 5 (Random.int 0 100)