19

I am trying to fill a list with random numbers and am having diffculty getting the random number part. What I have right now prints out a random number 10 times, what I want is to print out 10 different random numbers

   let a = (new System.Random()).Next(1, 1000)


   let listOfSquares = [ for i in 1 .. 10->a]
    printfn "%A" listOfSquares

any tips or suggestions?

Aaron
  • 4,380
  • 19
  • 85
  • 141

6 Answers6

40

Your code is simply getting one random number and using it ten times.

This extension method might be useful:

type System.Random with
    /// Generates an infinite sequence of random numbers within the given range.
    member this.GetValues(minValue, maxValue) =
        Seq.initInfinite (fun _ -> this.Next(minValue, maxValue))

Then you can use it like this:

let r = System.Random()
let nums = r.GetValues(1, 1000) |> Seq.take 10
Joel Mueller
  • 28,324
  • 9
  • 63
  • 88
24
let genRandomNumbers count =
    let rnd = System.Random()
    List.init count (fun _ -> rnd.Next ())

let l = genRandomNumbers 10
printfn "%A" l
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • 3
    Creating `System.Random()` inside a function [tends](https://rextester.com/TUDIW48492) to give same lists if called twice. [Explanation](https://stackoverflow.com/q/767999/6131611) – Pavel Oct 14 '19 at 00:22
  • 1
    You can also use "ignore" function to make it look nicer. `List.init count (ignore >> rnd.Next)` – Pavlo Holotiuk Mar 02 '20 at 19:32
6

When I write a random something dispenser I like to use the same random number generator for each call to the dispenser. You can do that in F# with closures (a combination of Joel's and ildjarn's answer).

Example:

let randomWord =
    let R = System.Random()
    fun n -> System.String [|for _ in 1..n -> R.Next(26) + 97 |> char|]

In this way, a single instance of Random is 'baked into' the function, reusing it with each call.

cfern
  • 5,956
  • 2
  • 25
  • 22
  • Nice answer, but does not answer the question. Change it to generate numbers instead of a word and I think I'd like this one better. – Mr. Baudin Dec 14 '14 at 08:58
2

There are two problems:

1) In F# functions are supposed to be pure so a function without arguments is considered as final value.

To declare impure function "without arguments", let it take one argument of type unit

let a () = (new System.Random()).Next(1, 1000)

and call it passing unit argument

let list = [ for i in 1 .. 10 -> a () ]

Source

2) New System.Random() instance is created each time when a is called. This results in getting same numbers. To fix this, create the instance only once

let random = new System.Random()
let a () = random.Next(1, 1000)
let list = [ for i in 1 .. 10 -> a ()]

This isn't specific to F#, read explanation for C# for better understanding

Pavel
  • 5,374
  • 4
  • 30
  • 55
0

You could also avoid declaring an impure function as said by Pavel and just run:

let rnd = Random()
let rndList = [for i in 0..100 do rnd.Next(1000)]
Gerardo Zinno
  • 1,518
  • 1
  • 13
  • 35
-2

I think one should be careful how to initialize System.Random as it uses the current time as seed. One instance should be enough for the whole app. Injecting random into functions has the advantage that you can use a fixed seed and reproduce with semi randomness, e.g. for testing your logic.

let rnd = System.Random()
let genRandomNumbers random count =
    List.init count (fun _ -> random.Next ())
let getRandomNumbersSeeded = getRandomNumbers rnd

let l = getRandomNumbersSeeded 10
printfn "%A" l
  • Close... `let rnd = System.Random()` `let genRandomNumbers count = ` ` List.init count (fun _ -> rnd.Next())` `let l = genRandomNumbers 10` `printfn "%A" l` (I'm having some issues formatting my reply. Basically, drop the `random` on line 2 and change `random.Next ()` to `rnd.Next()` on line 3). –  Apr 03 '19 at 15:07
  • The line `let l = genRandomNumbers 10` forgets the second argument in the function call. Better: `let l = genRandomNumbers 10 rnd`. Even better would be if he flipped the order of the 2 arguments, soe he can partially apply `let genRandomList = genRandomNumbers rnd`, which then would allow multiple calls like "let l1 = genRandomList 10; let l2 = genRandomList 15` etc. – BitTickler Sep 11 '20 at 10:17