Clojure Programming/Examples/Cookbook
From Wikibooks, the open-content textbooks collection
Contents |
[edit] Binding
[edit] How to do structural binding or destructuring in function argument lists or let bindings.
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
[edit] Looping
[edit] Loop through the items in a sequence/ Loop through items in a sequence
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]
[edit] for loop
user=> (dotimes [i 4] (prn i)) 0 1 2 3
[edit] Modifying State
[edit] How to write x = x+ 1
user=> (def x (atom 0)) #'user/x user=> (swap! x + 1) 1 user=> (swap! x + 1) 2 user=> @x 2
[edit] Sets
[edit] How to create a set
(def p #{1,2,3})
[edit] How to find union/intersection/difference of sets
(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}
[edit] Sequences
[edit] Operations
[edit] Create a list of n copies of an object
user=> (replicate 10 "a") ("a" "a" "a" "a" "a" "a" "a" "a" "a" "a")
[edit] Create a list of n calls to a function of no argument
; 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)
[edit] append or concatenate two more sequences together
user=> (concat [1 3] [3 4 3] [3 3]) (1 3 3 4 3 3 3)
[edit] Infinite Sequences
[edit] generate an infinite cyclic list
(def x (cycle [1 2 3])) user=> (take 12 x) (1 2 3 1 2 3 1 2 3 1 2 3)
[edit] generate an infinite sequence of random numbers
(def rands (repeatedly rand)) user=> (take 4 rands) (0.39300911409554096 0.24329175257444235 0.03259576739916903 0.7459916914364135) user=>
[edit] generate an infinite repeating sequence
(def just4 (repeat 4)) user=> (take 5 just4) (4 4 4 4 4)
[edit] generate infinite sequence of nested function calls
(comment Generates (x (inc x) (inc (inc x)) ...)) (def integers (iterate inc 0)) user=> (take 4 integers) (0 1 2 3)
(def newton (iterate (fn[x] (/ (+ x (/ 2.0 x)) 2)) 2)) user=> (take 5 newton) (2 1.5 1.4166666666666665 1.4142156862745097 1.4142135623746899)
[edit] Polymorphism
[edit] overload function based on number of arguments
(defn argcount ;;zero arguments ([] 0) ;; 1 argument ([x] 1) ;; list of arguments ([ x & args] (inc (count args)))) user=> (argcount ) 0 user=> (argcount "dog") 1 user=> (argcount "cat" 1 3 4) 4
[edit] create multi-method which dispatches on the number of its arguments
(defmulti g (fn[& arglist] (count arglist))) (defmethod g 0 [& arglist] "zero args") (defmethod g 1 [& arglist] "one arg") (defmethod g 2 [& arglist] "two args") (defmethod g :default [& arglist] "more than two args") user=> (g) "zero args" user=> (g 1) "one arg" user=> (g 2) "one arg" user=> (g 3 4) "two args" user=> (g "cart" 1 [2 3 ]) "more than two args"
[edit] 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 (fn[x] (class x))) (comment Use this definition for f if the class of the argument x is an integer) (defmethod f Integer [x] "Argument is an integer") (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 an integer") user=> (f 3) "Argument is an integer" user=> (f 3.4) "Argument is a double" user=> (f "list") "Argument is not an integer"
(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 an integer and class of second argument is an integer) (defmethod g [Integer,Integer] [x,y] "Both arguments are integers") (comment Use this definition for g if class of first argument is integer and class of second argument is double) (defmethod g [Integer,Double] [x,y] "First argument is an integer and second argument is a double") (comment Use this definition for g as the default ) (defmethod g :default [x,y] "All other cases") user=> (g 3 2) "Both arguments are integers" user=> (g 3 4.3) "First argument is an integer and second argument is a double" user=> (g 4.3 4.3) "All other cases"
[edit] 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") user=> (h 4) "argument is 4" user=> (h 3) "argument is not 4" user=> (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] (and (<= 4 x) (<= 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") user=> (h 1) "argument is not between 4 and 10" user=> (h 4) "argument is between 4 and 10" user=> (h 10) "argument is between 4 and 10" user=> (h 11) "argument is not between 4 and 10"
[edit] Java
[edit] Add an item to the classpath from Clojure
(add-classpath "file:///Applications/clojure/jfugue.jar")
This is useful if you are using Enclojure for NetBeans because as of 1/09 NetBeans doesn't seem to see external jars even if you add them to library folder.
[edit] Instantiate a new Java object
(new JFrame) ;; or (JFrame.)
[edit] Call a static method of a java class
user=> (Math/cos 3) -0.9899924966004454 user=> (. Math cos 3) -0.9899924966004454
[edit] Call non-static method of a java object
;;method name first (.getContentPane frame) ;;object first (. frame getContentPane)
[edit] Nested series of method calls
;; equivalent to frame.getContentPane.getSize() (.. frame getContentPane getSize)
[edit] Simple Drawing in a window
(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))
[edit] Import Java Classes from Jar File
(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))
[edit] File IO
[edit] Read entire contents of file into string
(slurp "somefile.txt")
[edit] Strings
[edit] Concatenate strings
user=> (str "A" "B" "C") "ABC"
[edit] Concatenate a sequence of strings
user=> (apply str ["A" "B" "C"]) "ABC"
[edit] Join a sequence of strings with a separator
user=> (apply str (interpose ":" ["A" "B" "C"])) "A:B:C"
[edit] Split a string at boundaries specified by a regular expression
user=> (re-seq #"\w+" "to be or not to be") ("to" "be" "or" "not" "to" "be")
[edit] Reverse string
user=> (apply str (reverse "I am cold")) "dloc ma I"
[edit] Convert an object to string
user=> (str 3) "3" user=> (str 3.0) "3.0" user=> (str 'a) "a" user=> (str '(1 2)) "(1 2)"
[edit] Operating System Calls
[edit] Get the current working directory
user=> (. System getProperty "user.dir") "/Applications/clojure"