如何更改基于二维列表的Haskell Store Comonad焦点?生命游戏开发求助
使用Haskell Store Comonad开发生命游戏的问题
嘿,我来帮你搞定这个Store Comonad在生命游戏里的问题,先从你的代码和核心需求入手拆解:
基础类型与初始场定义
我们先把你已有的类型和初始测试场整理出来:
type Cel = ((Float, Float), Bool) type Field2D = [[Cel]] -- 测试用的初始二维场 initialField2D :: Field2D initialField2D = [[((0.0, 0.0), True), ((0.0, 1.0), True)], [((1.0, 0.0), True), ((1.0, 1.0), True)]]
先搞懂Store Comonad的结构
Haskell的Control.Comonad.Store里的Store类型本质是带焦点的环境,它的核心定义是:
data Store s a = Store (s -> a) s
- 第一个参数
(s -> a)是「查询函数」:给定一个状态(这里就是二维坐标),返回对应的值(你的Cel) - 第二个参数
s是当前的焦点:也就是你当前关注的细胞位置
所以你要定义的initialStore2D,正确的类型应该是Store (Int, Int) Cel——用整数坐标作为状态来定位Field2D里的细胞(毕竟生命游戏的格子都是离散的整数位置,你Cel里的Float可能是用来渲染的,不影响定位逻辑)。
构建initialStore2D
首先我们需要一个从坐标获取对应Cel的函数,先写一个基础版(后续可以优化边界处理):
-- 从Field2D中根据坐标(i,j)获取Cel lookUp :: Field2D -> (Int, Int) -> Cel lookUp field (i,j) = field !! i !! j
然后用这个函数和初始焦点(比如(0,0))来构造Store:
import Control.Comonad.Store -- 记得先导入这个模块 initialStore2D :: Store (Int, Int) Cel initialStore2D = Store (lookUp initialField2D) (0, 0)
如果要处理边界(比如超出场范围的坐标自动循环,模拟 torus 地图),可以把查询函数改成安全版:
lookUpSafe :: Field2D -> (Int, Int) -> Cel lookUpSafe field (i,j) = let rows = length field cols = length (head field) -- 取模实现循环边界 i' = i `mod` rows j' = j `mod` cols in field !! i' !! j' -- 用安全查询函数构建的Store initialStore2DSafe :: Store (Int, Int) Cel initialStore2DSafe = Store (lookUpSafe initialField2D) (0, 0)
如何切换Store的焦点
Store Comonad提供了现成的函数来操作焦点,最常用的有这几个:
seek:切换焦点到指定坐标
这个函数会返回一个新的Store,焦点变成你指定的位置:-- 把焦点切换到(1,1)位置 focusOn11 :: Store (Int, Int) Cel focusOn11 = seek (1,1) initialStore2Dpeek:查看指定坐标的值,不改变当前焦点
如果只是想看看某个位置的细胞状态,不用切换焦点,可以用这个:-- 查看(1,0)位置的Cel,原Store的焦点还是(0,0) cellAt10 :: Cel cellAt10 = peek (1,0) initialStore2Dpos:获取当前Store的焦点坐标
想知道现在焦点在哪?用这个:currentPos :: (Int, Int) currentPos = pos initialStore2D -- 返回(0,0)
内容的提问来源于stack exchange,提问作者Thobro
相关产品推荐
相关产品推荐

