序列化匿名函数

时间:2016-10-28 12:50:50

标签: clojure

我正在写一个遗传算法。为了帮助在运行时使用设置,我试图将设置(突变率,交叉机会,适应度函数......)保存到磁盘。这将允许我修改文件,然后偶尔读取文件以在运行时更新设置。从控制台获取输入会更容易,但Intellij输入输入的能力非常糟糕,并且证明使用起来太难了,所以这就是我正在尝试的路线。

我的问题是:设置由记录表示。许多单独的设置都是数字,因此很容易保存到磁盘。但是有些设置是功能,不能很好地保存:

(defrecord Settings [
                 gene-set
                 sequence-length
                 fitness-f])

(with-out-str (pr
              (->Settings #{1 2 3}
                          10
                          #(count %)))))

收率:

"#ai.genetic_algorithm.genetic_algorithm2_2.Settings{:gene-set #{1 3 2}, :sequence-length 10, :fitness-f #object[ai.genetic_algorithm.genetic_algorithm2_2$eval776$fn__777$fn__778 0x1b30c92 \"ai.genetic_algorithm.genetic_algorithm2_2$eval776$fn__777$fn__778@1b30c92\"]}"

在大多数情况下,除了表示适应度函数的方式外,这很好。如果我尝试重新阅读该文件:

(read-string
  (with-out-str (pr
                  (->Settings #{1 2 3}
                              10
                              #(count %)))))

我明白了:

ai.genetic-algorithm.genetic-algorithm2-2=> RuntimeException No reader function for tag object  clojure.lang.LispReader$CtorReader.readTagged (LispReader.java:1245)

我在尝试保存/读取函数时只收到此错误。我猜它无法知道我要求它读取函数,所以它是barfs。

有没有办法可靠地将匿名函数保存到文件?

是的,我知道read-string有潜在危险。我只是将它用于个人项目,所以我并不担心这个漏洞。

1 个答案:

答案 0 :(得分:0)

如果更改代码而不是生成函数,然后从命名空间中提取该函数以将其保存到文件,则需要生成设置的表达式并将表达式保存到文件中。然后使用加载该文件的结果作为新状态。

user> (let [new-config '{:gene-set #{1 2 3}
                        :sequence-length 10
                        :fitness-f (fn [x] (count x))}]
           (with-open [w (clojure.java.io/writer "/tmp/config.edn")]
             (binding [*out* w]
               (pr new-config)))     
           (load-file "/tmp/config.edn"))

{:gene-set #{1 3 2}, 
 :sequence-length 10, 
 :fitness-f #function[user/eval111126/fn--111127]}

当您定义要保存的新适应度函数时,将其生成为符号序列,并在将序列编译为函数类之前保存该序列。让它编译的唯一时间是它被加载,所以你知道它将永远是可加载的东西。这将抓住错误。

如果您发现自己想要机械地生成这些组合,您可能需要考虑给它们命名,只保存配置中的组合顺序。