Я начал с создания двух положительных тестовых случаев для нашей функции 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