リストの周囲を0でpaddingする
一次元なら例えば
これの周囲を0 paddingして
[1,2,3]
これを得る
[0,1,2,3,0]
二次元なら例えば
これの周囲を0 paddingして
code:_
[
1,2,3,
4,5,6,
7,8,9
]
これを得る
code:_
[
0,0,0,0,0,
0,1,2,3,0,
0,4,5,6,0,
0,7,8,9,0,
0,0,0,0,0
]
一次元の方は何も難しくない
code:hs
-- >>> padzeros 1,2,3
-- 0,1,2,3,0
padzeros :: Int -> Int
padzeros xs = 0 : xs ++ 0
この「paddingする部分」を関数に切り出してみる
code:hs
padzeros :: Int -> Int
padzeros = padding 0
padding :: Int -> Int -> Int
padding n xs = n : xs ++ n
更に、paddingを多相にする
code:hs
padzeros :: Int -> Int
padzeros = padding 0
padding :: a -> a -> a
padding border xs = border : xs ++ border
aに[Int]を入れることができ、これが二次元の実装につながる
サイズを3*3に固定して2次元の実装を考える
こんな感じで書ける
code:hs
padzeros :: Int -> Int
padzeros = map col . row
where
row = padding 0,0,0
col = padding 0
padding :: a -> a -> a
padding border xs = border : xs ++ border
何が面白いのかと言うと、paddingをrowにもcolにも使えている点mrsekut.icon
大まかな流れとしては、
まず列方向に0 paddingし、その後、一次元のときと同様に行方向に0 paddingしてる
https://gyazo.com/480522f69829b495ffcb34a052185c20
では、これの3*3の制限をなくして一般化する
2次元の実装はこう書ける
code:hs
-- >>> padzeros [ 1,2,3, 4,5,6, 7,8,9 ]
-- 0,0,0,0,0],0,1,2,3,0,0,4,5,6,0,0,7,8,9,0,[0,0,0,0,0
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
row周りの解説
要するに、こんな風に呼んでる
code:hs
row xs = expand 0,0,0,0,0 xs
リストに対して*>を使ってる
リストに対する定義はこんな感じ
code:hs
xs *> ys = y | _ <- xs, y <- ys
実際の挙動の例
code:hs
example1 :: Int
example1 = 1, 2, 3 *> 4, 5
-- 結果: 4, 5, 4, 5, 4, 5
左項の要素ごとに、右項の値を繰り返す
従って、以下の様に書けば、同じ長さの0の列を返す
code:hs
-- >>> example2 1,2,3,4,5
-- 0,0,0,0,0
example2 xs = xs *> 0
bindのところは関数モナドを使ったpoint free styleに書いた
rowの定義は以下のように書いても全部同じ
code:hs
row = padding =<< (*> 0) . head -- point-free
row xs = padding (((*> 0) . head) xs) xs -- point復活
row xs = padding (head xs *> 0) xs -- (*>)を中置