Clojure - przykłady kodu
1. Tekstowy 'Hello World'
(print "Hello World")
2. GUI 'Hello World'
(. javax.swing.JOptionPane (showMessageDialog nil "Hello World"))

3. Silnia
(defn factorial [n] (apply * (range 1 (inc n))))
4. GUI konwerter Celsiusz <-> Fahrenheit
Aplikacja okienkowa Swinga - konwersja stopni Celsiusza na Fahrenheita:
(import '(javax.swing JFrame JLabel JTextField JButton)
'(java.awt.event ActionListener)
'(java.awt GridLayout))
(defn celsius []
(let [frame (JFrame. "Celsius Converter")
temp-text (JTextField.)
celsius-label (JLabel. "Celsius")
convert-button (JButton. "Convert")
fahrenheit-label (JLabel. "Fahrenheit")]
(.addActionListener convert-button
(proxy [ActionListener] []
(actionPerformed [evt]
(let [c (Double/parseDouble (.getText temp-text))]
(.setText fahrenheit-label
(str (+ 32 (* 1.8 c)) " Fahrenheit"))))))
(doto frame
(.setLayout (GridLayout. 2 2 3 3)) (.add temp-text)
(.add celsius-label)
(.add convert-button)
(.add fahrenheit-label)
(.setSize 300 80)
(.setVisible true))))
(celsius)

5. Symulacja kolonii mrówek (ants.clj)
Napisana przez Richa Hickeya aby zaprezentować wsparcie dla współbieżności w Clojurze. Każda mrówka to niezależny (działający równolegle) agent. Wszystkie mrówki w bezpieczny sposób wchodzą w interakcję ze środowiskiem (planszą).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ant sim ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
; which can be found in the file CPL.TXT at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;dimensions of square world
(def dim 80)
;number of ants = nants-sqrt^2
(def nants-sqrt 7)
;number of places with food
(def food-places 35)
;range of amount of food at a place
(def food-range 100)
;scale factor for pheromone drawing
(def pher-scale 20.0)
;scale factor for food drawing
(def food-scale 30.0)
;evaporation rate
(def evap-rate 0.7)
(def animation-sleep-ms 200)
(def ant-sleep-ms 40)
(def evap-sleep-ms 1000)
(def running true)
(defstruct cell :food :pheromons) ;may also have :ant and :home
;world is a 2d vector of refs to cells
(def world
(apply vector
(map (fn [_]
(apply vector (map (fn [_] (ref (struct cell 0 0)))
(range dim))))
(range dim))))
(defn get-cell [[x y]]
(-> world (nth x) (nth y))) ; (nth (nth world x) y))
(defstruct ant :direction) ;may also have :food
(defn create-ant
"create an ant at the location, returning an ant agent on the location"
[loc dir]
(dosync
(let [cell (get-cell loc)
a (struct ant dir)]
(alter cell assoc :ant a) ; c.ant = a
(agent loc))))
(def home-off (/ dim 4))
(def home-range (range home-off (+ nants-sqrt home-off)))
(defn setup
"places initial food and ants, returns seq of ant agents"
[]
(dosync
(dotimes [i food-places]
(let [cell (get-cell [(rand-int dim) (rand-int dim)])]
(alter cell assoc :food (rand-int food-range))))
(doall
(for [x home-range y home-range]
(do
(alter (get-cell [x y])
assoc :home true)
(create-ant [x y] (rand-int 8)))))))
(defn bound
"returns n wrapped into range 0-b"
[b n]
(let [n (rem n b)]
(if (neg? n)
(+ n b)
n)))
(defn wrand
"given a vector of slice sizes, returns the index of a slice given a
random spin of a roulette wheel with compartments proportional to
slices."
[slices]
(let [total (reduce + slices)
r (rand total)]
(loop [i 0 sum 0]
(if (< r (+ (slices i) sum))
i
(recur (inc i) (+ (slices i) sum))))))
;dirs are 0-7, starting at north and going clockwise
;these are the deltas in order to move one step in given dir
(def dir-delta {0 [0 -1]
1 [1 -1]
2 [1 0]
3 [1 1]
4 [0 1]
5 [-1 1]
6 [-1 0]
7 [-1 -1]})
(defn delta-loc
"returns the location one step in the given dir. Note the world is a torus"
[[x y] dir]
(let [[dx dy] (dir-delta (bound 8 dir))]
[(bound dim (+ x dx)) (bound dim (+ y dy))]))
;(defmacro dosync [& body]
; `(dosync ~@body))
;ant agent functions
;an ant agent tracks the location of an ant, and controls the behavior of
;the ant at that location
(defn turn
"turns the ant at the location by the given amount"
[loc amt]
(dosync
(let [cell (get-cell loc)
ant (:ant @cell)]
(alter cell assoc :ant (assoc ant :direction (bound 8 (+ (:direction ant) amt))))))
loc)
(defn move
"moves the ant in the direction it is heading. Must be called in a
transaction that has verified the way is clear"
[loc]
(let [old-cell (get-cell loc)
ant (:ant @old-cell)
newloc (delta-loc loc (:direction ant))
p (get-cell newloc)]
;move the ant
(alter p assoc :ant ant)
(alter old-cell dissoc :ant)
;leave pheromone trail
(when-not (:home @old-cell)
(alter old-cell assoc :pheromons (inc (:pheromons @old-cell))))
newloc))
(defn take-food [loc]
"Takes one food from current location. Must be called in a
transaction that has verified there is food available"
(let [cell (get-cell loc)
ant (:ant @cell)]
(alter cell assoc
:food (dec (:food @cell))
:ant (assoc ant :food true))
loc))
(defn drop-food [loc]
"Drops food at current location. Must be called in a
transaction that has verified the ant has food"
(let [cell (get-cell loc)
ant (:ant @cell)]
(alter cell assoc
:food (inc (:food @cell))
:ant (dissoc ant :food))
loc))
(defn rank-by
"returns a map of xs to their 1-based rank when sorted by keyfn"
[keyfn xs]
(let [sorted (sort-by (comp float keyfn) xs)]
(reduce (fn [ret i] (assoc ret (nth sorted i) (inc i)))
{} (range (count sorted)))))
(defn behave
"the main function for the ant agent"
[loc]
(let [p (get-cell loc)
ant (:ant @p)
ahead (get-cell (delta-loc loc (:direction ant)))
ahead-left (get-cell (delta-loc loc (dec (:direction ant))))
ahead-right (get-cell (delta-loc loc (inc (:direction ant))))
places [ahead ahead-left ahead-right]]
(. Thread (sleep ant-sleep-ms))
(dosync
(when running
(send-off *agent* #'behave))
(if (:food ant)
;going home
(cond
(:home @p)
(-> loc drop-food (turn 4))
(and (:home @ahead) (not (:ant @ahead)))
(move loc)
:else
(let [ranks (merge-with +
(rank-by (comp #(if (:home %) 1 0) deref) places)
(rank-by (comp :pheromons deref) places))]
(([move #(turn % -1) #(turn % 1)]
(wrand [(if (:ant @ahead) 0 (ranks ahead))
(ranks ahead-left) (ranks ahead-right)]))
loc)))
;foraging
(cond
(and (pos? (:food @p)) (not (:home @p)))
(-> loc take-food (turn 4))
(and (pos? (:food @ahead)) (not (:home @ahead)) (not (:ant @ahead)))
(move loc)
:else
(let [ranks (merge-with +
(rank-by (comp :food deref) places)
(rank-by (comp :pheromons deref) places))]
(([move #(turn % -1) #(turn % 1)]
(wrand [(if (:ant @ahead) 0 (ranks ahead))
(ranks ahead-left) (ranks ahead-right)]))
loc)))))))
(defn evaporate
"causes all the pheromones to evaporate a bit"
[]
(doall
(for [x (range dim) y (range dim)]
(dosync
(let [p (get-cell [x y])]
(alter p assoc :pheromons (* evap-rate (:pheromons @p))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; UI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(import
'(java.awt Color Graphics Dimension)
'(java.awt.image BufferedImage)
'(javax.swing JPanel JFrame))
;pixels per world cell
(def scale 5)
(defn fill-cell [#^Graphics g x y c]
(doto g
(.setColor c)
(.fillRect (* x scale) (* y scale) scale scale)))
(defn render-ant [ant #^Graphics g x y]
(let [black (. (new Color 0 0 0 255) (getRGB))
gray (. (new Color 100 100 100 255) (getRGB))
red (. (new Color 255 0 0 255) (getRGB))
[hx hy tx ty] ({0 [2 0 2 4]
1 [4 0 0 4]
2 [4 2 0 2]
3 [4 4 0 0]
4 [2 4 2 0]
5 [0 4 4 0]
6 [0 2 4 2]
7 [0 0 4 4]}
(:direction ant))]
(doto g
(.setColor (if (:food ant)
(new Color 255 0 0 255)
(new Color 0 0 0 255)))
(.drawLine (+ hx (* x scale)) (+ hy (* y scale))
(+ tx (* x scale)) (+ ty (* y scale))))))
(defn render-place [g p x y]
(when (pos? (:pheromons p))
(fill-cell g x y (new Color 0 255 0
(int (min 255 (* 255 (/ (:pheromons p) pher-scale)))))))
(when (pos? (:food p))
(fill-cell g x y (new Color 255 0 0
(int (min 255 (* 255 (/ (:food p) food-scale)))))))
(when (:ant p)
(render-ant (:ant p) g x y)))
(defn render [g]
(let [v (dosync (apply vector (for [x (range dim) y (range dim)]
@(get-cell [x y]))))
img (new BufferedImage (* scale dim) (* scale dim)
(. BufferedImage TYPE_INT_ARGB))
bg (. img (getGraphics))]
(doto bg
(.setColor (. Color white))
(.fillRect 0 0 (. img (getWidth)) (. img (getHeight))))
(dorun
(for [x (range dim) y (range dim)]
(render-place bg (v (+ (* x dim) y)) x y)))
(doto bg
(.setColor (. Color blue))
(.drawRect (* scale home-off) (* scale home-off)
(* scale nants-sqrt) (* scale nants-sqrt)))
(. g (drawImage img 0 0 nil))
(. bg (dispose))))
(def panel (doto (proxy [JPanel] []
(paint [g] (render g)))
(.setPreferredSize (new Dimension
(* scale dim)
(* scale dim)))))
(def frame (doto (new JFrame) (.add panel) .pack .show))
(def animator (agent nil))
(defn animation [x]
;(.setPriority (Thread/currentThread) (Thread/MIN_PRIORITY))
(when running
(send-off *agent* #'animation))
(. panel (repaint))
(. Thread (sleep animation-sleep-ms))
nil)
(def evaporator (agent nil))
(defn evaporation [x]
(when running
(send-off *agent* #'evaporation))
(evaporate)
(. Thread (sleep evap-sleep-ms))
nil)
(defn start-ants []
(def ants (setup))
(send-off animator animation)
(dorun (map #(send-off % behave) ants))
(send-off evaporator evaporation))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; use ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ants (setup))
(send-off animator animation)
(dorun (map #(send-off % behave) ants))
(send-off evaporator evaporation))

Podsumowanie
Program napisany w Clojurze można zapakować do jara. Do uruchomienia wystarczy Java 5 (bez żadnych dodatkowych bibliotek).
Sam Clojure rozprowadzany jest jako plik jar o wielkości około 2MB, co pozwala go łatwo dołączyć także do istniejących programów w Javie.
