如何为List.filter包含多个参数?

时间:2016-08-03 16:18:09

标签: f#

如何为List.filter包含多个参数?

我需要在函数中添加一些参数作为过滤列表的谓词。

在F#中,List.filter只接受一个参数。但是,我需要为谓词添加多个参数才能工作。

就我而言,我需要添加 sourceX sourceY 作为参数:

let jumpOptions space =
    match space with
    | Allocated p -> match p with
                     | Red   (ch,xy) -> xy = (sourceX + 1, sourceY - 1) ||
                                        xy = (sourceX - 1, sourceY - 1)

                     | Black (ch,xy) -> xy = (sourceX + 1, sourceY + 1) ||
                                        xy = (sourceX - 1, sourceY + 1)
    | _ -> false

let jumpsForSoldier piece positions =
    match piece with
    | Black (ch,pos) -> positions |> List.filter jumpOptions
    | Red   (ch,pos) -> positions |> List.filter jumpOptions

总之,我希望将列表中的元素保持为纯粹。因此,我不想将列表中的每个元素与其他值捆绑在一起以满足过滤函数。

任何指导?

附录

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

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

let private yDirection = function
    | Black _ -> -1
    | Red   _ ->  1

let private toAvailable = function
    | Available pos -> true
    | _             -> false

let available positions = positions |> List.filter toAvailable

let private availableSelection = function
    | Available pos -> Some pos
    | Allocated _   -> None

let private availablePositions positions = 
    positions |> List.filter toAvailable
              |> List.choose availableSelection

let private allocatedSelection = function
    | Allocated p -> match p with
                     | Red   (ch,xy) -> Some xy
                     | Black (ch,xy) -> Some xy
    | _ -> None

let private allocatedPositions positions = 
    positions |> List.filter toAvailable
              |> List.choose allocatedSelection

let private getCoordinate = function
    | Available xy -> Some xy
    | _            -> None

let coordinateOf = function
    | Black (checker , pos) -> pos
    | Red   (checker , pos) -> pos

let jumpOptions space =
    match space with
    | Allocated p -> match p with
                     | Red   (ch,xy) -> let sourceX, sourceY = coordinateOf source
                                        xy = (sourceX + 1, sourceY - 1) ||
                                        xy = (sourceX - 1, sourceY - 1)

                     | Black (ch,xy) -> let sourceX, sourceY = coordinateOf p
                                        xy = (sourceX + 1, sourceY + 1) ||
                                        xy = (sourceX - 1, sourceY + 1)
    | _ -> false

let jumpsForSoldier piece positions =
    match piece with
    | Black (ch,pos) -> positions |> List.filter jumpOptions
    | Red   (ch,pos) -> positions |> List.filter jumpOptions

let private isKing piece = 
    match piece with
    | Black (checker , _) -> match checker with
                             | BlackSoldier -> false
                             | BlackKing    -> true

    | Red   (checker , _) -> match checker with
                             | RedSoldier   -> false
                             | RedKing      -> true
(* Public *)
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 optionsFor piece positions =

    let sourceX , sourceY = coordinateOf piece


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

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

    match piece |> isKing with
    | false -> positions |> availablePositions 
                         |> List.filter optionsForSoldier


    | true ->  positions |> availablePositions 
                         |> List.filter optionsForKing

let move piece destination positions =

    let rec movePiece positions destinationXY =
        let foundPiece = positions |> List.filter (fun space -> space = Allocated piece)
                                   |> List.head
        match foundPiece with
        | Allocated (Black (ch, xy)) -> (positions |> List.filter (fun space -> space <> Allocated (Black (ch, xy)))
                                                   |> List.filter (fun space -> space <> destination))
                                                   @  [Available (xy) ; (Allocated (Black (ch, destinationXY)))]

        | Allocated (Red   (ch, xy)) -> (positions |> List.filter (fun space -> space <> Allocated (Red (ch, xy)))
                                                   |> List.filter (fun space -> space <> destination))
                                                   @  [Available (xy) ; (Allocated (Red   (ch, destinationXY)))]
        | _ -> positions

    let options   = optionsFor piece positions
    let canMoveTo = (fun target -> options |> List.exists (fun xy -> xy = target))

    match getCoordinate destination with
    | Some target -> match canMoveTo target with
                     | true  -> movePiece positions target
                     | false -> positions
    | None -> positions

(* Tests *)
[<Test>]
let ``get jump options for red soldier`` () =
    // Setup
    let redPiece =   Red   ( RedSoldier , (0,2) )
    let blackPiece = Black ( BlackSoldier , (1,3) )
    let positions = [Allocated redPiece;    Available (2,2);  Available (4,2);  Available (6,2)                  
                     Allocated blackPiece;  Available (3,3);  Available (5,3);  Available (7,3)
                     Available (0,4);       Available (2,4);      Available (4,4);  Available (6,4)]
    // Test
    positions |> jumpsForSoldier redPiece
              |> should equal [Allocated blackPiece]

2 个答案:

答案 0 :(得分:6)

您可以拥有任意数量的参数,然后将函数部分应用于除1之外的所有参数,并将结果传递给List.filter

let jumpOptions sourceX sourceY space = ...

...
   positions |> List.filter (jumpOptions 5 42)

详细了解部分申请here

答案 1 :(得分:1)

您可以使用模式匹配提取所需信息并使用部分应用(在TQBF评论后编辑的代码)

VS