简体   繁体   中英

Unable to generate random number F#

I am working on a small project and am trying to learn F# the functional way ,for some strange reason I am unable to generate a random number and as such unable to get a random date. here is my F# code

let IfancyHerList = [("Sara",1); ("Saima",2); ("Zoe",3);("Scarlett",4);
("Jennifer",5);("Sandra Bullock",6)]

let MyGirlFriend  = List.pick (fun funToNight  ->
                     let n = rand.Next(10)
                     match funToNight with                    
                     | (value,n ) -> Some value
                     |_ -> None) IfancyHerList
printfn "Your date for tonight is %s lucky fella" MyGirlFriend

The let n = rand.Next(10) does generate a random number but not in match block

any suggestion would be most welcome

thanks

Your code doesn't do what you expect it to do. Pattern Matching is technically the opposite of constructing data. It just decompose/de-construct data.

What you do here:

match funToNight with                    
| (value,n ) -> Some value
|_ -> None

has the following meaning:

  1. Check if funToNight is a tuple.
  2. Assign the first value of the tuple to value
  3. Assign the second value of the tuple to n

It seems what you expected was:

  1. Check if the second value of a tuple is what is inside n

And that isn't how Pattern Matching works.

You can add conditionals like comparing a value with a pre-defined variable by adding a when clause to the Pattern Matching. Like this:

match funToNight with
| (value,x) when x = n -> Some value
| _ -> None

But in my opinion, your case is exactly a use-case where Pattern Matching don't make sense to begin with. You want to check if the second entry is your random number, so use an if statement instead.

let MyGirlFriend  =
    List.pick (fun funToNight  ->
        let n = rand.Next(10)
        if   (snd funToNight) = n
        then Some (fst funToNight)
        else None
    ) IfancyHerList

instead of fst and snd you also can do it in the lambda.

let MyGirlFriend =
    List.pick (fun (name,nr)  ->
        let n = rand.Next(10)
        if   nr = n
        then Some name
        else None
    ) IfancyHerList

This is the general solution to pick that also works with tuples of other sizes. fst and snd only work with tuples that has exactly two elements.

Besides that you probably want to use List.tryPick instead of List.pick . List.pick throws an exception if it cannot find an element. On top variables should start with a lower-case. Upper-case values are used for Types/Classes.

So here is a complete working example:

let rand = new System.Random()

let ifancyHerList =
    [
        ("Sara",1); ("Saima",2); ("Zoe",3); ("Scarlett",4);
        ("Jennifer",5);("Sandra Bullock",6)
    ]

let myGirlFriend =
    List.tryPick (fun (name,nr)  ->
        let n = rand.Next(10)
        if   nr = n
        then Some name
        else None
    ) ifancyHerList

match myGirlFriend with
| Some name -> printfn "Your date for tonight is %A lucky fella" name
| None      -> printfn "You don't have a date tonight!"

Addendum

Your List.pick call returns quite a lot None and doesn't pick an entry. The reason is that you generate the random number inside the lambda function you pass to List.pick .

Your current "flow" of your code is like this. You go through the list. First picking ("Sara",1) . You generate a random, let's say 5 . 1 and 5 doesn't match so the next entry ("Saima", 2) will be used. But then you generate again a new random number, let's say 3 and the next entry is picked because 3 and 2 are not equal. This can go on without every picking any entry. Even if you change the random generation to rand.Next(6) .

So a general advice is that you shouldn't add side-effects to lambda expressions. I assume you want to generate one random number and then pick it from the list. Its easy to change by extracting the random call outside the lambda.

let myGirlFriend =
    let n = rand.Next(10)
    List.tryPick (fun (name,nr) ->
        if   nr = n
        then Some name
        else None
    ) ifancyHerList

So a general advice, avoid side-effects in Higher-Order functions.

Your real issue isn't with random numbers at all, it's with your match statement. You misunderstand how it works. Short version: match (whatever) with (value, n) -> ... doesn't do what you think it does: it always matches, and assigns the names value and n to the two parts of whatever tuple you matched against.

The longer version is that you're making the same mistake as F#: Not understanding match .. with , so I'll just point you to that question to read the more in-depth answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM