Skip to content

Instantly share code, notes, and snippets.

@ccidral
Last active January 5, 2023 01:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ccidral/4816a84eaa0c8d758e8c3cd93790e9b6 to your computer and use it in GitHub Desktop.
Save ccidral/4816a84eaa0c8d758e8c3cd93790e9b6 to your computer and use it in GitHub Desktop.
DFS in Clojure: persistent vs transient
(ns dfs
(:require [clojure.java.io :as io]
[clojure.string :as str]))
;; the dfs fns below are adapted from https://gist.github.com/xfthhxk/3810ecfaae8f066e2162ab02108ccadb
(defn load-graph [filepath]
(with-open [rdr (io/reader filepath)]
(let [[_vertices _edges & lines] (line-seq rdr)]
(reduce (fn [g line]
(let [[v w] (str/split line #" ")
v (Integer/parseInt v)
w (Integer/parseInt w)]
(-> g
(update v #(conj (or % []) w))
(update w #(conj (or % []) v)))))
{}
lines))))
;; wget https://algs4.cs.princeton.edu/41graph/largeG.txt
(defonce large-g
(load-graph "/path/to/largeG.txt"))
(defn graph-search
"dfs graph search using persistent collections."
[g start]
(loop [next [start]
visited #{}]
(cond
(empty? next)
visited
(visited (peek next))
(recur (pop next) visited)
:else
(let [v (peek next)
neighbors (g v)
next (into (pop next) neighbors)
visited (conj visited v)]
(recur next visited)))))
(comment
(count (time (graph-search large-g 0))))
(defn peek! [tv]
(get tv (dec (count tv))))
(defn into! [to from]
(reduce #(conj! %1 %2) to from))
(defn graph-search-with-transients
"dfs graph search using transient collections."
[g start]
(persistent!
(loop [next (transient [start])
visited (transient #{})]
(cond
(zero? (count next))
visited
(get visited (peek! next))
(recur (pop! next) visited)
:else
(let [v (peek! next)
next (into! (pop! next) (g v))
visited (conj! visited v)]
(recur next visited))))))
(comment
(count (time (graph-search-with-transients large-g 0))))
(defn run-with-persistent
[& _]
(dotimes [_ 5]
(time (graph-search large-g 0))))
;; $ clojure -X dfs/run-with-transient
;; "Elapsed time: 5545.410697 msecs"
;; "Elapsed time: 5425.255928 msecs"
;; "Elapsed time: 5459.086837 msecs"
;; "Elapsed time: 5519.741187 msecs"
;; "Elapsed time: 5575.652866 msecs"
(defn run-with-transient
[& _]
(dotimes [_ 5]
(time (graph-search-with-transients large-g 0))))
;; $ clojure -X dfs/run-with-persistent
;; "Elapsed time: 4594.230125 msecs"
;; "Elapsed time: 4434.272369 msecs"
;; "Elapsed time: 4497.725457 msecs"
;; "Elapsed time: 4336.609415 msecs"
;; "Elapsed time: 4282.192125 msecs"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment