18

Elm のRandomモジュールのドキュメントには次のように記載されています。

予期しないシードを取得する良い方法は、現在の時刻を使用することです。 http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random

ただし、FRP アプリケーションでこのような初期化ロジックを実行する方法の良い例は見当たりません。どの信号に反応する必要がありますか? 最小限のコードと最大限の明快さでこれを行う方法。

4

4 に答える 4

27

これにはさまざまな方法があります。それぞれに独自の利点があります。私が知っている 3 つについて、それぞれに似た例を挙げて説明します。

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-extra パッケージ* でこれを行うのが最も簡単です。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 相互運用機能を使用してプログラムの起動時間を取得します。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)}ます。

于 2015-02-19T21:03:29.007 に答える
5

上記の @Apanatshka からの 3 番目の例を作り直して、少なくとも 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)

これには HTML ファイルで特別な助けが必要なので、2 つの例を含むファイルを次に示します。

<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>
于 2015-12-06T02:43:16.170 に答える
1

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)
于 2015-10-06T16:07:00.747 に答える