Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Haskell项目中组织大量的状态_Haskell_Emulation - Fatal编程技术网

如何在Haskell项目中组织大量的状态

如何在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

我正在写我的第一个真正的Haskell项目,我在计划中组织状态时遇到了困难。这是一个Gameboy颜色模拟器,所以有很多小标志,整个州看起来像

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位数据条目的地址),不利于缓存一致性。幸运的是,在更新另一个记录字段时,您不需要遵循这些指针,但仍然有相当多的垃圾漂浮在周围。但是,我不确定它对您的应用程序的影响到底有多大。