我首先为我们的 check 函数创建了两个正面测试用例 -
const puzzle1 =
[ [" ", " ", "#", "#", "#", " ", " ", " ", " ", " "]
, [" ", " ", "#", " ", "#", " ", " ", " ", " ", " "]
, [" ", " ", "#", "#", "#", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
const puzzle2 =
[ [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", " ", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
console.log(check(puzzle1))
// expected: [ 0, 2 ]
console.log(check(puzzle2))
// expected: [ 3, 4 ]
还有一个否定的情况-
const puzzle3 =
[ [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
console.log(check(puzzle3))
// expected: false
首先我实现了cross 和blank 来更轻松地处理子问题-
const cross = x =>
x === "#"
const blank = x =>
x === " "
现在我们实现check -
const check =
( [ [ a, b, c, ...r0 ] = []
, [ d, e, f, ...r1 ] = []
, [ g, h, i, ...r2 ] = []
, ...rest
]
, row = 0
, col = 0
) =>
// bottom-right is out of bounds
i === undefined
? false
// perimeter is cross and center is blank
// solution found: return the row and col
: [ a, b, c, d, f, g, h, i ] .every (cross) && blank (e)
? [ row, col ]
// otherwise check the next column
: check
( [ [ b, c, ...r0 ]
, [ e, f, ...r1 ]
, [ h, i, ...r2 ]
, ...rest .map (([ _, ...row ]) => row)
]
, row
, col + 1
)
|| // or check the next row
check
( [ [ d, e, f, ...r1 ]
, [ g, h, i, ...r2 ]
, ...rest
]
, row + 1
, col
)
我喜欢这个解决方案,因为我们可以直观地看到检查中发生了什么 -
// (a b c f i h g d) makes a "donut" shape
// (e) is the donut hole
[ [ a, b, c, ...r0 ] = []
, [ d, e, f, ...r1 ] = []
, [ g, h, i, ...r2 ] = []
, ...rest
]
// how am I supposed to visualize this?
arr[row][col] == "#"
arr[row+1][col] == "#"
arr[row+2][col] == "#"
arr[row][col+1] == "#"
arr[row+1][col+1] == " "
arr[row+2][col+1] == "#"
arr[row][col+2] == "#"
arr[row+1][col+2] == "#"
arr[row+2][col+2] == "#"
使用嵌套的for 循环会限制我们使用索引来思考问题并执行手动查找,例如arr[row+1][col+2]。在这种粒度级别上思考会使大脑感到疲劳,并且容易产生错误的程序。相比之下,深度解构和递归的使用使我们的程序能够反映其数据的形状并将我们从复杂性中解放出来。
展开下面的程序,在你自己的浏览器中验证结果-
const cross = x =>
x === "#"
const blank = x =>
x === " "
const check =
( [ [ a, b, c, ...r0 ] = []
, [ d, e, f, ...r1 ] = []
, [ g, h, i, ...r2 ] = []
, ...rest
]
, row = 0
, col = 0
) =>
i === undefined
? false
: [ a, b, c, d, f, g, h, i ] .every (cross) && blank(e)
? [ row, col ]
: check
( [ [ b, c, ...r0 ]
, [ e, f, ...r1 ]
, [ h, i, ...r2 ]
, ...rest .map (([ _, ...row ]) => row)
]
, row
, col + 1
)
||
check
( [ [ d, e, f, ...r1 ]
, [ g, h, i, ...r2 ]
, ...rest
]
, row + 1
, col
)
const puzzle1 =
[ [" ", " ", "#", "#", "#", " ", " ", " ", " ", " "]
, [" ", " ", "#", " ", "#", " ", " ", " ", " ", " "]
, [" ", " ", "#", "#", "#", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
const puzzle2 =
[ [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", " ", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
const puzzle3 =
[ [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", "#", "#"]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", "#", "#", "#", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
, [" ", " ", " ", " ", " ", " ", " ", " ", " ", " "]
]
console.log(check(puzzle1))
// [ 0, 2 ]
console.log(check(puzzle2))
// [ 3, 4 ]
console.log(check(puzzle3))
// false