Clojure实现遗传算法报错:LazySeq无法转换为Associative类
问题原因
- 错误本质是
LazySeq(惰性序列)没有实现Associative接口,无法执行assoc操作,触发类型转换异常。 - 根因出在
crossover函数:用map拼接两个亲本的基因组得到的new-genome是惰性序列,没有转为向量就直接存入个体的:genome字段。后续mutate函数读取该字段调用assoc修改基因组时就会触发报错。 - 单独测试函数未触发错误的原因:单独测试
mutate时使用的是new-individual生成的向量类型基因组,单独测试crossover时不会触发后续的assoc修改操作,只有全流程运行到突变步骤时,才会触发惰性序列的求值和后续的assoc调用,所以报错没有对应行号,难以定位。 - 额外隐患:
calculate-fitness函数返回的是map生成的惰性序列,多次遍历会重复计算适应度,也可能带来非预期的惰性求值问题。
修复方案
- 修复
crossover函数,将生成的新基因组转为向量:
(defn crossover [first-individual, second-individual, crossover-rate, target] (let [new-genome (map (fn [i1,i2] (let [crossover-probability (rand)] (cond (<= crossover-probability crossover-rate) i1 :else i2))) (first-individual :genome) (second-individual :genome))] ;; 新增vec调用将惰性序列转为向量 {:genome (vec new-genome) :fitness (fitness-function new-genome target)}))
- 优化
calculate-fitness函数,去掉多余的匿名函数嵌套,返回向量类型的种群,避免惰性求值问题:
(defn calculate-fitness [population, target] (defn fitness-function-helper [individual, target] (assoc individual :fitness (fitness-function (individual :genome) target))) ;; 简化map调用,并用vec转为非惰性序列 (vec (map #(fitness-function-helper % target) population)))
- 可选优化:
new-generation、new-mutated-generation返回的种群也可以用vec转为向量,提升后续nth、conj操作的效率。
完成以上修改后即可正常运行。
内容的提问来源于stack exchange,提问作者23ars
相关产品推荐
相关产品推荐

