Skip to content

Commit

Permalink
Add an initial Clojure implementation for unit 2.
Browse files Browse the repository at this point in the history
  • Loading branch information
sjl committed Oct 12, 2011
1 parent 8938c8c commit de18782
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 0 deletions.
6 changes: 6 additions & 0 deletions unit2/clojure/unit02/.gitignore
@@ -0,0 +1,6 @@
pom.xml
*jar
/lib/
/classes/
.lein-failures
.lein-deps-sum
16 changes: 16 additions & 0 deletions unit2/clojure/unit02/README
@@ -0,0 +1,16 @@
Unit 2 Algorithms in Clojure
============================

Usage
-----

lein deps
lein repl

(require '[unit02.trees :as trees])
(trees/search-breadth trees/sample-tree :e)
(trees/search-depth trees/sample-tree :e)

(require '[unit02.graphs :as graphs])
(graphs/search-breadth graphs/sample-graph-ny :rochester :albany)
(graphs/search-depth graphs/sample-graph-ny :rochester :albany)
6 changes: 6 additions & 0 deletions unit2/clojure/unit02/project.clj
@@ -0,0 +1,6 @@
(defproject unit02 "1.0.0-SNAPSHOT"
:description "ai-class unit 2 algorithms in Clojure"
:dependencies [[org.clojure/clojure "1.2.1"]
[org.clojure/clojure-contrib "1.2.0"]
[incanter "1.2.4"]]
:dev-dependencies [[lein-marginalia "0.6.1"]])
91 changes: 91 additions & 0 deletions unit2/clojure/unit02/src/unit02/graphs.clj
@@ -0,0 +1,91 @@
(ns unit02.graphs)



; Undirected Graph ------------------------------------------------------------
;
; A graph is a map of label -> node.
; Each node has a label and a list of connections.
; Each connection is a map of label -> cost.
;
; Connections between nodes are undirected, and the cost is the same in both
; directions.

(defrecord Node [label connections])

(defn make-graph [labels connections]
(let [graph (zipmap labels (map #(Node. %1 %2) labels (cycle [{}])))]
(loop [graph graph
connections connections]
(if (empty? connections)
graph
(let [[labels cost] (first connections)
[a b] labels]
(recur (-> graph
(assoc-in [a :connections b] cost)
(assoc-in [b :connections a] cost))
(rest connections)))))))


; Searches --------------------------------------------------------------------
(defn- path-cost [path graph]
(loop [prev (first path)
path (rest path)
cost 0]
(if (empty? path)
cost
(recur (first path)
(rest path)
(+ cost ((:connections (graph prev))
(first path)))))))

(defn- new-targets [path current-node explored graph]
(let [unseen (filter (complement explored)
(keys (:connections current-node)))
new-paths (map #(conj path %) unseen)]
(map #(with-meta % {:cost (path-cost % graph)})
new-paths)))


(defn search [frontier-conj graph start goal]
(loop [explored #{}
frontier (seq [[start]])
examined 1]
(let [path (first frontier)
current (last path)]
(if (= current goal)
{:path path
:examined examined
:cost (:cost (meta path))}
(when current
(recur (conj explored current)
(let [current-node (graph current)]
(frontier-conj (rest frontier)
(new-targets path current-node explored graph)))
(inc examined)))))))


(defn frontier-conj-breadth [frontier children]
(concat frontier children))

(defn frontier-conj-depth [frontier children]
(concat children frontier))


(def search-breadth (partial search frontier-conj-breadth))
(def search-depth (partial search frontier-conj-depth))

; Sample Graph ----------------------------------------------------------------
(def sample-graph-ny
(make-graph [:buffalo :rochester :syracuse :binghamton :new-york-city]
{[:buffalo :rochester] 75
[:syracuse :rochester] 90
[:ithaca :rochester] 90
[:ithaca :binghamton] 50
[:ithaca :syracuse] 60
[:binghamton :syracuse] 75
[:binghamton :rochester] 141
[:binghamton :new-york-city] 176
[:syracuse :new-york-city] 247
[:albany :new-york-city] 150
[:albany :syracuse] 150}))
47 changes: 47 additions & 0 deletions unit2/clojure/unit02/src/unit02/trees.clj
@@ -0,0 +1,47 @@
(ns unit02.trees)


; Tree ------------------------------------------------------------------------
;
; A tree is equivalent to a single node. A single node is a record:
;
; {:label :foo :children [... nodes ...]}

(defrecord Node [label children])

; Searches --------------------------------------------------------------------
(defn- search [frontier-conj tree goal]
; frontier is a sequence of vectors representing paths we still need to try:
; ([Node :a, Node :b], [Node :a, Node :c], ...)
(loop [frontier (seq [[tree]])
examined 1]
(let [path (first frontier)
current (last path)]
(if (= (:label current) goal)
{:path (map :label path)
:examined examined}
(recur (frontier-conj (rest frontier)
(map #(conj path %) (:children current)))
(inc examined))))))


(defn- frontier-conj-depth [frontier children]
(concat children frontier))

(defn- frontier-conj-breadth [frontier children]
(concat frontier children))


(def search-depth (partial search frontier-conj-depth))
(def search-breadth (partial search frontier-conj-breadth))

; Sample Tree -----------------------------------------------------------------
;
; a
; b c
; d e f g

(def sample-tree (Node. :a [(Node. :b [(Node. :d [])
(Node. :e [])])
(Node. :c [(Node. :f [])
(Node. :g [])])]))

0 comments on commit de18782

Please sign in to comment.