这里是。这是我的第一个F#程序。如果我错过了该语言的功能,请在我仍在学习的同时提醒我。
这是我的示例输入
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . B . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . A . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . C . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . G . . . . .
. . . . . . . D . . . . . . . . . . . . . . . . .
. . . . . . . . F . . . . . . . . . . . . . . . .
. . . . . . . E . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
这是输出
. . . . . . . . . a b . . . . . . . b g . . . . .
. . . . . . . . . a b . B . . . b b b g . . . . .
. . . . . . . . . . a b . . . b c c c g . . . . .
. . . . . . . . A . . a b . b c . . c g . . . . .
. . . . . . . . . . . a b b c . . . c g . . . . .
a a a a a a a a . . . a b c . . C . c g . . . . .
d d d d d d d d a a a a b c . . . c g . . . . . .
. . . . . . . . d d d d b c . . c g . G . . . . .
. . . . . . . D d d d d d c . . c g . . . . . . .
d d d d d d d d f f f f f f c . c g . . . . . . .
e e e e e e e e e e e e e e c . c g . . . . . . .
. . . . . . . . . . . . . e c . c g . . . . . . .
. . . . . . . . . . . . . e c . c g . . . . . . .
. . . . . . . . . . . . . e c . c g . . . . . . .
这是代码。请享用。
// The first thing that we need is some data.
let originalData = [
"........................."
"............B............"
"........................."
"........A................"
"........................."
"................C........"
"........................."
"...................G....."
".......D................."
"........F................"
".......E................."
"........................."
"........................."
"........................."
]
现在我们需要将数据转换为二维数组,以便可以通过索引器访问它。
let dataMatrix =
originalData
|> List.map (fun st -> st.ToCharArray())
|> List.toArray
// We are going to need a concept of ownership for each
// cell.
type Owned =
| Unclaimed
| Owner of char
| Claimed of char
| Boundary of char
让我们创建一个代表每个单元格所有权的矩阵
let claims =
dataMatrix
|> Array.map (fun row ->
row
|> Array.map (function
| '.' -> Owned.Unclaimed
| ch -> Owned.Owner(ch))
)
让我们有一个实用的方法来查看发生了什么。
let printIt () =
printfn ""
claims
|> Array.iter (fun row ->
row |> Array.iter (function
| Owned.Claimed(ch) -> printf " ."
| Owned.Owner(ch) -> printf " %c" ch
| Owned.Boundary(ch) -> printf " %c" ch
| _ -> printf " ." )
printfn "")
让我们创建一条记录来表示特定大写字母所在的位置。
type CapitalLocation = { X:int; Y:int; Letter:char }
现在我们要查找所有大写字母。
let capitals =
dataMatrix
|> Array.mapi (fun y row ->
row
|> Array.mapi (fun x item ->
match item with
| '.' -> None
| _ -> Some({ X=x; Y=y; Letter=item }))
|> Array.choose id
|> Array.toList
)
|> Array.fold (fun acc item -> item @ acc) List.empty<CapitalLocation>
|> List.sortBy (fun item -> item.Letter)
在前进的过程中,我们需要一个方向概念。
type Direction =
| Left = 0
| Up = 1
| Right = 2
| Down = 3
// Function gets the coordinates of the adjacent cell.
let getCoordinates (x, y) direction =
match direction with
| Direction.Left -> x-1, y
| Direction.Up -> x, y-1
| Direction.Right -> x+1, y
| Direction.Down -> x, y+1
| _ -> (-1,-1) // TODO: Figure out how to best throw an error here.
在前进的过程中,我们将需要了解大小。这将有助于我们监控我们是否正在超越界限。
type Size = { Width:int; Height: int }
// Get the size of the matrix.
let size = {Width=originalData.Head.Length; Height=originalData.Length}
活动模式:匹配给定单元格的条件。
let (|OutOfBounds|UnclaimedCell|Claimed|Boundary|) (x,y) =
match (x,y) with
| _,_ when x < 0 || y < 0 -> OutOfBounds
| _,_ when x >= size.Width || y >= size.Height -> OutOfBounds
| _ ->
match claims.[y].[x] with
| Owned.Unclaimed -> UnclaimedCell(x,y)
| Owned.Claimed(ch) -> Claimed(x,y,ch)
| Owned.Boundary(ch) -> Boundary(x,y,ch)
| Owned.Owner(ch) -> Claimed(x,y,ch)
现在我们要开始征收黄铜税了。这要求牢房!
let claimCell letter (x, y) =
// Side effect: Change the value of the cell
(claims.[y].[x] <- Owned.Claimed (System.Char.ToLower letter)) |> ignore
使用活动模式,声明该单元格(如果未声明),并返回相邻单元格的坐标。
let claimAndReturnAdjacentCells (letter, coordinates, direction) =
match coordinates with
| UnclaimedCell (x,y) ->
// Claim it and return the Owned object.
claimCell letter coordinates // meaningful side effect
// use Direction as int to allow math to be performed.
let directionInt = int direction;
Some(
// [counter-clockwise; forward; clockwise]
[(directionInt+3)%4; directionInt; (directionInt+1)%4]
|> List.map enum<Direction>
|> List.map (fun newDirection ->
(
letter,
getCoordinates coordinates newDirection,
newDirection
))
)
| Claimed(cx,cy,cch) when cch <> System.Char.ToLower letter->
// If we find a "Claimed" element that is not our letter, we have
// hit a boundary. Change "Claimed" to "Boundary" and return the
// element that led us to evaluating this element. It is also a
// boundary.
(claims.[cy].[cx] <- Owned.Boundary (System.Char.ToLower cch)) |> ignore
let reverseDirection = enum<Direction>(((int direction)+2)%4)
Some[(
cch,
getCoordinates (cx, cy) reverseDirection,
reverseDirection
)]
| _ -> None
我们开始创建此数据包的列表,让我们创建一个类型以使事情更清楚。
type CellClaimCriteria = (char * (int * int) * Direction)
给定用于声明单元格的条件列表,我们将遍历该列表,以返回下一个要声明的单元格并递归到该列表中。
let rec claimCells (items:CellClaimCriteria list) =
items
|> List.fold (fun acc item ->
let results = claimAndReturnAdjacentCells item
if Option.isSome(results)
then (acc @ Option.get results)
else acc
) List.empty<CellClaimCriteria>
|> (fun l ->
match l with
| [] -> []
| _ -> claimCells l)
对于每个资本,在每个方向上创建一个声明条件,然后递归声明这些单元格。
let claimCellsFromCapitalsOut ()=
capitals
|> List.fold (fun acc capital ->
let getCoordinates = getCoordinates (capital.X, capital.Y)
[Direction.Left; Direction.Up; Direction.Right; Direction.Down]
|> List.map (fun direction ->
(
capital.Letter,
getCoordinates direction,
direction
))
|> (fun items -> acc @ items)) List.empty<CellClaimCriteria>
|> claimCells
每个程序都需要一个主程序。
[<EntryPoint>]
let main args =
printIt()
claimCellsFromCapitalsOut()
printIt()
0