Elm のRandom
モジュールのドキュメントには次のように記載されています。
予期しないシードを取得する良い方法は、現在の時刻を使用することです。 http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random
ただし、FRP アプリケーションでこのような初期化ロジックを実行する方法の良い例は見当たりません。どの信号に反応する必要がありますか? 最小限のコードと最大限の明快さでこれを行う方法。
Elm のRandom
モジュールのドキュメントには次のように記載されています。
予期しないシードを取得する良い方法は、現在の時刻を使用することです。 http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random
ただし、FRP アプリケーションでこのような初期化ロジックを実行する方法の良い例は見当たりません。どの信号に反応する必要がありますか? 最小限のコードと最大限の明快さでこれを行う方法。
これにはさまざまな方法があります。それぞれに独自の利点があります。私が知っている 3 つについて、それぞれに似た例を挙げて説明します。
できることの 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
して実際の時間を取得してください)
通常、プログラムへの入力として時間を必要としない場合、前のソリューションは理想的ではありません。プログラムの開始時間でプログラムの状態を初期化し、残りのプログラム実行時間のタイム ティッカーを無視する必要がない場合があります。
おそらく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
*私はリンクされたパッケージの作成者であるため、私は偏見を持っている可能性があります. しかし、同じ機能を提供する他のパッケージについては知りません。
前の解決策が不十分であるとわかった場合は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)}
ます。
上記の @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>
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)