提供类型类或实现接口的优点是,为使用该类型类或接口而编写的代码无需任何修改即可使用您的代码。
可以用什么程序编写Comonad
?AComonad
提供了一种方法来检查当前位置的值(不观察其邻居),使用或extract
观察每个位置的邻域。如果没有任何附加功能,这并不是非常有用。但是,如果我们还需要其他函数以及实例,我们可以编写依赖于本地数据和来自其他地方的数据的程序。例如,如果我们需要允许我们更改位置的函数,例如 your ,我们可以编写仅依赖于数据的本地结构而不依赖于数据结构本身的程序。duplicate
extend
Comonad
advance
举一个具体的例子,考虑一个用Comonad
以下Bidirectional
类编写的元胞自动机程序:
class Bidirectional c where
forward :: c a -> Maybe (c a)
backward :: c a -> Maybe (c a)
该程序可以将其与 一起Comonad
用于extract
存储在单元格中的数据并探索单元格forward
和backward
当前单元格。它可以duplicate
用来捕获每个单元的邻域并fmap
检查该邻域。这种组合fmap f . duplicate
是extract f
。
这是这样一个程序。rule'
仅对示例感兴趣;它仅使用左右值在邻域上实现元胞自动机规则。rule
在给定类的情况下从邻域中提取数据,并对每个邻域运行规则。slice
拉出更大的社区,以便我们可以轻松地展示它们。simulate
运行模拟,显示每一代的这些较大的邻域。
rule' :: Word8 -> Bool -> Bool -> Bool -> Bool
rule' x l m r = testBit x ((if l then 4 else 0) .|. (if m then 2 else 0) .|. (if r then 1 else 0))
rule :: (Comonad w, Bidirectional w) => Word8 -> w Bool -> w Bool
rule x = extend go
where
go w = rule' x (maybe False extract . backward $ w) (extract w) (maybe False extract . forward $ w)
slice :: (Comonad w, Bidirectional w) => Int -> Int -> a -> w a -> [a]
slice l r a w = sliceL l w (extract w : sliceR r w)
where
sliceR r w | r > 0 = case (forward w) of
Nothing -> take r (repeat a)
Just w' -> extract w' : sliceR (r-1) w'
sliceR _ _ = []
sliceL l w r | l > 0 = case (backward w) of
Nothing -> take l (repeat a) ++ r
Just w' -> sliceL (l-1) w' (extract w':r)
sliceL _ _ r = r
simulate :: (Comonad w, Bidirectional w) => (w Bool -> w Bool) -> Int -> Int -> Int -> w Bool -> IO ()
simulate f l r x w = mapM_ putStrLn . map (map (\x -> if x then '1' else '0') . slice l r False) . take x . iterate f $ w
该程序可能旨在与列表中的以下内容一起Bidirectional
Comonad
使用Zipper
。
data Zipper a = Zipper {
heads :: [a],
here :: a,
tail :: [a]
} deriving Functor
instance Bidirectional Zipper where
forward (Zipper _ _ [] ) = Nothing
forward (Zipper l h (r:rs)) = Just $ Zipper (h:l) r rs
backward (Zipper [] _ _) = Nothing
backward (Zipper (l:ls) h r) = Just $ Zipper ls l (h:r)
instance Comonad Zipper where
extract = here
duplicate (Zipper l h r) = Zipper (goL (h:r) l) (Zipper l h r) (goR (h:l) r)
where
goL r [] = []
goL r (h:l) = Zipper l h r : goL (h:r) l
goR l [] = []
goR l (h:r) = Zipper l h r : goR (h:l) r
但也可以与CyclicList
Bidirectional
Comonad
.
data CyclicList a = CL a (Seq a)
deriving (Show, Eq, Functor)
instance Bidirectional CyclicList where
forward (CL x xs) = Just $ case viewl xs of
EmptyL -> CL x xs
x' :< xs' -> CL x' (xs' |> x)
backward (CL x xs) = Just $ case viewr xs of
EmptyR -> CL x xs
xs' :> x' -> CL x' (x <| xs')
instance Comonad CyclicList where
extract (CL x _) = x
duplicate (CL x xs) = CL (CL x xs) (go (singleton x) xs)
where
go old new = case viewl new of
EmptyL -> empty
x' :< xs' -> CL x' (xs' >< old) <| go (old |> x') xs'
我们可以重用simulate
任何一种数据结构。有一个更有趣的CyclicList
输出,因为它不是撞到墙上,而是回绕来与自己交互。
{-# LANGUAGE DeriveFunctor #-}
import Control.Comonad
import Data.Sequence hiding (take)
import Data.Bits
import Data.Word
main = do
putStrLn "10 + 1 + 10 Zipper"
simulate (rule 110) 10 10 30 $ Zipper (take 10 . repeat $ False) True (take 10 . repeat $ False)
putStrLn "10 + 1 + 10 Cyclic"
simulate (rule 110) 10 10 30 $ CL True (fromList (take 20 . repeat $ False))