2014-09-04 43 views
6

在我的應用程序中,我試圖實現一個動畫系統。在這個系統中,動畫被表示爲幀的循環列表:從創建Comonad實例中獲得什麼好處

data CyclicList a = CL a [a] 

我們可以(低效率)推進動畫如下:

advance :: CyclicList a -> CyclicList a 
advance (CL x []) = CL x [] 
advance (CL x (z:zs)) = CL z (zs ++ [x]) 

現在,我敢肯定,這個數據類型是comonad:

instance Functor CyclicList where 
    fmap f (CL x xs) = CL (f x) (map f xs) 

cyclicFromList :: [a] -> CyclicList a 
cyclicFromList [] = error "Cyclic list must have one element!" 
cyclicFromList (x:xs) = CL x xs 

cyclicLength :: CyclicList a -> Int 
cyclicLength (CL _ xs) = length xs + 1 

listCycles :: CyclicList a -> [CyclicList a] 
listCycles cl = let 
    helper 0 _ = [] 
    helper n cl' = cl' : (helper (n-1) $ advance cl') 
in helper (cyclicLength cl) cl 

instance Comonad CyclicList where 
    extract (CL x _) = x 
    duplicate = cyclicFromList . listCycles 

我的問題是:我會得到什麼樣的好處(如果有的話)使用comonad實例?

+0

您將能夠使用參數化的任何函數在任意連接器上工作。我現在不能指出任何權利,但我確信有一些存在。 – 2014-09-04 23:38:03

+0

[什麼是Haskell中的comonad類型類?](http://stackoverflow.com/questions/8428554/what-is-the-comonad-typeclass-in-haskell?rq=1) – sjy 2014-09-05 07:31:41

回答

2

提供類型類或實現接口的優點是,爲了使用該類型類型或接口而編寫的代碼可以在不做任何修改的情況下使用您的代碼。

根據Comonad可以編寫什麼程序?一個Comonad提供了一種既檢查在當前位置的值使用extract和方式與duplicateextend觀察每個位置附近(不遵守它的鄰居)。沒有任何額外的功能,這不是非常有用。但是,如果我們還需要其他函數以及Comonad實例,我們可以編寫依賴於本地數據和來自其他地方的數據的程序。例如,如果我們需要允許我們更改位置的函數(例如advance),那麼我們可以編寫僅依賴於數據本地結構的程序,而不是數據結構本身。

對於一個具體的例子,考慮寫在Comonad術語和下述Bidirectional類元胞自動機程序:

class Bidirectional c where 
    forward :: c a -> Maybe (c a) 
    backward :: c a -> Maybe (c a) 

這一方案可以利用這一點,連同Comonad,對存儲在單元extract數據和探索當前單元格的單元格forwardbackward。它可以使用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 

這一計劃可能已經打算用下面的BidirectionalComonad,名單上的一個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 

但也將與CyclicListBidirectionalComonad工作。

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)) 
相關問題