(def x (some-data-structure 1 2 3)) (def x2 (binding [*print-dup* true] (prn-str x))) ;; now x2 is a string serialization of x ;; You can save x2 to disk or whatever ;; Then to read it back in: (def x-clone (read-string x2))
(Note: you really shouldn't be programming in Clojure with a bunch of
def
'ed vars like that...)Unfortunately, Clojure records does not support this functionality quite just yet. There is defrecord2 that implements this serialization functionality for records though, described in this discussion in the Clojure group.
I had a second problem though, since I had records storing Incanter matrices, which are Parallel Colt matrices, and they don't
print-dup
in a way that can be read back in with read
(or read-string
). So to solve both problems at the same time, we can just implement our own print-dup
for the record we create with defrecord
(print-dup
is a multmethod).So as an example, let's say I have:
(ns silly.core (:use incanter.core)) (defrecord rec-mat [m])
m
will be an Incanter matrix. We'd write something like this:
(defmethod print-dup silly.core.rec-mat [obj wrtr] (.write wrtr "#=(silly.core/new-rec-mat #=(clojure.lang.PersistentArrayMap/create ") (print-dup (update-in [:m] #(matrix-to-list-frm %)) wrtr) (.write wrtr "))"))
What that does is create a string that can be read back in by
read
or read-string
, and which will call your silly.core/new-rec-mat
function with a single argument (a map with key :m
, that has as value the Incanter matrix in a list form — built by the matrix-to-list-frm
function)That means we still have two more functions to write to complete this exercise. First, the
matrix-to-list-frm
:
(defn matrix-to-list-frm [mat] (if (== 1 (first (dim mat))) (list (to-list mat)) (to-list mat)))
to-list
is a function from Incanter, but it doesn't handle matrices with only a single row properly — single row and single column matrices gets transformed into the same list form, so when read back in, it's hard to tell if it was originally a row or column vector, which may be an important distinction in your program — and that is why we have to wrap it up with our own matrix-to-list-frm
function.Secondly, we write the
new-rec-mat
function:
(defn new-rec-mat [{:keys [:m]}] (rec-mat. (matrix m)))
And that's it! Using the same kind of thing as above, you can make records contain Clojure vectors of Incanter matrices, and still be able to
print-dup
it out.If you just need to save a single Incanter matrix, you should probably just use the
incanter.core/save
function though.Edit: Argh! I forgot to write about how this doesn't work if you
assoc
into the record some key/val that's not pre-defined as part of that record. There's a way around it, of course, but I haven't had time to write it down here...one of these days though...
No comments:
Post a Comment