snocリストを作ってみよう。

30分プログラム、その597。snocリストを作ってみよう。

普通、リストは

data List a = Cons a (List a) | Nil

と定義するところを逆に

data List a = Snoc (List a) a | Nil

と定義したsnoc listというものがあるらしいです。

普通のリストと逆なので、末尾への要素追加がO(1)で、先頭への要素追加がO(n)になります。あとおもしろいことに、foldrが末尾再帰で定義できたりします。
というわけで、これを作ってみよう。

使い方

-- 普通のリストからsnocリストを作る
*Main> fromList [1,2,3]
Snoc (Snoc (Snoc Nil 1) 2) 3

-- map
*Main> Main.map (+1) $ fromList [1, 2, 3]
Snoc (Snoc (Snoc Nil 2) 3) 4

ソースコード

data List a = Snoc (List a) a| Nil deriving (Show,Eq)

snoc :: List a -> a -> List a
last :: List a -> a
init :: List a -> List a
head :: List a -> a
tail :: List a -> List a
fromList :: [a] -> List a
map   :: (a -> b) -> List a -> List b
length :: List a -> Int
foldr :: (a -> b -> b) -> b -> List a -> b
foldl :: (a -> b -> a) -> a -> List b -> a

fromList xs = aux Nil xs
    where aux xs []     = xs
          aux xs (y:ys) = aux (Snoc xs y) ys

snoc xs x = Snoc xs x

last Nil = error "empty list"
last (Snoc _ x) = x

init Nil = error "empty list"
init (Snoc xs _) = xs

head Nil = error "empty list"
head (Snoc Nil x) = x
head (Snoc xs _)  = Main.head xs

tail Nil = error "empty list"
tail (Snoc Nil x) = Nil
tail (Snoc xs x)  = Snoc (Main.tail xs) x

map _ Nil = Nil
map f (Snoc xs x) = Snoc (Main.map f xs) (f x)

length Nil = 0
length (Snoc xs _) = 1 + Main.length xs

foldr f init Nil = init
foldr f init (Snoc xs x) = Main.foldr f (f x init) xs

foldl f init Nil = init
foldl f init (Snoc xs x) = f (Main.foldl f init xs) x