Jump to content

Clojure Programming/Examples/Cookbook

From Wikibooks, open books for an open world

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?

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"

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"

References

[edit | edit source]
  1. range on ClojureDocs