Generating unique random number

两盒软妹~` 提交于 2019-12-13 17:30:20

问题


I have an application that I want to generate a list of 5 unique numbers on init three times after reading a json. So basically I want to get something like [31, 59, 62, 72, 2, 16, 2, 38, 94, 15, 55, 46, 83, 2, 10]. My challenge is that I am new to functional programming and elm and I am a bit lost. So I understand that Random.generate takes a msg and a generator and returns a cmd message and it is mostly used in the update function but this is not where I need as it is a helper function and doesn't need to communicate with the client. I think it can be used in init but I don't know how. Again my function is recursive and I don't know how to apply this logic with Random.generate recursively.

I understand my code will not work and I have tried it because Random.int does not generate a random number but a type of generate but still I don't know how to apply this to get what I want.

recursion : Int -> List a -> List number
recursion a b =
  if List.length b > 5
      then b
  else 
      let 
          rand = Random.int 0 a
      in
          if(List.member rand b)
              then recursion a b
          else
              recursion a (rand :: b)

can be called with:

recursion 50 []

I want to generate a list/array of 5 unique random 3 times.


回答1:


Great question. There are two parts here: - generating random numbers, and - wiring this all up

You will need the Random library for the former, and a little inspection will lead you to something like

get15Randoms = Random.generate OnRandomNumbers <| Random.list 5 (int 0 100)

This has type Cmd Msg - it's an async operation that will return on a Msg.

Wiring it up will be a series of stages. You refer to doing it in 'init' but that's not how Elm is going to work for you here. In the init function you can start off your json request. Then you'll need something like

init = ( initModel
       , Http.get {url =..., expect = Http.expectJson OnJson yourDecoder} 
       )

update msg model = 
    case msg of 
        OnJson (Ok data) ->
            -- attach json
            ( {model | data = data }, get15Randoms )
        OnRandomNumbers ints ->
            ( { model | ints = ints }, Cmd.none )

In other words, once the json comes back you can attach that, and use that iteration of the update to launch your random number request, and then catch the result in a subsequent iteration




回答2:


Random number generation is a side-effect because it's not predictable by definition, meaning its output isn't purely determined by its inputs. In Elm all side-effects go through the update function because if side-effects were allowed anywhere there would be no guarantee that any part of your code is pure and predictable. Things might start behaving differently at random, and it would be very hard figure out why since random input can occur anywhere.

That said, init is one place where it might make sense to allow side-effects, since there isn't any state yet. But since most side-effects are not immediate and you would most likely want to present some UI to indicate that your app is loading, I assume the API just hasn't been complicated to allow for a use case so rare. Especially since there are a couple workarounds you can use:

Workaround 1 - an empty representation

Since you're using a list to contain the random numbers, you could probably just use an empty list to represent that you've not received the numbers yet. Otherwise use a Maybe, or a custom type. This might be a bit cumbersome since you have to handle the empty case every time you use it, but depending on your use case it might be acceptable.

Workaround 2 - flags

Elm allows for sending data into your program from the outside on initializtion, and will pass this to your init function. This mechanism is called flags. You can use this to generate the numbers in JavaScript before sending it in as a parameter.

In your index.html you would put:

var app = Elm.Main.init({
  node: document.getElementById('elm'),
  flags: Array.from({length: 15}, () => Math.floor(Math.random() * 50))
});

And in init you accept the numbers as an ordinary argument:

init : List number -> Model
init numbers = 
    { myNumbers = numbes
    , ...
    }



回答3:


I want to respond with what I ended up doing motivated by @Simon-h's answer. I had a Msg type RndGen Int and since update is a function I decided to call RndGen recursively with help from the update function and flag it off when I got the number of random numbers I needed.

update msg model =
 case msg of
   NoOp ->
     (model, get15Randoms)
   RndGen rndGen ->
     if List.length (model.questions) < 15
       then
         if List.member rndGen model.questions
           then
             (model, get15Randoms)
         else
           ({model | questions = rndGen :: model.questions  }, get15Randoms)
     else
       (model, Cmd.none)

and

get15Randoms = 
  Random.generate RndGen (Random.int 0 100)

on init

init questions =
  (Model (getQuestions questions) 1 True [] False "", get15Randoms)

I will like to know if my thinking aligns with what the elm community will expect.



来源:https://stackoverflow.com/questions/53922706/generating-unique-random-number

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!