2016-08-02 5 views
0

存在してはならない共用体のケースを強制的にマップするとき、どうすればよいですか?存在してはならない共用体のケースを強制的にマップするとき、どうすればよいですか?

次のフィルタは、特定の場合に一致する要素のみが追加されることを保証する。

let toAvailable = 
    (fun space -> match space with 
        | Available pos -> true 
        | _    -> false) 

Iは、別のタイプのフィルタリングされた結果をマップする:

let availablePositions = 
    positions |> List.filter toAvailable 
       |> List.map (fun space -> match space with 
             | Available pos -> pos 
             | Allocated _ -> (-1,-1)) // Should never happen 

しかし、私は前の操作で扱ったケースをまだ扱わなければならない。したがって、私はすでに「割り当てられた」ケースを除外しました。

この場合、どうすればよいですか?

はここで完全な機能です:

let optionsFor piece (positions:Space list) = 

    let yDirection = match piece with 
        | Black _ -> -1 
        | Red _ -> 1 

    let sourceX , sourceY = 
     match piece with 
     | Black (checker , pos) -> pos 
     | Red (checker , pos) -> pos 

    let optionsForPiece = 
     (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) || 
        pos = ((sourceX + 1) , (sourceY + yDirection))) 

    let availablePositions = 
     positions |> List.filter toAvailable 
        |> List.map (fun space -> match space with 
              | Available pos -> pos 
              | Allocated _ -> (-1,-1)) // Should never happen 

    availablePositions |> List.filter optionsForPiece 

は、ここに私のドメイン全体です:ここで

open NUnit.Framework 
open FsUnit 

(* Types *) 
type Black = BlackKing | BlackSoldier 
type Red = RedKing | RedSoldier 

type Coordinate = int * int 

type Piece = 
    | Black of Black * Coordinate 
    | Red of Red * Coordinate 

type Space = 
    | Allocated of Piece 
    | Available of Coordinate 

type Status = 
    | BlacksTurn | RedsTurn 
    | BlackWins | RedWins 

(* Functions *) 
let black coordinate = Allocated (Black (BlackSoldier , coordinate)) 
let red coordinate = Allocated (Red (RedSoldier , coordinate)) 

let startGame() = 
    [ red (0,0); red (2,0); red (4,0); red (6,0) 
     red (1,1); red (3,1); red (5,1); red (7,1) 
     red (0,2); red (2,2); red (4,2); red (6,2) 

     Available (1,3); Available (3,3); Available (5,3); Available (7,3) 
     Available (0,4); Available (2,4); Available (4,4); Available (6,4) 

     black (1,5); black (3,5); black (5,5); black (7,5) 
     black (0,6); black (2,6); black (4,6); black (6,6) 
     black (1,7); black (3,7); black (5,7); black (7,7) ] , BlacksTurn 

let toAvailable = 
    (fun space -> match space with 
        | Available pos -> true 
        | _    -> false) 

let available (positions:Space list) = positions |> List.filter toAvailable 

let optionsFor piece (positions:Space list) = 

    let yDirection = match piece with 
        | Black _ -> -1 
        | Red _ -> 1 

    let sourceX , sourceY = 
     match piece with 
     | Black (checker , pos) -> pos 
     | Red (checker , pos) -> pos 

    let optionsForPiece = 
     (fun pos -> pos = ((sourceX - 1) , (sourceY + yDirection)) || 
        pos = ((sourceX + 1) , (sourceY + yDirection))) 

    let availablePositions = 
     positions |> List.filter toAvailable 
        |> List.map (fun space -> match space with 
              | Available pos -> pos 
              | Allocated _ -> (-1,-1)) // Should never happen 

    availablePositions |> List.filter optionsForPiece 

は私のテストです:

[<Test>] 
let ``get avaialable positions for black soldier``() = 
    // Setup 
    let piece = Black (BlackSoldier , (1,5)) 

    // Test 
    let available = startGame() |> fst 
           |> available 
           |> optionsFor piece 

    // Verify 
    let northWestAvailable = available |> List.exists (fun pos -> pos = (0,4)) 
    let northEastAvailable = available |> List.exists (fun pos -> pos = (2,4)) 

    (northWestAvailable && northEastAvailable) |> should equal true 

[<Test>] 
let ``get avaialable positions for red soldier``() = 
    // Setup 
    let piece = Red (RedSoldier , (0,2)) 

    // Test 
    startGame() |> fst 
       |> available 
       |> optionsFor piece 
       |> List.exists (fun pos -> pos = (1,3)) 
       |> should equal true 
+2

サイドノートとして、コードが右に振り回される傾向があります。これにより、コードを少し難しくしています(しかし、それはまだ非常にきれいです)。次のコードのインデントを減らし、最初の引数の下にパイプ演算子を並べるために、 '='の後に改行を使用することを検討してください。私は他の誰かがそのように書くのを見たことがない。 – TeaDrivenDev

+2

なぜあなたはそれを崩壊させて、いくつかの(pos)とNoneを返さないのですか?その後、List.chooseを使ってNoneを取り除きます。 – s952163

+0

@TeaDrivenDev形式を更新しました。フィードバックをお寄せいただきありがとうございます。 –

答えて

1

どのようにこのようなものについて:

type Red = RedKing | RedSoldier 
type Black = BlackKing | BlackSoldier 

type Coordinate = int * int 

type Piece = 
    | Black of Black * Coordinate 
    | Red of Red * Coordinate 

type Space = 
    | Allocated of Piece 
    | Available of Coordinate 



let x1 = Allocated (Black (BlackKing, (5,5))) 
let x2 = Available (10,10) 
let x3 = Available (20,10) 

let xs = [x1;x2;x3] 

EDIT TDDさんのコメントあたり

let xs' = 
    xs 
    |> List.choose (function | Available pos -> Some(pos) 
          | _    -> None) 

// val xs' : Coordinate list = [(10, 10); (20, 10)] 

必要な場合にもOption.mapを使用することができます。

+3

'List.choose'で直接マッチング関数を使うのではなく、' List.map'で "迂回"するのはなぜですか? – TeaDrivenDev

+0

ありがとうございます。 – s952163

+2

答えには関係ありませんが、可能な場合は、ワイルドカードの代わりに明示的に(ここでは 'Allocated _'を使用して)ケースを処理してください。そのようにしてコードをリファクタリング/更新し、新しいケースを追加すると、コンパイラは静かにそれを呑み込む代わりに新しい未処理のケースを警告します – Sehnsucht

関連する問題