所以我最近一直在尝试固定点,并最终通过常规的固定点来发现一些用途;现在我正在转向共生固定点,我担心我被卡住了;
下面是我尝试过的几个例子,以及哪些成功了,哪些失败了:
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
module WFix where
import Control.Comonad
import Control.Comonad.Cofree
import Control.Monad.Fix因此,我从loeb定理作为一个列表开始;列表中的每个元素都是一个函数,它接受最终结果来计算它的答案;这让我可以进行“电子表格”计算,其中值可以依赖于其他值。
spreadSheetFix :: [Int]
spreadSheetFix = fix $ \result -> [length result, (result !! 0) * 10, (result !! 1) + 1, sum (take 3 result)]好了,我已经完成了基本的修复工作,是时候转向comonad类型了!下面是几个简单的comonads示例:
data Stream a = S a (Stream a)
deriving (Eq, Show, Functor)
next :: Stream a -> Stream a
next (S _ s) = s
instance Comonad Stream where
extract (S a _) = a
duplicate s@(S _ r) = S s (duplicate r)
instance ComonadApply Stream where
(S f fs) <@> (S a as) = S (f a) (fs <@> as)
data Tape a = Tape [a] a [a]
deriving (Show, Eq, Functor)
moveLeft, moveRight :: Tape a -> Tape a
moveLeft w@(Tape [] _ _) = w
moveLeft (Tape (l:ls) a rs) = Tape ls l (a:rs)
moveRight w@(Tape _ _ []) = w
moveRight (Tape ls a (r:rs)) = Tape (a:ls) r rs
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w@(Tape l _ r) = Tape lefts w rights
where
lefts = zipWith const (tail $ iterate moveLeft w) l
rights = zipWith const (tail $ iterate moveRight w) r
instance ComonadApply Tape where
Tape l f r <@> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')好的,下面的组合子来自Control.Comonad;
wfix :: Comonad w => w (w a -> a) -> a
wfix w = extract w (extend wfix w)
cfix :: Comonad w => (w a -> a) -> w a
cfix f = fix (extend f)
kfix :: ComonadApply w => w (w a -> a) -> w a
kfix w = fix $ \u -> w <@> duplicate u我从尝试wfix开始:
streamWFix :: Int
streamWFix = wfix st
where
incNext = succ . extract . next
st = (S incNext (S incNext (S (const 0) st)))
> streamWFix
-- 2这似乎是通过调用w上的第一个w a -> a来工作的,直到在本例中达到解析const 0为止;这是有意义的。我们也可以使用磁带来完成此操作:
selfReferentialWFix :: Int
selfReferentialWFix = wfix $ Tape [const 10] ((+5) . extract . moveLeft) []
-- selfReferentialWFix == 15我想我明白了,但是下一次我有点卡住了,我似乎没有直觉知道cfix应该做什么。即使是最简单的东西,当我评估它时,我也会永远想着旋转;甚至尝试使用getOne提取流的第一个元素也失败了。
getOne :: Stream a -> a
getOne (S a _) = a
simpleCFix :: Stream Int
simpleCFix = cfix go
where
go _ = 0与kfix类似;即使是简单的尝试似乎也不会终止。我对kfix的理解是,每个“槽”中的函数都会传递一个专注于该点的已评估comonad的副本;是这样吗?
我试着在这上面使用'getOne‘:
streamKFix :: Stream Int
streamKFix = kfix st
where
go _ = 0
st = S go st下面是使用Tape的有限尝试,它也无法运行:
tapeKFix :: Tape Int
tapeKFix = kfix $ Tape [] (const 0) []那么,对于我的问题,有人能提供一些使用cfix和kfix的可运行的(非平凡的)示例,并解释它们是如何工作的吗?我计划使用kfix来最终做一个“Conway的生活游戏”风格的实验,我认为kfix在与给定细胞周围的邻居一起工作时是有用的,对吗?
请随时提出任何澄清的问题,并帮助我扩展我的知识和直觉的fix!
谢谢!
发布于 2017-07-18 16:27:23
Tape的ComonadApply和Comonad实例还不够懒惰,不能与kfix一起使用。
适用于Tape的duplicate要求您先证明磁带存在,然后才能断定结果为Tape
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w@(Tape l _ r) = Tape lefts w rights
-- ^ ^
-- matches a Tape |
-- before determining that the result is a Tape<@>会先检查两个参数是否都是磁带,然后才能得出结果是Tape
instance ComonadApply Tape where
Tape l f r <@> Tape l' a r' = Tape (zipWith ($) l l') (f a) (zipWith ($) r r')
-- ^ ^ ^
-- matches two Tapes |
-- before detrmining that the result is a Tape总而言之,kfix (Tape _ _ _)永远不可能生成Tape
kfix w = fix $ \u -> w <@> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <@> duplicate u
kfix (Tape _ _ _) = fix $ \u -> (Tape _ _ _) <@> case u of (Tape _ _ _) -> ...
-- ^ |
-- ----------- <<loop>> -------------您可以通过使用irrefutable patterns提高duplicate和/或<@>的工作效率来解决此问题。即使还没有生成Tape构造函数,模式~(Tape l a r)也是匹配的。下面是如何使用它来提高duplicate的工作效率
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w@(~(Tape l _ r)) = Tape lefts w rights
where
lefts = zipWith const (tail $ iterate moveLeft w) l
rights = zipWith const (tail $ iterate moveRight w) r不可反驳的模式匹配等同于使用函数来提取值。对于duplicate来说,它等同于编写
left (Tape l _ _) = l
right (Tape _ _ r) = r
instance Comonad Tape where
extract (Tape _ a _) = a
duplicate w = Tape lefts w rights
where
l = left w
r = right w
...https://stackoverflow.com/questions/45158934
复制相似问题