2012-12-12 72 views
5

我正在研究創建一副紙牌的F#教程。列出了類型,但我無法理解如何遍歷類型以創建完整卡組的地圖。我期待做類似F#類型和循環

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

有沒有辦法做到這一點?以下是創建的類型。

我想如果我將它們從類型更改爲列表,他們可以結合,對吧?那麼,類型的要點是什麼?

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

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

type deck= Deck of ranks * suits 

回答

4

Enums是代表卡片的好選擇。你可以在套裝和行列之間免費進行比較,並輕鬆地將枚舉從/轉換爲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) ] 

如果你去discriminated unions,你必須手動進行所有suit S的名單和所有rank秒。優點是DU的模式匹配比枚舉更好。

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

下游用戶也可以「免費」提供的比較(基於案例的順序)。 – Daniel

+0

我不認爲枚舉是一個不錯的選擇。您甚至可以將循環值更改爲「-10..20」和「0..6」,並且您的代碼仍會創建一些無效的「卡片」。並且如上所述,您也可以像DUO一樣自由地比較枚舉。 –

5

它採用了可識別聯合其網格比F#的語法

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)) 

枚舉更多好聽的另一種方法。然後,你可以做一些巧妙的模式匹配像

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

你甚至可以定義一些活動模式以獲得紅色/黑色卡片。

+1

你忘了'Valcard(10)';這是手動列出所有值的缺點。 – pad

+0

@pad - 好點 - 切換到列表上的地圖 –

+1

請注意,'ValCard of int'是一個泄漏的抽象,因爲'ValCard 0'和'ValCard 11'等是非法值。 – pad

2

作爲附錄墊的回答,您還可以使用反射來產生甲板:

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) ]