Performance 康威'的性能;使用商店comonad的生活游戏

Performance 康威'的性能;使用商店comonad的生活游戏,performance,haskell,comonad,Performance,Haskell,Comonad,我已经编写了一个使用comonad的简单实现(参见下面的代码)。我的问题是,从第五次迭代开始,网格生成明显变慢。我的问题是否与我正在使用Store comonad有关?还是我犯了一个明显的错误?据我所知,基于拉链组件的,是有效的 import Control.Comonad data Store s a = Store (s -> a) s instance Functor (Store s) where fmap f (Store g s) = Store (f . g) s

我已经编写了一个使用comonad的简单实现(参见下面的代码)。我的问题是,从第五次迭代开始,网格生成明显变慢。我的问题是否与我正在使用Store comonad有关?还是我犯了一个明显的错误?据我所知,基于拉链组件的,是有效的

import Control.Comonad

data Store s a = Store (s -> a) s

instance Functor (Store s) where
    fmap f (Store g s) = Store (f . g) s

instance Comonad (Store s) where
    extract (Store f a) = f a
    duplicate (Store f s) = Store (Store f) s

type Pos = (Int, Int)

seed :: Store Pos Bool
seed = Store g (0, 0)
    where
        g ( 0,  1) = True
        g ( 1,  0) = True
        g (-1, -1) = True
        g (-1,  0) = True
        g (-1,  1) = True
        g _        = False

neighbours8 :: [Pos]
neighbours8 = [(x, y) | x <- [-1..1], y <- [-1..1], (x, y) /= (0, 0)]

move :: Store Pos a -> Pos -> Store Pos a
move (Store f (x, y)) (dx, dy) = Store f (x + dx, y + dy)

count :: [Bool] -> Int
count = length . filter id

getNrAliveNeighs :: Store Pos Bool -> Int
getNrAliveNeighs s = count $ fmap (extract . move s) neighbours8

rule :: Store Pos Bool -> Bool
rule s = let n = getNrAliveNeighs s
        in case (extract s) of
            True  -> 2 <= n && n <= 3
            False -> n == 3

blockToStr :: [[Bool]] -> String
blockToStr = unlines . fmap (fmap f)
    where
        f True  = '*'
        f False = '.'

getBlock :: Int -> Store Pos a -> [[a]]
getBlock n store@(Store _ (x, y)) =
    [[extract (move store (dx, dy)) | dy <- yrange] | dx <- xrange]
    where
        yrange = [(x - n)..(y + n)]
        xrange = reverse yrange

example :: IO ()
example = putStrLn
        $ unlines
        $ take 7
        $ fmap (blockToStr . getBlock 5)
        $ iterate (extend rule) seed
导入控制.Comonad
数据存储SA=存储->a
实例函子(存储),其中
fmap f(存储g s)=存储(f.g)s
实例Comonad(商店),其中
提取(存储f a)=f a
重复(存储f s)=存储(存储f)s
类型Pos=(Int,Int)
种子:商店Pos Bool
种子=存储g(0,0)
哪里
g(0,1)=真
g(1,0)=真
g(-1,-1)=真
g(-1,0)=真
g(-1,1)=真
g=假
邻居8::[Pos]
邻居8=[(x,y)| x店a位置
移动(存储f(x,y))(dx,dy)=存储f(x+dx,y+dy)
计数::[Bool]->Int
计数=长度。筛选器id
GetnAliveneighs::商店Pos Bool->Int
getNrAliveNeighs s=count$fmap(extract.move s)neigurs8
规则::门店Pos Bool->Bool
规则s=let n=getNrAliveNeighs s
如果是
True->2字符串
blockToStr=unlines.fmap(fmap f)
哪里
f真='*'
f False='。'
getBlock::Int->Store Pos a->[[a]]
getBlock n store@(store_uux,y))=

[[extract(move store(dx,dy))| dy存储组件本身并不真正存储任何东西(除了抽象意义上的函数是一个“容器”),而是必须从头开始计算。这显然在几次迭代中变得非常低效

但是,如果您只使用以下方法备份
s->a
函数,则无需更改代码即可缓解此问题:

尚未测试这是否真的提供了可接受的性能

顺便说一句,爱德华·科米特(Edward Kmett)有一个明确的记忆版本,但现在已经不存在了。我最近(在调整依赖项之后,似乎是这样)

import Data.MemoTrie

instance HasTrie s => Functor (Store s) where
  fmap f (Store g s) = Store (memo $ f . g) s

instance HasTrie s => Comonad (Store s) where
  extract (Store f a) = f a
  duplicate (Store f s) = Store (Store f) s