2015-11-15 47 views
2

我可以在集合上使用模式匹配嗎?如何在集合上使用模式匹配

我有一個網格,代表一個井字棋板。

如何使用模式匹配來識別「X」或「O」的條紋?

到目前爲止,我建一個這樣的網格:

// ---------------- 
// 0 | 1 | 2 | 
// ---------------- 
// 3 | 4 | 5 | 
// ---------------- 
// 6 | 7 | 8 | 
// ---------------- 

type Marker = 
| X = 0 
| O = 1 
| NULL = 3 

let cells = [0..8] 
let grid = [for cell in cells -> (cell, Marker.NULL)] 

let streakExists = 
    match grid with 
    | ??? 
    | ??? 

所以我想使用模式匹配識別相似條紋如下:

//如果電網[.. 2]所有。有X

//或網格。[3..5]都具有X

//或網格。[6..8]都具有X

//返回true

// -----------------

//如果網格。[0; 3; 6;]都具有X

//或網格[1; 4; 5]。所有具有X

//或網格[2; 5; 8]。所有具有X

//返回true

// ------------------

如果//電網[0; 4; 8]都有X

//返回true

注:

我學習F#的基礎知識。 因此,如果這個問題顯而易見,請原諒我。

回答

4

您可以在集合上使用模式匹配,但是有相當多的情況下您必須在井字遊戲中進行覆蓋,所以它可能不是最好的選擇。

如果我簡化代碼的一個位(使用可識別聯合而不是枚舉和使用只是一個識別聯合的值的列表),它是這樣的:

type Marker = 
    | X 
    | O 
    | NULL 

let grid = [ for cell in 0 .. 8 -> NULL ] 

let streakExists = 
    match grid with 
    | [X;X;X;_;_;_;_;_;_] 
    | [_;_;_;X;X;X;_;_;_] 
    | [_;_;_;_;_;_;X;X;X] -> "X wins" 
    | _ -> "Not sure" 

這會工作,但你可以看到涵蓋所有情況將會是多麼困難。如果我想解決同樣的問題,我可能會以不同的方式寫。你可以創建表示網格中的所有可能的條紋列表的列表:

let streaks = 
    [ for row in [0;3;6] do  // Generate one streak for each row 
     yield [row;row+1;row+2] 
    for col in [0;1;2] do  // Generate one streak for each column 
     yield [col;col+3;col+6] 
    yield [0;4;8]    // Explicitly add two 
    yield [2;4;6] ]   // diagonal streaks 

現在你可以檢查是否grid包含連勝通過測試是否有任何連勝(從streaks),使得在所有的值指定的指數是XO。這應該很容易處理List.forallList.exists

1

如果模式直接匹配到網格,我不知道語法將是非常好的,你的選擇是:

let streakExists grid = 
    match grid with 
    |[Marker.X; Marker.X; Marker.X; _; _; _; _; _; _] -> true 
    |[_; _; _; Marker.X; Marker.X; Marker.X; _; _; _] -> true 
    |[_; _; _; _; _; _; Marker.X; Marker.X; Marker.X] -> true 
    ... 
    | _ -> false 

let streakExists grid = 
    match grid with 
    |Marker.X :: Marker.X :: Marker.X :: _ -> true 
    | _ :: _ :: _ :: Marker.X :: Marker.X :: Marker.X :: _ -> true 
    | _ :: _ :: _ :: _ :: _ :: _ :: Marker.X :: Marker.X :: Marker.X :: [] -> true 
    ... 
    | _ -> false 

(此選項ISN是不理想的,因爲它可以在不正確大小的網格上返回true,所以不要使用它,我只是想演示語法。)

您可以創建一個函數來模式匹配3:

let streakExists grid = 
    let checkListOf3 list = 
     match list with 
     |[Marker.X; Marker.X; Marker.X] -> true 
     |_ -> false 
    if grid.[0..2] |> checkListOf3 then true 
    elif grid.[3..5] |> checkListOf3 then true 
    elif grid.[6..8] |> checkListOf3 then true 
    ... 
    else false 

編輯

你可能最後這個方法用他streaks一個由Tomas建議相結合,創造:

let streakExists grid = 
    let checkListOf3 list = 
     match list with 
     |[Marker.X; Marker.X; Marker.X] -> true 
     |_ -> false 
    streaks |> List.exists (List.map (fun i -> List.item i grid) >> checkListOf3) 
2

正如托馬斯寫道,它會更容易簡化Marker鍵入歧視聯盟:

type Marker = X | O | NULL 

而且,由於車板是如此之小,你可以把它作爲八個值的列表:

let grid = List.init 8 (fun _ -> NULL) 

我與托馬斯同意,模式匹配可能不是解決這一問題的最好辦法,但爲了完整起見,你可以使多少有些明顯的溝通,他們在匹配模式匹配:

let hasStreak = function 
    | [X; X; X; 
     _; _; _; 
     _; _; _] 
    | [_; _; _; 
     X; X; X; 
     _; _; _] 
    | [_; _; _; 
     _; _; _; 
     X; X; X] 
    | [X; _; _; 
     X; _; _; 
     X; _; _] 
    | [_; X; _; 
     _; X; _; 
     _; X; _] 
    | [_; _; X; 
     _; _; X; 
     _; _; X] 
    | [X; _; _; 
     _; X; _; 
     _; _; X] 
    | [_; _; X; 
     _; X; _; 
     X; _; _] -> true 
    // The same sort of cases should go here for O... 
    | _ -> false 
+0

我不能只用一個參數hasStreak呢?然後我可以只定義一次模式,而不是'X'和'O'。沒有?讓hadStreak m = function | [m; m; m; _; _; _:_; _; _] –

+0

@ScottNimrod不以任何方式我能想到的... –

+0

我在聽DNR#1206上的節目 –