F# Types and Looping

久未见 提交于 2019-12-10 14:42:51

问题


I am working on an F# tutorial that creates a deck of cards. The types are listed, but I cannot understand how to loop through the types to create the map of the full deck. I expected to do something like

Foreach rank in ranks
   Foreach suit in suits
       somehow combine the two
   next suit
next rank

Is there no way to do this? Below are the types created.

I think if I changed them from types to lists they could union, right? So, what's the point of types?

type suits=
    |Spade=1
    |Heart=2
    |Club=3
    |Diamond=4

type ranks=
    |ValCard of int
    |Jack 
    |Queen
    |King

type deck= Deck of ranks * suits

回答1:


Enums is a good choice for representing cards. You have comparison among suits and among ranks for free, and easily convert enums from/to int.

type suit =
    | Spade = 1
    | Heart = 2
    | Club = 3
    | Diamond = 4

type rank = 
    | Ace = 1 | Two = 2 | Three = 3 | Four = 4 | Five = 5 | Six = 6 | Seven = 7 
    | Eight = 8 | Nine = 9 | Ten = 10 | Jack = 11 | Queen = 12 | King = 13

/// 'Card' is a type which represents a particular card     
type Card = Card of rank * suit

/// 'deck' is a list consisting of all cards in a full deck
let deck = [ for r in 1..13 do
               for s in 1..4 do
                 yield Card(enum<rank> r, enum<suit> s) ]

If you go for discriminated unions, you have to manually make lists of all suits and all ranks. The advantage is better pattern matching of DUs than that of enums.

type suit =
    | Spade
    | Heart
    | Club
    | Diamond

type rank = | Ace | Two | Three | Four | Five | Six | Seven 
            | Eight | Nine | Ten | Jack | Queen | King

type Card = Card of rank * suit

let private suits = [Spade; Heart; Club; Diamond]
let private ranks = [Ace; Two; Three; Four; Five; Six; Seven; 
                     Eight; Nine; Ten; Jack; Queen; King]

let deck = [ for rank in ranks do
               for suit in suits do
                 yield Card(rank, suit) ]



回答2:


An alternative approach which uses a discriminated union which meshes more nicely than enums with F#'s syntax

type suit=
    |Spade
    |Heart
    |Club
    |Diamond
    static member all = [Spade;Heart;Club;Diamond]

type rank=
    |ValCard of int
    |Jack 
    |Queen
    |King
    static member all =([1..10]|> List.map (ValCard)) @ [Jack;Queen;King]

type card = |Card of rank * suit

let all_cards = suit.All |> List.collect (fun s -> rank.all |> List.map (fun r -> Card(r,s))

Then you can do some neat pattern matching like

all_cards 
|> List.iter (fun c ->
    match c with
    |Card(King,Spade) -> ...
    |Card(King,_) -> ...
    |Card(_) -> ...

You could even define some Active patterns to get red/black cards .




回答3:


As an addendum to pad's answer, you could also use reflection to generate the deck:

type Union<'T> private () =
  static member val Cases = 
    FSharpType.GetUnionCases(typeof<'T>)
    |> Array.map (fun case -> FSharpValue.MakeUnion(case, null) :?> 'T)

let deck = 
  [ for rank in Union<rank>.Cases do
      for suit in Union<suit>.Cases do
        yield Card(rank, suit) ]


来源:https://stackoverflow.com/questions/13834396/f-types-and-looping

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