2014-09-04 6 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]) 

을 지금, 나는 확신이이 데이터 유형

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은 내가 comonad 인스턴스를 사용하는 (있는 경우) 혜택의 종류를받을 수 있나요?

+0

임의의 합성 주석에서 작동하도록 매개 변수화 된 모든 기능을 사용할 수 있습니다. 나는 지금 당장은 지적 할 수 없지만, 존재하는 것이 있다고 확신합니다. –

+0

[하스켈의 comonad typeclass 란 무엇입니까?] (http://stackoverflow.com/questions/8428554/what-is-the-comonad-typeclass-in-haskell?rq=1) – sjy

답변

2

타입 클래스를 제공하거나 인터페이스를 구현할 때의 장점은 해당 typeclass 또는 인터페이스를 사용하도록 작성된 코드가 수정하지 않고 코드를 사용할 수 있다는 것입니다.

어떤 프로그램을 Comonad으로 쓸 수 있습니까? Comonadextract을 사용하여 현재 위치의 값을 검사하고 (이웃을 관찰하지 않고) duplicate 또는 extend으로 모든 위치의 이웃을 관찰하는 방법을 제공합니다. 추가 기능이 없어도이 기능은 대단히 유용하지 않습니다. 그러나 Comonad 인스턴스와 함께 다른 기능이 필요한 경우 로컬 데이터와 다른 곳의 데이터에 의존하는 프로그램을 작성할 수 있습니다. 예를 들어, advance과 같이 위치를 변경할 수있는 함수가 필요하면 데이터 구조 자체가 아니라 데이터의 로컬 구조에만 의존하는 프로그램을 작성할 수 있습니다.

는 구체적인 예를 들어, Comonad면 다음과 같은 Bidirectional 클래스로 작성 셀룰러 오토마타 프로그램을 고려 : extract 데이터가 셀에 저장에, Comonad과 함께,

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

프로그램이 사용할 수 및 현재 셀의 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 

이 프로그램

은 다음 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 

뿐만 아니라 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))