2次元リストの全要素の周囲の和を求める
例えば、以下のようなmatrixが与えられる
code:_
[
0, 1, 0,
1, 0, 0,
1, 1, 0
]
これらの各セルの周囲の和を求める
今回は、自分自身と周囲の和を求めることにする
周囲だけ欲しければ後から自分自身を引けばいいだけなのでmrsekut.icon
code:hs
neighbors xss = zipWith (zipWith (-)) (neighborhoodSum xss) xss
例えば、1行目の1列目 (左上)の0の周囲の和は
0(自分自身) + 1(右) + 1(下) + 0(右下) = 2
例えば、中央の0の周囲の和は
上記の3 * 3のmatrixの全セルの和なので、4
これを全セルに対して行う
今回の例だと9個のセルすべて
当然、3 * 3であるとは限らない
これをどのようにして求めるか
シグネチャ
code:hs
neighborhoodSum :: Int -> Int
実例
2023/10/5 Minesweeper
2024/12/22 Game of Life
いくつもの方法が考えられる
各セルごとに座標を持たせ、座標で計算する
modelを定義しちゃって、up, leftとかで取得できるようにする
3 * 3のwindowをスライドしていく
etc.
隣接する行同士を足し、その後、隣接する列同士を足すという方針
(もちろん順序は逆でもいい)
これは、この法則を見つけさえすれば、かなり単純に書けるmrsekut.icon
https://gyazo.com/93deba469fc2088011f5db4da0fa0116
code:hs
-- >>> neighborhoodSum 1,2,3],4,5,6,[7,8,9
-- 12,21,16],27,45,33,[24,39,28
neighborhoodSum :: Int -> Int
neighborhoodSum = rowSum . colSum
where
colSum = map windows3Sum
rowSum = transpose . map windows3Sum . transpose
-- >>> windows3Sum 1,2,3
-- 3,6,5
windows3Sum :: Int -> Int
windows3Sum = map sum . windows3 . padding
windows3 :: a -> a
windows3 xs = [ x, y, z | (x:y:z:_) <- tails xs ]
padding :: Int -> Int
padding xs = 0 : xs ++ 0
windows3
3要素のwindowをスライドする関数
あまり意味はないが、対象性をきれいに書ける
code:hs
neighborhoodSum :: Int -> Int
neighborhoodSum = t . t
where
t = transpose . map sumAdjacent
transposeを使わない多相版
これを参考にした
code:hs
-- >>> neighborhoodSum 1,2,3],4,5,6,[7,8,9
-- 12,21,16],27,45,33,[24,39,28
neighborhoodSum :: Int -> Int
neighborhoodSum = map smoothCols . smoothRows
where
smoothCols = trips add3 0
smoothRows = trips (zipWith3 add3) 0,0..
-- >>> trips add3 0 1,2,3,4,5
-- 3,6,9,12,9
trips :: (a -> a -> a -> b) -> a -> a -> b
trips f border = map (\a,b,c -> f a b c) . windows3 . padding border
-- >>> padding 0 1,2,3
-- 0,1,2,3,0
padding :: a -> a -> a
padding border xs = border : xs ++ border
-- >>> windows3 1,2,3,4,5
-- 1,2,3],2,3,4,[3,4,5
windows3 :: a -> a
windows3 xs = [ x, y, z | (x:y:z:_) <- tails xs ]
add3 :: Int -> Int -> Int -> Int
add3 a b c = a + b + c
3*3のwindowをslideして求める
3要素のwindowをスライドする関数を使う
リストの周囲を0でpaddingする
code:hs
-- >>> neighborhoodSum 1,2,3],4,5,6,[7,8,9
-- 12,21,16],27,45,33,[24,39,28
neighborhoodSum :: Int -> Int
neighborhoodSum = map (map sum) . windows33 . padzeros
padzeros :: Int -> Int
padzeros = map col . row
where
row = padding =<< (*> 0) . head
col = padding 0
padding :: a -> a -> a
padding border xs = border : xs ++ border
windows33 :: a -> a
windows33 = map (map concat . transpose . map windows3) . windows3
windows3 :: a -> a
windows3 xs = [ x, y, z | (x:y:z:_) <- tails xs ]
インデックスアクセスする
2023/10/5 Minesweeper#6523a3fa19827000003b7c36
具体的になりがち
DSLを作る
modelを作る
up, left, などで取得できるようにする
大仰になりがち