为`Cons a`实现`concat`

时间:2014-12-11 04:12:35

标签: haskell

我正在研究实施instance Monad []的练习。但是,我想根据以下定义实现Monad Cons

data Cons a = Cons a (Cons a) | Empty

我尝试实现等效的concat,但我称之为flatten

flatten :: Cons (Cons a) -> Cons a
flatten Empty                  = Empty
flatten (Cons c@(Cons _ _) xs) = ...

但后来我对[a]如何映射到Cons a (Cons a)感到困惑。

请给我一个提示,写下flatten的剩余部分。

2 个答案:

答案 0 :(得分:4)

实际上非常简单。 concat函数通常定义如下:

concat :: [[a]] -> [a]
concat = foldr (++) []

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ a []     = a
foldr f a (x:xs) = f x (foldr f a xs)

(++) :: [a] -> [a] -> [a]
[]     ++ ys = ys
(x:xs) ++ ys = x : xs ++ ys

让我们分别调用ConsflattenreduceR这些函数的append版本:

flatten :: Cons (Cons a) -> Cons a
flatten = reduceR append Empty

reduceR :: (a -> b -> b) -> b -> Cons a -> b
reduceR _ a Empty       = a
reduceR f a (Cons x xs) = f x (reduceR f a xs)

append :: Cons a -> Cons a -> Cons a
append Empty       ys = ys
append (Cons x xs) ys = Cons x (append xs ys)

与@jamshidh不同,我使用了右侧折叠而不是左侧折叠,因为实现(++)的方式,a ++ (b ++ c)的计算成本低于(a ++ b) ++ c

现在我们可以将Cons设为Monad的实例,如下所示:

instance Functor Cons where
    fmap _ Empty       = Empty
    fmap f (Cons x xs) = Cons (f x) (fmap f xs)

instance Monad Cons where
    return a = Cons a Empty
    m >>= f = flatten (map f m)

简单。请尝试Cons下一个ApplicativeAlternative的实例。

答案 1 :(得分:3)

为了防止爆炸,我首先要定义一个结合了两个Cons a项目的函数

consPlus::Cons a->Cons a->Cons a
consPlus Empty x = x
consPlus (Cons x rest) y = Cons x (consPlus rest y)

然后我会定义Cons a

foldl版本
consFoldl::(b->a->b)->b->Cons a->b
consFoldl f init Empty = init
consFoldl f init (Cons x rest) = consFoldl f (f init x) rest

然后最终以明显的方式将其扩展到concat

consConcat = consFoldl consPlus Empty

另一种避免脑爆炸的技术就是定义Cons a这样的

data Cons a = a ::: Cons a | Empty
infixr 8 :::

区别在于语法,但更容易阅读

(1:::2:::Empty):::(3:::Empty):::Empty

比这个

Cons (Cons 1 (Cons 2 Empty)) (Cons (Cons 3 Empty) Empty)