Reputation: 11595
How do I include multiple arguments for List.filter?
I need to add some parameters to a function that serves as a predicate for filtering a list.
In F#, the List.filter accepts just one argument. However, I need to add multiple arguments for my predicate to work.
In my case, I need to add sourceX and sourceY as parameters:
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
In conclusion, I want to keep the elements within my list pure. Hence, I do not want to bundle each element within my list with other values just to satisfy a filter function.
Any guidance?
Appendix:
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]
Upvotes: 0
Views: 164
Reputation: 5049
You could extract the required info using pattern matching and use partial application (code edited after TQBF comments)
let jumpOptions (sourceX, sourceY) = function
Allocated (Red (_, (x, y)) as p)
| Allocated (Black (_, (x, y)) as p) when abs (sourceX - x) = 1
-> y = sourceY - yDirection p
| _ -> false
let jumpsForSoldier = function
Red (_, pos)
| Black (_, pos) -> List.filter (jumpOptions pos)
Upvotes: 1
Reputation: 80744
You can have as many parameters as you want, then partially apply the function to all of them but one, and pass the result to List.filter
.
let jumpOptions sourceX sourceY space = ...
...
positions |> List.filter (jumpOptions 5 42)
Read more on partial application here.
Upvotes: 6