You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Clojure实现Conway生命游戏添加Glider到网格报错问询

Clojure实现康威生命游戏滑翔机添加问题

已实现的基础函数

使用Clojure实现Conway's Game of Life过程中,已完成可正常运行的核心基础函数如下:

(ns glider.core
  (:gen-class))

(defn new-grid
  "Creates a 2d grid with rows*cols."
  [rows cols]
  (partition cols (take (* rows cols) (repeat false))))

(defn get-coordinates
  "Returns the row/col coordinates of each 2d grid field."
  [grid]
  (let [rows (count grid)
        cols (count (get grid 0))]
    (partition cols (for [r (range rows) c (range cols)] [r c]))))

(defn set-at
  "Sets the field at row/col to state."
  [grid row col state]
  (assoc grid row (assoc (get grid row) col state)))

三个基础函数的功能:

  • new-grid:创建指定行列数的二维网格,所有单元格初始状态为false(约定true为细胞存活,false为细胞死亡)
  • get-coordinates:返回二维网格所有单元格对应的行列坐标,结果按行分组
  • set-at:接收网格、行号、列号、目标状态,返回修改指定位置状态后的新网格

问题代码与报错

编写add-glider函数,尝试将滑翔机图案添加到网格的右下象限:

(defn add-glider
  "Adds a glider pattern to the lower-right quadrant of the grid."
  [grid]
  (let [rows (count grid)
        cols (count (get grid 0))
        row-offset (/ rows 2)
        col-offset (/ cols 2)
        glider [[false true false]
                [false false true]
                [true true true]]
        glider-coords (get-coordinates glider)
        grid-coords (map (fn [[r c]] [(+ r row-offset) (+ c col-offset)]) glider-coords)]
    (apply (fn [[r c]] (set-at grid r c true)) grid-coords)))

原实现思路:

  1. 计算网格行列数,取网格中点作为行列偏移量
  2. 定义3*3的滑翔机布尔状态矩阵
  3. 调用get-coordinates生成滑翔机自身坐标,通过偏移计算得到滑翔机在目标网格上的对应坐标
  4. 将这些坐标位置的单元格状态设置为true
    配套测试代码:
(defn -main
  "Creates a grid and adds a glider to it."
  [& args]
  (let [grid (new-grid 8 8)
        grid-with-glider (add-glider grid)]
    (println grid)
    (println grid-with-glider)))

通过lein运行时抛出错误:

$ lein repl
glider.core=> (-main)
Execution error (ClassCastException) at glider.core/add-glider$fn (core.clj:32).
class clojure.lang.PersistentVector cannot be cast to class java.lang.Number (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')

报错指向第32行代码:

grid-coords (map (fn [[r c]] [(+ r row-offset) (+ c col-offset)]) glider-coords)

需要解答两个问题:

  1. 上述错误信息的具体含义是什么?
  2. 当前使用apply的实现思路是否合理,这类批量设置网格单元格的需求应如何正确实现?

问题解答

1. 错误信息含义

这个类型转换错误的核心是传入+运算符的参数类型不符合要求:+仅接受数字类型参数,但实际传入了Clojure的向量(PersistentVector)类型,无法转换为数字。
导致问题的根源是对get-coordinates返回值的结构理解错误:get-coordinates会用partition按行对坐标分组,3*3的滑翔机调用该函数返回的是嵌套结构(((0,0) (0,1) (0,2)) ((1,0) (1,1) (1,2)) ((2,0) (2,1) (2,2))),而非平铺的单个坐标对序列。后续map处理时,每次取到的元素是一整行的3个坐标组成的向量,不是单个[r c]坐标对,解构时r会拿到行内第一个坐标(本身是向量类型),传入+做运算时就触发了类型错误。
代码还隐藏了另一个问题:Clojure中/默认返回分数类型,当网格行列数为奇数时,偏移量会是分数,无法作为序列索引使用。

2. 实现思路问题与正确实现方案

原代码中apply的用法完全不合理,存在两个核心缺陷:

  • apply会把后续集合的所有元素展开作为参数传入目标函数,但原代码写的匿名函数仅接受1个坐标参数,即使坐标结构正确,也只会接收到第一个坐标,剩余参数会被直接忽略
  • set-at是纯函数,每次调用仅返回修改了单个位置的新网格,原代码每次调用set-at都基于原始未修改的grid操作,不会累积修改结果,就算参数正确最终也只会保留最后一次修改的效果
    这类批量更新不可变结构的需求,标准实现方式是使用reduce做累积迭代,修正后的完整add-glider实现如下:
(defn add-glider
  "Adds a glider pattern to the lower-right quadrant of the grid."
  [grid]
  (let [rows (count grid)
        cols (count (first grid))
        ;; 用quot做整数除法,保证偏移量是合法的整数索引
        row-offset (quot rows 2)
        col-offset (quot cols 2)
        glider [[false true false]
                [false false true]
                [true true true]]
        ;; 遍历滑翔机矩阵,取出所有需要设为存活状态的目标坐标
        glider-cells (for [[r row] (map-indexed vector glider)
                           [c state] (map-indexed vector row)
                           :when state]
                       [(+ r row-offset) (+ c col-offset)])]
    ;; 用reduce累积修改:初始值是原始网格,每次迭代更新一个坐标的状态
    (reduce (fn [g [r c]] (set-at g r c true)) grid glider-cells)))

修正点说明:

  • 用quot做整数除法计算偏移量,保证索引为合法整数
  • 不再调用get-coordinates获取嵌套的分组坐标,直接通过map-indexed遍历滑翔机矩阵,拿到每个单元格的行号、列号、存活状态,仅过滤出需要设为true的存活单元格坐标
  • 用reduce替代错误的apply实现,迭代过程中始终基于上一次修改生成的新网格做更新,保证所有修改都能累积生效

内容的提问来源于stack exchange,提问作者Patrick Bucher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 23:39:27