董事会是int[][]
,我想找到这种形状
1
1
1
来自电路板的所有4个对称(旋转)变体并记录位置。 e.g。
...
...
... x x x x x x ...
... x x 1 1 x x ...
... x 1 x x x x ...
... x x x x x x ...
...
...
使用F#处理这类问题会更好吗?
下面是我的c#代码,用于仅垂直检查模式(水平检查的代码是相似的)
List<Position> GetMatchVertical(int reelID)
{
List<Position> ret = new List<Position>();
var myReel = board[reelID];
var leftReel = reelID - 1 >= 0 ? board[reelID - 1] : null;
var rightReel = reelID + 1 < boardSize ? board[reelID + 1] : null;
int currentColor = myReel[0];
for (int reelPosition = 1; reelPosition < boardSize; reelPosition++)
{
int nextColor = myReel[reelPosition];
if (currentColor == nextColor)
{
if (leftReel!=null)
{
if (reelPosition + 1 < boardSize && leftReel[reelPosition + 1] == currentColor)
{
ret.Add(logPosition(...));
}
}
if (rightReel!=null)
{
if (reelPosition - 2 >= 0 && rightReel[reelPosition - 2] == currentColor)
{
ret.Add(logPosition(...));
}
}
}
else
{
currentColor = nextColor;
}
}
return ret;
}
答案 0 :(得分:15)
这绝对非常适合函数式编程和F#。有许多可能的方法。我认为pad的解决方案可能是最直接的解决方案,这是一个非常好的起点。如果你需要更通用的东西,那么Huusom的解决方案非常好。
还有更通用的方法,即构建域特定语言(DSL)来检测数组中的模式。这是更高级的功能技术,但它非常适合您的示例。如果你这样做,那么你可以用一种非常简洁的方式表达相当复杂的模式。这是一个例子:
// Create a detector that tests if a location
// contains 1 and returns 'false' when out of range
let one = border false (equals 1)
// A shape detector for your pattern
let pattern =
around (0, 0) one <&> around (1, 0) one <&>
around (-1, 1) one
// Test pattern with any rotation: Combine
// 4 possible rotations with logical or.
let any =
pattern <|> rotate pattern <|>
rotate (rotate pattern) <|>
rotate (rotate (rotate pattern))
此示例使用各种基元来构建模式的声明性规范。值any
表示可以运行以测试模式是否发生在给定位置的函数。它处理模式的所有旋转,它也进行边界检查。您还需要添加镜像模式,但这很容易扩展。
解释实现可能需要一篇完整的博客文章,但这里有一个注释源代码应该是非常易读的:
/// A type that represents a function that tests
/// whether an array contains some pattern at a
/// specified location. It gets the location to
/// test & the array as arguments and returns bool.
type ShapeDetector = SD of (int -> int -> int[,] -> bool)
/// A primitive that tests whether the value at the
/// current location contains a value 'v'
let equals v = SD (fun x y arr -> arr.[x,y] = v)
/// A combinator that takes 'ShapeDetector' and
/// creates a new one that returns 'def' when
/// accessing outside of the array bounds
let border def (SD f) = SD (fun x y arr ->
if x < 0 || y < 0 || x >= arr.GetLength(0) || y >= arr.GetLength(1)
then def else f x y arr)
/// A combinator that calls a given ShapeDetector
/// at a location specified by offset dx, dy
let around (dx, dy) (SD f) = SD (fun x y arr ->
f (x + dx) (y + dy) arr)
/// A combinator that takes a ShapeDetector and
/// builds a new one, which is rotated by 90 degrees
let rotate (SD f) = SD (fun x y arr ->
f -y x arr)
/// Creates a shape detector that succeeds only
/// when both of the arguments succeed.
let (<&>) (SD f1) (SD f2) = SD (fun x y arr ->
f1 x y arr && f2 x y arr)
/// Creates a shape detector that succeeds
/// when either of the arguments succeed.
let (<|>) (SD f1) (SD f2) = SD (fun x y arr ->
f1 x y arr || f2 x y arr)
最后,这是一个在样本2D阵列上运行模式检测器的示例:
// Create a 2D array as a sample input
let inp =
array2D [ [ 0; 0; 1 ]
[ 0; 1; 0 ]
[ 0; 1; 0 ] ]
// Get the underlying function and run it
// for all possible indices in the array
let (SD f) = any
for x in 0 .. 2 do
for y in 0 .. 2 do
printfn "%A %A" (x, y) (f x y inp)
答案 1 :(得分:4)
你可以在F#中找到使用模式匹配的水平形状(对于垂直形状也是如此):
/// Try to match with horizontal shapes
/// 1 x x and 1 1 x
/// x 1 1 x x 1
///
/// 1 1 x and x x 1
/// x x 1 1 1 x
/// could be found by reversing matched sub-arrays
let matchHorizontalShapes (board: _ [] []) =
let positions = ResizeArray()
for i in 0..board.Length - 2 do
for j in 0..board.[0].Length - 3 do
match [|board.[i].[j..j+2];
board.[i+1].[j..j+2]|] with
| [|[|1; 1; _|];
[|_; 1; 1|]|] -> positions.Add((i, j), (i+1, j+1), (i+1, j+2))
positions.Add((i, j), (i, j+1), (i+1, j+2))
| [|[|1; _; _|];
[|_; 1; 1|]|] -> positions.Add((i, j), (i+1, j+1), (i+1, j+2))
| [|[|1; 1; _|];
[|_; _; 1|]|] -> positions.Add((i, j), (i, j+1), (i+1, j+2))
| _ -> ()
positions.ToArray()
答案 2 :(得分:1)
如果您根据模式创建一组坐标偏移,则可以获取值并将结果与一组已知值匹配。
let find_matches board pattern =
let xb = Array2D.length1 board
let yb = Array2D.length2 board
// safe lookup on board
let get_value= function
| (x, _) when (x < 0) || (x >= xb) -> None
| (_, y) when (y < 0) || (y >= yb) -> None
| (x, y) -> Some (Array2D.get board x y)
// do a patten match on board.
let has_pattern = function
| [Some 1; Some 1; Some 1] -> true
| _ -> false
// se if a given coordinate is a match
let is_match (x,y) =
pattern
|> List.map (fun (x',y') -> (x+x', y+y')) // expand the coordinat to a list of coordinates
|> List.map get_value // find the values coordinates
|> has_pattern // match to pattern
[for x in 0..(xb-1) do for y in 0..(yb-1) -> x, y]
|> List.filter is_match
此功能适用于[(0,0); (1, -1); (1, -2)]
的模式(您的示例来自上方)。
请注意我使用的是Array2D(int [,])而不是示例中给出的int [] []。