我正在研究实施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
的剩余部分。
答案 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
让我们分别调用Cons
,flatten
和reduceR
这些函数的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
下一个Applicative
和Alternative
的实例。
答案 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)