I'm new to Clojure and trying to learn by implementing some algorithms in it. The algorithm I'm writing is for calculating the node betweenness centrality
metric for a graph data structure.
The function in the algorithm (Brandes algorithm) I'm trying to implement goes like this:
Here, V
are the vertex of the graph and s
is the starting node from which we are trying to calculate and return the shortest path metrics S, Pred and sigma
This is what I have managed to come up by using loom to create the initial graph g
for each starting node start
:
(defn ss-shortest-path
[g start]
(let [nodeset (disj (nodes g) start)
pred (apply assoc {} (interleave (nodes g) (repeat nil)))
dist (apply assoc {start 0} (interleave nodeset (repeat -1)))
sigma (apply assoc {start 1} (interleave nodeset (repeat 0)))
stack []]
(loop [queue (conj clojure.lang.PersistentQueue/EMPTY start)]
(if (empty? queue)
{:sigma sigma
:pred pred
:stack stack}
(let [v (peek queue)
stack (conj stack v)]
(doseq [w (successors g v)]
(when (= (dist w) -1)
(do
(conj queue w)
(assoc dist w (+ 1 (dist v)))))
(when (= (dist w) (+ 1 (dist v)))
(do
(assoc sigma w (+ (sigma w) (sigma v)))
(assoc pred w v))))
(recur (pop queue)))))))
I know that Clojure data structures are immutable so each time I call conj
or assoc
in the variables pred, sigma, stack, dist
a new copy is created and the original variables remain as they are.
But, I don't want to use mutable states like atoms
, refs
, as I have a feeling that'd be simply copying the imperative style that I already know.
So, I'm seeking the help of some experienced Clojurists to help me create this function in an idiomatic style.
Thanks in advance.