Clojure Programming/Examples/Cookbook
Binding
[edit | edit source]How to do structural binding or destructuring in function argument lists or let bindings.
[edit | edit source]Instead of accessing values in a map like this:
(defn list-xyz [xyz-map]
(list (:x xyz-map) (:y xyz-map) (:z xyz-map)))
user=> (list-xyz {:x 1, :y 2 :z 3})
(1 2 3)
You can destructure the map like this:
(defn list-xyz [{x :x, y :y, z :z}]
(list x y z))
user=> (list-xyz {:x 1, :y 2, :z 3})
(1 2 3)
or better yet, like this:
(defn list-xyz [{:keys [x, y, z]}]
(list x y z))
user=> (list-xyz {:x 1, :y 2 :z 3})
(1 2 3)
Instead of accessing vector elements like this:
(defn vec-norm [vec3]
(Math/sqrt (+ (* (nth vec3 0) (nth vec3 0))
(* (nth vec3 1) (nth vec3 1))
(* (nth vec3 2) (nth vec3 2)))))
you can destructure the vector like this:
(defn vec-norm [[x, y, z]]
(Math/sqrt (+ (* x x) (* y y) (* z z))))
user=> (vec-norm [1, 2, 3])
3.7416573867739413
Looping
[edit | edit source]Loop through the items in a sequence
[edit | edit source]user=> (doseq [ item '((1 2) [3 4] "D")] (prn item))
(1 2)
[3 4]
"D"
nil
user=> (doseq [item {:a 1 :b 2}] (prn item))
[:b 2]
[:a 1]
user=> (for [item {:a 1 :b 2}] item)
([:a 1] [:b 2])
for loop
[edit | edit source]user=> (dotimes [i 4] (prn i))
0
1
2
3
recursive looping
[edit | edit source](defn my-zipmap [keys vals]
(loop [my-map {}
my-keys keys
my-vals vals]
(if (and (seq my-keys) (seq my-vals))
(recur (assoc my-map (first my-keys) (first my-vals))
(rest my-keys)
(rest my-vals))
my-map)))
(println (my-zipmap [:a :b :c] [1 2 3]))
{:c 3, :b 2, :a 1}
Modifying State
[edit | edit source]How to write x = x+ 1
[edit | edit source]user=> (def x (atom 0))
#'user/x
user=> (swap! x + 1)
1
user=> (swap! x + 1)
2
user=> @x
2
Or, a more idiomatic way would be to use inc:
user=> (def x (atom 0))
#'user/x
user=> (swap! x inc)
1
user=> (swap! x inc)
2
user=> @x
2
The most idiomatic way is not do this at all. Why would you want to do x=x+1 in clojure?
Sets
[edit | edit source]How to create a set
[edit | edit source](def p #{1,2,3})
How to find union/intersection/difference of sets
[edit | edit source](def a #{1,2,3,4})
(def b #{2,3,5})
user=> (clojure.set/union a b)
#{1 2 3 4 5}
user=> (clojure.set/intersection a b)
#{2 3}
user=> (clojure.set/difference a b)
#{1 4}
Sequences
[edit | edit source]Operations
[edit | edit source]Create a list of n copies of an object
[edit | edit source]user=> (repeat 10 "a")
("a" "a" "a" "a" "a" "a" "a" "a" "a" "a")
Create a list of n calls to a function of no argument
[edit | edit source]; repeatedly generates an infinite sequence of calls to a function that takes no arguments
; No calls are made until the result is needed! (do not try to evaluate this list directly, it is infinite,
; and will run forever)
user=> (def random-ints (repeatedly #(rand-int 100)))
; use take to take the integers:
user=> (take 10 random-ints)
(66 8 31 90 78 18 28 8 94 3)
;NOTE: seqs are cached, so taking new random ints will always return the same result.
;This also means that if you take many ints from a global seq (def'd one) ALL the integers will
;stay in memory until the name is redefined to something else!
user=> (take 15 random-ints) ; first 10 are the same, 5 new ints generated
(66 8 31 90 78 18 28 8 94 3 84 29 71 85 41)
; to avoid a global list, you can do this:
(defn make-calls [n func]
(take n (repeatedly func)))
; no fear of keeping huge lists in memory this time (unless you hold onto them, of course)
user=> (make-calls 5 #(rand-int 100))
(60 75 89 62 36)
; upon next call, the result will be different:
user=> (make-calls 5 #(rand-int 100))
(94 95 88 11 93)
append or concatenate two more sequences together
[edit | edit source]user=> (concat [1 3] [3 4 3] [3 3])
(1 3 3 4 3 3 3)
Infinite Sequences
[edit | edit source]generate an infinite cyclic list
[edit | edit source](def x (cycle [1 2 3]))
user=> (take 12 x)
(1 2 3 1 2 3 1 2 3 1 2 3)
generate an infinite sequence of random numbers
[edit | edit source](def rands (repeatedly rand))
user=> (take 4 rands)
(0.39300911409554096 0.24329175257444235 0.03259576739916903 0.7459916914364135)
user=>
generate an infinite repeating sequence
[edit | edit source](def just4 (repeat 4))
user=> (take 5 just4)
(4 4 4 4 4)
generate infinite sequence of nested function calls
[edit | edit source]The infinite sequence (0 1 2 3 4 5 ....) can be defined using (range) since clojure 1.2:[1]
(def integers (range))
(take 10 integers)
;; ⇒ (0 1 2 3 4 5 6 7 8 9)
or alternatively using iterate:
;; Generates (x (inc x) (inc (inc x)) ...)
(def integers (iterate inc 0))
(take 4 integers)
;; ⇒ (0 1 2 3)
(def newton (iterate (fn[x] (/ (+ x (/ 2.0 x)) 2)) 2))
(take 5 newton)
;; ⇒ (2 1.5 1.4166666666666665 1.4142156862745097 1.4142135623746899)
Polymorphism
[edit | edit source]Overload function based on number of arguments:
(defn argcount
([] 0) ; Zero arguments
([x] 1) ; One argument
([x & args] (+ 1 (count args)))) ; List of arguments
(argcount)
;; ⇒ 0
(argcount "dog")
;; ⇒ 1
(argcount "cat" 1 3 4)
;; ⇒ 4
Create multi-method which dispatches on the number of its arguments:
(defmulti g (fn[& arglist] (count arglist)))
(defmethod g 0 [& arglist] "No arguments.")
(defmethod g 1 [& arglist] "One argument.")
(defmethod g 2 [& arglist] "Two arguments.")
(defmethod g :default [& arglist] "More than two arguments.")
(g) ; ⇒ "No arguments."
(g 1) ; ⇒ "One argument."
(g 2) ; ⇒ "One argument."
(g 3 4) ; ⇒ "Two arguments."
(g "cart" 1 [2 3 ]) ; ⇒ "More than two arguments."
Create a multi-method which dispatches on the class of its arguments:
(comment Define f to be a multi-method function and dispatch using class of argument)
(defmulti f class)
(comment Use this definition for f if the class of the argument x is a long)
(defmethod f Long [x] "Argument is a long")
(comment Use this definition for f if the class of the argument x is a double)
(defmethod f Double [x] "Argument is a double")
(comment Use this definition for f for all other argument types )
(defmethod f :default [x] "Argument is not a number")
(f 3) ; ⇒ "Argument is a long"
(f 3.4) ; ⇒ "Argument is a double"
(f "string") ; ⇒ "Argument is not a number"
(comment Define g to be a multi-method function which dispatches on the class of its arguments)
(defmulti g (fn[x,y] [(class x) (class y )]))
(comment Use this definition for g if class of first argument is a long and class of second argument is a long)
(defmethod g [Long,Long] [x,y] "Both arguments are longs")
(comment Use this definition for g if class of first argument is long and class of second argument is double)
(defmethod g [Long,Double] [x,y] "First argument is a long and second argument is a double")
(comment Use this definition for g as the default )
(defmethod g :default [x,y] "All other cases")
(g 3 2) ; ⇒ "Both arguments are longs"
(g 3 4.3) ; ⇒ "First argument is a long and second argument is a double"
(g 4.3 4.3) ; ⇒ "All other cases"
Create a multi-method that dispatches on the value of its argument:
(comment Create multi method h that dispatches on the value of the argument)
(defmulti h (fn[x] x))
(comment Use this definition for h if argument is 4)
(defmethod h 4 [x] "argument is 4")
(comment Use this definition for other values of h)
(defmethod h :default [x] "argument is not 4")
(h 4) ; ⇒ "argument is 4"
(h 3) ; ⇒ "argument is not 4"
(h [3 34]) ; ⇒ "argument is not 4"
(comment Create multi method h that dispatches on the value of the argument being in an interval)
(defmulti h (fn [x] (<= 4 x 10)))
(comment Use this definition for h if argument is between 4 and 10 )
(defmethod h true [x] "argument is between 4 and 10")
(comment Use this definition for other values of h)
(defmethod h :default [x] "argument is not between 4 and 10")
(h 1) ; ⇒ "argument is not between 4 and 10"
(h 4) ; ⇒ "argument is between 4 and 10"
(h 10) ; ⇒ "argument is between 4 and 10"
(h 11) ; ⇒ "argument is not between 4 and 10"
Java
[edit | edit source]Instantiate a new Java object
[edit | edit source](new JFrame)
;; or
(JFrame.)
Call a static method of a java class
[edit | edit source]user=> (Math/cos 3)
-0.9899924966004454
user=> (. Math cos 3)
-0.9899924966004454
Call non-static method of a java object
[edit | edit source];;method name first
(.getContentPane frame)
;;object first
(. frame getContentPane)
Accessing inner classes
[edit | edit source]Class definition in Java.
public class OkCancelDialog {
//Inner class
public static enum State {
OK, CANCEL;
};
}
;;accessing the inner class and its static fields
(println OkCancelDialog$State/OK)
(println OkCancelDialog$State/CANCEL)
Nested series of method calls
[edit | edit source];; equivalent to frame.getContentPane().getSize()
(.. frame getContentPane getSize)
Simple Drawing in a window
[edit | edit source](ns drawing-demo
(:import [javax.swing JPanel JFrame]
[java.awt Dimension]))
(defn make-panel []
(let [panel (proxy [JPanel] []
(paintComponent [g]
(.drawLine g 0 0 100 100)))]
(doto panel
(.setPreferredSize (Dimension. 400 400)))))
(defn make-frame [panel]
(doto (new JFrame)
(.add panel)
.pack
.show))
(make-frame (make-panel))
Import Java Classes from Jar File
[edit | edit source](import '(cljext.swing DelegatingPanel IPainter))
The first item in the import list is the package name followed by the names of all the classes in the package to import.
Note: you must use the package name and not the name of the jar file which contains the package of classes. If you're not sure what the package name is, from a terminal type:
jar tf jarfilename.jar
Let's say you see something like: org/jfugue/Anticipator.class
Then the import statement would be:
(import '(org.jfugue Anticipator))
Not:
(import '(jarfilename Anticipator))
File IO
[edit | edit source]Load File IO library
[edit | edit source](use 'clojure.contrib.duck-streams)
Read entire contents of file into string
[edit | edit source](slurp "somefile.txt")
Write to Output File
[edit | edit source]Writing creates a new file or will overwrite existing file
(spit "output.txt" "some output text")
To append to existing file use "spit" with ":append" set to true
(spit "output.txt" "more text with spit append" :append true)
Now our file should say:
"some output textmore text with spit append"
Strings
[edit | edit source]Use str to concatenate strings:
(str "A" "B" "C")
;; ⇒ "ABC"
and use apply with str as an argument to concatenate a sequence of strings:
(apply str ["A" "B" "C"])
;; ⇒ "ABC"
(apply str ["/usr/include" \/ "stdio.h"])
;; ⇒ "/usr/include/stdio.h"
(map
(fn [file] (str "/usr/include/" file ".h"))
["stdio" "gmp" "signal"])
;; ⇒ ("/usr/include/stdio.h" "/usr/include/gmp.h" "/usr/include/signal.h")
Use interpose to join a sequence of strings with a separator:
(apply str (interpose \: ["A" "B" "C"]))
;; ⇒ "A:B:C"
(apply str
(interpose \:
["/usr/local/sbin" "/usr/local/bin" "/usr/sbin" "/usr/bin" "/sbin" "/bin"]))
;; ⇒ "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Use re-seq to split a string at boundaries by a regular expression, here \w+ denotes a character class of all alphanumeric characters plus "_":
(re-seq #"\w+" "to be or not to be")
;; ⇒ ("to" "be" "or" "not" "to" "be")
Reversing a string is done via reverse which returns a sequence of the characters in the string; use apply with str to turn it into a string again:
(reverse "I am cold")
;; ⇒ (\d \l \o \c \space \m \a \space \I)
(apply str (reverse "I am cold"))
;; ⇒ "dloc ma I"
and to convert any object into a string simply supply it as an argument to the str function:
(str 3) ; ⇒ "3"
(str 3.0) ; ⇒ "3.0"
(str 'a) ; ⇒ "a"
(str '(1 2)) ; ⇒ "(1 2)"
(str {:a 1 :b 2}) ; ⇒ "{:a 1, :b 2}"
Operating System Calls
[edit | edit source]Get the current working directory
[edit | edit source]user=> (System/getProperty "user.dir")
"/Applications/clojure"
Set the current working directory (result is prior working directory)
[edit | edit source]user=> (System/setProperty "user.dir" "~/test")
"/Applications/clojure"