使用 Comonads 和模板来解决《Advent of Code》中的“座位系统”

这篇文章是关于用 Haskell 解决 Advent of Code 2020 的“Seating System”挑战,使用了幺半群(comonads)和模板(stencils)。主要内容如下:

  • 挑战描述:座位布局在网格上,每个位置可以是地板(.)、空座位(L)或 occupied 座位(#)。根据相邻座位的 occupied 数量决定座位状态变化,规则为:空座位且相邻无 occupied 座位则变为 occupied;occupied 座位且相邻 4 个或以上 occupied 座位则变为空座位;否则座位状态不变。这是一个经典的细胞自动机问题,需要编写程序模拟座位状态变化直至稳定。
  • 细胞自动机相关

    • 导入了GHC2021等扩展及多个库,用于后续代码编写。
    • 定义了Cell新类型,用模式同义词表示空座位、occupied 座位和地板,parseCell函数用于解析字符为Cellrule函数实现细胞自动机规则。
  • 解决方案

    • 定义Grid类型类,包含从列表创建网格、执行一步模拟和将网格转换回列表的函数,solve函数通过这些函数计算最终 occupied 座位数量。
    • 根据命令行参数选择三种不同方式解决挑战:使用列表的拉链(zipper)、幺半群(comonad)和模板(stencil)。
  • 拉链(Zipper)

    • 定义Zipper类型,用于模拟细胞自动机网格中的每个细胞,通过一系列函数实现位置、长度转换、列表与拉链转换、漂亮打印及左右移动焦点等功能。
    • 定义ZGrid新类型,是嵌套的拉链,类似Zipper有相关函数用于获取焦点、位置、大小、转换及漂亮打印等。还定义了移动焦点的函数zgUpzgDownzgLeftzgRight
  • 幺半群(Comonad)

    • 介绍幺半群是单子(Monad)的对偶,为模拟细胞自动机提供了合适的抽象接口,定义了Comonad类型类及相关函数extractduplicateextend
    • ZipperZGrid实现Comonad实例,extract返回焦点元素,duplicate返回包含所有可能焦点的网格,extend自动处理新网格的生成。
    • 实现zGridNeighbours函数获取网格中当前焦点的邻居细胞,stepZGrid函数执行一步细胞自动机模拟。
  • 数组(Array)

    • 考虑到列表在 Haskell 中操作较慢,提出将网格建模为 2D 数组和数组索引的组合,使用massiv库的数组。定义AGrid类型,包含数组和焦点索引。
    • AGrid实现Comonad实例,extract直接从数组中获取焦点元素,extend通过映射索引实现。还定义了listsToAGridaGridNeighboursstepAGrid函数用于创建AGrid、获取邻居和执行一步模拟。
  • 模板(Stencil)

    • massiv库的Stencil可用于模拟细胞自动机,定义SGrid新类型为 2D 无框数组。
    • 定义ruleStencil模板和stepSGrid函数,通过模板映射执行一步模拟,可使用多线程加速。
  • 奖励轮:模拟可视化:提供了细胞自动机模拟的可视化效果。

文章最后提供了完整代码的链接,并欢迎提问和分享。

阅读 43
0 条评论