【问题标题】:clojure.lang.LazySeq cannot be cast to class clojure.lang.Associativeclojure.lang.LazySeq 不能转换为类 clojure.lang.Associative
【发布时间】:2022-01-05 13:16:48
【问题描述】:

我是 Clojure 的新手,我尝试实现一种遗传算法。因此,我遇到了一个问题,实现不断抛出以下错误:

class clojure.lang.LazySeq cannot be cast to class clojure.lang.Associative (clojure.lang.LazySeq and clojure.lang.Associative are in unnamed module 
of loader 'app')

需要提到的是,每个函数都在 REPL 中单独测试并返回正确的结果,但是将它们放在一起后,就会抛出错误,我不明白,因为它似乎没有指定行号。

clojure 的版本是来自 master 分支并在 Windows 上使用 maven 构建的版本。

完整代码:

(ns ga)

(defn new-individual
    [genome-length]
    {:genome (vec (repeatedly genome-length #(rand-int 2))) :fitness 0}
    )


(defn fitness-function
    [genome, target]
    (Math/abs (- (reduce + genome) target))
)




(defn calculate-fitness
    [population, target]
    (defn fitness-function-helper 
        [individual, target]
        (assoc individual :fitness (fitness-function (individual :genome) target))
    )
        (map (fn [individual] (#(fitness-function-helper individual target))) population)
)



(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)
                )]
            {:genome new-genome :fitness (fitness-function new-genome target)}
        )
        
)
        


(defn mutate
  [individual, genome-length, target]
  (let [new-genome (assoc (individual :genome) (rand-int genome-length) (rand-int 2))]
    {:genome new-genome :fitness (fitness-function new-genome target)}
  )
)



(defn better
  [i1 i2]
  (< (i1 :fitness) (i2 :fitness)))

(defn tournament-selection
  [population, population-size, steps, tournament-size, new-population, target]
  (if (< steps tournament-size)
    (recur population population-size (inc steps) tournament-size (conj new-population (nth population ((comp rand-int -) population-size 2))) target
      
     )
        ;(println new-population)
        (first (sort better (calculate-fitness new-population target)))
        
    )
)

(defn new-generation [population, population-size, crossover-rate, target, tournament-size]
    (loop [steps 0 new-population ()]
        (if (< steps population-size)
        (let [i1 (tournament-selection population population-size 0 tournament-size () target)]
            (let [i2 (tournament-selection population population-size 0 tournament-size () target)]
                (let [offs (crossover i1 i2 crossover-rate target)]
                    (recur (inc steps) (conj new-population offs))
                )
            )
        )
        new-population
    )
        )
    )
    
    (defn new-mutated-generation [population, population-size, genome-length, target]
        (loop [steps 0 new-population ()]
            (if (< steps population-size)
                    (recur (inc steps) (conj new-population (mutate (nth population steps) genome-length target)))
                    new-population
            )

        )

    )
        

(defn evolve [population-size, genome-length, target]
(let [population (calculate-fitness (repeatedly population-size #(new-individual genome-length)) target)]
    (let [offsprings (new-generation population population-size 0.5 target 5)]
        (println (new-mutated-generation offsprings population-size genome-length target))
        )
    


)

)

(evolve 10 5 5)

【问题讨论】:

  • 要获得异常,只需在您的 REPL 中评估 *e。另外,不要嵌套 defn 和/或 def 语句。如果需要在函数内创建函数,请使用let+fnletfn
  • @EugenePakhomov 感谢您的建议。我会记住的!

标签: clojure


【解决方案1】:

函数mutate 发生错误。它有这个来源:

(defn mutate
  [individual, genome-length, target]
  (let [new-genome (assoc (individual :genome) (rand-int genome-length) (rand-int 2))]
    {:genome new-genome :fitness (fitness-function new-genome target)}))

在一个步骤中,您使用以下参数调用它:{:genome (0 1 1 1 1), :fitness 1} 5 5(基因组可以有不同的值,但它始终是数字序列)。

(individual :genome) 返回(0 1 1 1 1)(序列),然后您使用assoc,这是哈希映射或向量的函数。

基因组一开始是向量,但在crossover函数中转换为序列-这里使用mapv而不是map

(defn crossover
  [first-individual, second-individual, crossover-rate, target]
  (let [new-genome (mapv (fn [i1, i2] (let [crossover-probability (rand)]
                                        (if (<= crossover-probability crossover-rate) i1 i2)))
                         (first-individual :genome) (second-individual :genome))]
    {:genome new-genome :fitness (fitness-function new-genome target)}))

顺便说一句,定义末尾的所有括号都属于同一行。

【讨论】:

  • 非常感谢您的回答和解释。
【解决方案2】:

堆栈跟踪显示有问题的代码是行

(let [new-genome (assoc (vec (individual :genome)) (rand-int genome-length) (rand-int 2))]

更具体地说,是对assoc 的调用。如果我们通过在上面插入以下行来编辑代码:

(println "Individual: " (individual :genome) ", " (class (individual :genome)))

打印出来

Individual:  (0 1 1 0 1) ,  clojure.lang.LazySeq

问题在于 assoc 不能与惰性序列 (clojure.lang.LazySeq) 一起使用,因为它没有实现 assoc 所需的 clojure.lang.Associative 接口。

这个惰性序列是通过在这一行调用map 构造的:

(let [new-genome (map (fn [i1,i2] (let [crossover-probability (rand)]

如果您将map 替换为mapv,则代码如下所示

(let [new-genome (mapv (fn [i1,i2] (let [crossover-probability (rand)]

代码将起作用。

【讨论】:

  • 非常感谢您的回答和解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多