I'm learning Clojure and started by copying the functionality of a Python program that would create genomic sequences by following an (extremely simple) Hidden Markov model.
In the beginning I stuck with my known way of serial programming and used the def keyword a lot, thus solving the problem with tons of side effects, kicking almost every concept of Clojure right in the butt. (although it worked as supposed)
Then I tried to convert it to a more functional way, using loop, recur, atom and so on. Now when I run I get an ArityException, but I can't read the error message in a way that shows me even which function throws it.
(defn create-model [name pA pC pG pT pSwitch]
; converts propabilities to cumulative prop's and returns a char
(with-meta
(fn []
(let [x (rand)]
(if (<= x pA)
\A
(if (<= x (+ pA pC))
\C
(if (<= x (+ pA pC pG))
\G
\T))))) ; the function object
{:p-switch pSwitch :name name})) ; the metadata, used to change model
(defn create-genome [n]
; adds random chars from a model to a string and switches models randomly
(let [models [(create-model "std" 0.25 0.25 0.25 0.25 0.02) ; standard model, equal props
(create-model "cpg" 0.1 0.4 0.4 0.1 0.04)] ; cpg model
islands (atom 0) ; island counter
m (atom 0)] ; model index
(loop [result (str)]
(let [model (nth models @m)]
(when (< (rand) (:p-switch (meta model))) ; random says "switch model!"
; (swap! m #(mod (inc @m) 2)) ; swap model used in next iteration
(swap! m #(mod (inc %) 2)) ; EDIT: correction
(if (= @m 1) ; on switch to model 1, increase island counter
; (swap! islands #(inc @islands)))) ; EDIT: my try, with error
(swap! islands inc)))) ; EDIT: correction
(if (< (count result) n) ; repeat until result reaches length n
(recur (format "%s%c" result (model)))
result)))))
Running it works, but calling (create-genome 1000) leads to
ArityException Wrong number of args (1) passed to: user/create-genome/fn--772 clojure.lang.AFn.throwArity (AFn.java:429)
My questions:
- (obviously) what am I doing wrong?
- how exactly do I have to understand the error message?
Information I'd be glad to receive
- how can the code be improved (in a way a clojure-newb can understand)? Also different paradigms - I'm grateful for suggestions.
- Why do I need to put pound-signs # before the forms I use in changing the atoms' states? I saw this in an example, the function wouldn't evaluate without it, but I don't understand :)
Ok, it's a long shot, but it looks like your atom-updating functions:
and
are of 0-arity, and they should be of arity at least 1.
This leads to the answer to your last question: the
#(body)
form is a shortcut for(fn [...] (body))
. So it creates an anonymous function. Now the trick is that ifbody
contains%
or%x
where x is a number, the position where it appears will be substituted for the referece to the created function's argument numberx
(or the first argument if it's only%
).In your case that
body
doesn't contain references to the function arguments, so#()
creates an anonymous function that takes no arguments, which is not whatswap!
expects.So swap tries to pass an argument to something that doesn't expect it and boom!, you get an ArityException.
What you really needed in those cases was:
and
respectively