Edited at

haskellで2重リストにmapを適用させる例


最初に

手続き型言語においては、2次元配列や二重リストなどは扱うことも多々あり、二重のforループを用いることで代入や変更が容易ですが、Haskellで考える際にどのように考えるのが良いのかと詰まったので共有しておきます。理解の助けになれば幸いです。


Haskellでリストを扱う際について

haskellにおいては、リストのリストを扱うときでも、リストのように扱うことができます。

先に実例を述べてしまうと、

remap :: (a -> b) -> [[a]] -> [[b]]

remap f = map $ map f

このような形で実装することもできます

実際にHoogleでmapの型を見に行くと、


map :: (a -> b) -> [a] -> [b]


このようにリストを引数に取っていますが、今回のようにリストのリストを渡すことも可能です。

ですがそれでは味気ないので、いくつかのアプローチから多重リストを扱う関数を考えてみましょう。


とりあえず二重リストで実装する


再帰で実装してみる

再帰でプログラムを考える際には、終わる条件と繰り返す部分を考えればいいわけですから、今回も同じように考えてみますと、

リストが最後になれば終了し、_ [] = []

二重リストのうち最初のリストにmap関数を、残りのリストに自分を適用すればいいわけですから、

f (x:xs) = map f x : 自分自身 f xs このようになります。

つまり、

recursionMap :: (a -> b) -> [[a]] -> [[b]]

recursionMap _ [] = []
recursionMap f (x:xs) = map f x : recursionMap f xs

このように実装できるわけです。(mapを使わずに実装する方法も考えたのですが、思いつかなかったので断念しました。


リスト内包表記で表現してみる

ところで、再帰を使うことで関数を実装してみましたが、そもそも束縛が一度でいいなら、関数を用意する必要すらないわけです。そこで、リスト内包表記を用いて実装することも考えてみましょう。

リスト内包表記は、当然二重のリストも扱うことが可能なため、 リスト内包表記を入れ子にすることで実装することができます。例えば、[[1,1],[1,1]]と言うリストに(+1)と言う関数をmapしたいときは、

[ [ x +1 | x <- xs] | xs <- [[1,1],[1,1]] ] 

このように表現できます。ですが、二重のリスト内包表記は、非常に可視性が悪く、コードのわかりやすさを損なうおそれがありますので、

[ map (+1) xs | xs <- [[1], [1]] ]

このように書く方が良いでしょう。


最後に

ところでこのように二重リストを考えましたが、私もまだまだHaskell勉強中の身でありますため、

もっと綺麗に書く方法であるとか、他の実装方法などあれば批評などドシドシお願いいたします。