如何在Haskell项目中组织大量的状态
我正在写我的第一个真正的Haskell项目,我在计划中组织状态时遇到了困难。这是一个Gameboy颜色模拟器,所以有很多小标志,整个州看起来像如何在Haskell项目中组织大量的状态,haskell,emulation,Haskell,Emulation,我正在写我的第一个真正的Haskell项目,我在计划中组织状态时遇到了困难。这是一个Gameboy颜色模拟器,所以有很多小标志,整个州看起来像 data Memory s = Memory { memory :: STUArray s Word16 Word8 , registers :: STUArray s Word8 Word8 , sp :: STRef s Word16
data Memory s = Memory { memory :: STUArray s Word16 Word8
, registers :: STUArray s Word8 Word8
, sp :: STRef s Word16
, pc :: STRef s Word16
, cycles :: STRef s Word16
, ime :: STRef s Bool --Interrupt Master Enable Flag
, halt :: STRef s Bool --Are we halted or not
, mode :: STRef s GPUMode -- GPU mode
, line :: STRef s Word8 -- GPU line
, transferred :: STRef s Bool
, gpuCycles :: STRef s Word16
, window :: Window
, renderer :: Renderer
}
我对状态进行读/写操作,如:
data Address = OneRegister Register
| TwoRegister {registerA :: Register, registerB :: Register}
| MemAddr Word16
| SP
| PC
| CYCLES
| IME
| HALT_STATE
| GPU_MODE
| GPU_LINE
| GPU_TRANSFERRED_LINE
| GPU_CYCLES
data MemVal = MemVal8 Word8
| MemVal16 Word16
| Flag Bool
| Mode GPUMode
read :: Memory s -> Address -> ST s MemVal
write :: Memory s -> Address -> MemVal -> ST s ()
你可以看到:
我有没有更干净的方法来组织一切?如果可能的话,我想在各个组件(CPU、GPU、墨盒组切换等)之间划分状态。在Haskell中有一个大的整体状态类型是惯用的吗
在程序中添加新的状态是一件非常痛苦的事情。镜头的控制包似乎是对的,但我不确定我是否可以很容易地将它与ST结合起来
谢谢 镜头对这类东西肯定有很大的帮助,但您更愿意将它们用于
状态中的大型嵌套纯状态对象,而不是ST
。我认为这对于所有这些变量来说都是可以的,尽管对于数组来说可能是不可接受的(每次修改都需要深度复制)
所以我可以想出两个选择:
- 从阵列切换到具有高效纯功能更新的数据结构,例如。完全抛弃那些
STRefs
,支持状态下基于镜头的更新
这将比ST
中的破坏性阵列更新更有效,但在快速的现代计算机上模拟游戏机可能就行了。
- 拆分内存类型,以便将数组保持在
ST
,但将所有其他状态分组到一个STRef
纯数据结构中。在此基础上,您可以使用镜头
data Memory s = Memory { memory :: STUArray s Word16 Word8
, registers :: STUArray s Word8 Word8
, memRefs :: STRef s MemRefs
, window :: Window
, renderer :: Renderer
}
data MemRefs = MemRefs { _sp :: Word16
, _pc :: Word16
, _cycles :: Word16
, _ime :: Bool --Interrupt Master Enable Flag
, _halt :: Bool --Are we halted or not
, _mode :: GPUMode -- GPU mode
, _line :: Word8 -- GPU line
, _transferred :: Bool
, _gpuCycles :: Word16
}
mkLenses ''MemRefs
好的是,你现在可以将MemRef
类型al-gusto分组,并使用镜头方便地深入到结构中。通过使结构更像树,更新实际上会更有效。(您可能还想取消那些Word16
和Bool
字段的装箱,将如此小的类型装箱实在是浪费。)
尽管如此,您应该准备好,这将不像在C++中实现类似复杂的实现一样快。为了实现可比的性能,您可能需要将泰勒所有的状态交给使用一个代码“>代码SARLY < /C>”,在该代码中所有的状态信息都被编码,并在代码< S> < /COD>中写入丑陋的OO风格的吸气剂和设置器。一开始,我想“哈,那会有多大?”。但是“我正在写我的第一个真正的Haskell项目,[…]一个Gameboy颜色模拟器”。雄心勃勃的第一步。从效率的角度来看,
STRef
目前对这类事情很差劲,而StateT BigRecord(ST s)
很痛苦,但速度很快。谢谢!接下来——为什么装箱小类型会浪费时间?据我所知,仅更新_ime字段(例如)与每次更新任何字段时更新一个新的MemRefs之间的区别。这在内存方面是最浪费的(使用64位指针指向一个单独的16位数据条目的地址),不利于缓存一致性。幸运的是,在更新另一个记录字段时,您不需要遵循这些指针,但仍然有相当多的垃圾漂浮在周围。但是,我不确定它对您的应用程序的影响到底有多大。