Elm - producing a list of random number changing w

2019-06-22 17:34发布

问题:

I am trying to make a column of random numbers changing every second, but I get different error messages:

import Random

main = flow down 
[ asText (Random.range 0 100 (every second))  
, asText (Random.range 0 100 (every second))
]

gives a parse error. What is wrong with my square bracket [?

Parse error at (line 5, column 1):
unexpected '['
expecting newline, spaces or end of input

Indent Maybe?

Once I indent, the example does compile but I just get <signal> instead of the actual number

main = flow down 
  [ asText (Random.range 0 100 (every second))  
  , asText (Random.range 0 100 (every second))
  ]

lift for signals?

Finally when I tried to use lift it gives me other confusion

main = flow down 
  [ lift asText (Random.range 0 100 (every second))  
  , lift asText (Random.range 0 100 (every second))
  ]

The error message is that I have the wrong type for lift.

Type error on line 5, column 5 to 9:
       lift

  Expected Type: Signal Element
    Actual Type: Element

No flow down just a list

If I forget flow down it still doesn't cooperate:

main = lift asText
  [  (Random.range 0 100 (every second))  
  ,  (Random.range 0 100 (every second))
  ]

I get an error message that _List was expected:

Type error between lines 5 and 7:
       [Random.range 0 100 (every second),
        Random.range 0 100 (every second)]

  Expected Type: _List
    Actual Type: Signal

?

Am I using Random.range correctly? I have not changed it from the original example:

  • http://elm-lang.org/edit/examples/Reactive/Randomize.elm

How do I get it to cooprate with lift and flow down ?

回答1:

A couple things are going on here:

Just as you expected, in the first part you have an indentation problem that the compiler doesn't like.

The next example, works because the two things in the list are in fact signals. But, that is not what you wanted. Instead, you want to print out the symbols.

Here is a simple example lifting Random.range:

import Random

main = asText <~ (Random.range 0 10 (every second))

This will display a value between 0 and 1 every second. This has to do with the way lift works. Its type is (a -> b) -> Signal a -> Signal b. Everytime the signal of the second argument changes, it runs the specified function with the value.

So, the compilation error on the third thing ou are trying is complaining that the function flow is expecting the contents of your list to be Element but they are actually Signal Element.

The last thing you have there doesn't work because lift is expecting a Signal as its second argument but you are giving it a [Signal] instead.

You really want something like this:

import Random

main = lift2 randomcolumn (Random.range 0 100 (every second)) (Random.range 0 100 (every second))

randomcolumn x y = flow down [asText x,
                          asText y]

http://share-elm.com/sprout/53d28d73e4b07afa6f983534

Hope this helps!



回答2:

Here's an answer that works with 0.15 [EDIT: and 0.16], currently the latest version of Elm. Since Joe's answer was written, the Random library has been overhauled completely to use a pure random number generator. The pseudorandom numbers are deterministic: every run is always the same, unless you change the initial seed.

We start with imports: boring but necessary, and then define some constants using the Random library.

import Graphics.Element exposing (flow, down, show, Element)
import Time exposing (fps)
import Random

gen = Random.int 0 100
gen2 = Random.pair gen gen
seed0 = Random.initialSeed 42

Next we define a state type, containing the random seed and the numbers to display. I assumed we want two; for a list of constant length, use Random.list n gen. We also define an initial state using the record constructor syntax (and two "random" numbers).

type alias State = {seed : Random.Seed, first : Int, second : Int}
state0 = State seed0 36 89

Now we define a step function to be run once a second. Here we peel off two random numbers and store them, along with the new seed. Notice that we use a new seed each time, chained one to the next.

step : a -> State -> State
step _ state =
  let
    ((first, second), seed') = Random.generate gen2 state.seed
  in
    State seed' first second

Now we use foldp to introduce state, to actually run that step function.

state : Signal State
state = Signal.foldp step state0 (fps 1)

We define a pure render function. No signals here.

render : State -> Element
render state =
    flow down [show state.first, show state.second]

And finally we map (formerly lift) the render function on to the state.

main = Signal.map render state

If you concatenate the gray boxes and remove the interstitial comments, you will get a working Elm 0.15 program. But be advised that it appears to be CPU-intensive.