简体   繁体   中英

F# Sequences and Records

I have 2 sequences...

type Suit = Spades | Clubs | Hearts | Diamonds
type Rank = Ace | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King

type Card = { suit: Suit; rank: Rank}

and intValueCard = {rank: Rank; value: int} // translates the union into an actual int intValueCard = {rank: Rank; value: int} // translates the union into an actual int

and just wondering how I can get the Card's Rank in an actual int... so far I have

let getActualValue (card:Card) =
    value |> translatedValue.Rank

but for the life of me I can't figure out how to deal with a sequence and getActualValue doesn't work..

I don't see any sequences. You have Suit and Rank which are discriminated unions, and you have Card which is a record. Not sure what you meant by intValueCard = {rank: Rank; value: int} intValueCard = {rank: Rank; value: int} , and getActualValue refers to a translatedValue which isn't explained. What will the integer value from the rank be used for? You can possibly get better help if you edit and improve your question.

Let's assume you want the rank in order to calculate the advantage in a game. It is probably best to create a function like this.

let weightFromRank rank =
    match rank with
    | Ace -> 1
    | Two -> 2
    ...etc...

You can use the function keyword to make a less verbose version of this function.

I believe it is not a good idea to use an enum, because you will miss out on important advantages of functional programming. Enum is a .NET type, and not a functional type.

Different kinds of games have different kind of weights on Ace. Some games give Ace a weight of 14, one above the King. You can then have different functions for calculating the weight, depending on what kind of game is played.

Take the following advice with a grain of salt, since I'm not a top notch expert on F#. A different approach would be to put the weight as a separate field in a record or a discriminated union or something like that. This approach I believe is less efficient, because the weight follows from the rank, so you would effectively be duplicating information in places where it's not needed, thereby also making your code more difficult to understand. It would also be less efficient to handle different weights for different kinds of games.

You can declare Rank as a enum:

type Rank = 
    | Ace = 1 
    | Two = 2 
    | Three = 3 
....

then use int to get the underlying value:

let getActualValue ({rank = r}) = int r

If you want the integer rank for comparison with other ranks, then you can just compare the discriminated union values instead, as comparison is built into them for free:

Eight < Nine // true
Jack < Six // false

[Seven; Ace; Four; Two] |> List.sort
// [Ace; Two; Four; Seven]

Now, if you really need the integer rank value for some reason you could use reflection to get the DU cases. Just make sure that you that you do it inside a top level module value, not inside a function, so that the reflection is only done once when the program starts:

open FSharp.Reflection
let ranks =
    FSharpType.GetUnionCases(typeof<Rank>)
    |> Array.map (fun c -> FSharpValue.MakeUnion(c, [||]) :?> Rank)

let getActualValue rank = Array.findIndex ((=) rank) ranks

getActualValue Ace // 0
getActualValue Two // 1
getActualValue King // 12

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