Warning: array_merge(): Expected parameter 2 to be an array, bool given in /home/customer/www/inchkeithconsulting.com/public_html/wp-content/plugins/urvanov-syntax-highlighter/class-urvanov-syntax-highlighter.php on line 275
To start Clojure we can spin up the REPL with the command ‘clj’
1 2 3 | > clj Clojure 1.11.1 user=> |
And then to get back to the command line its as simple as CTRL-C
1 2 3 4 5 | > clj Clojure 1.11.1 user=> > ; Back to the command line because we type CTRL-C ; Clojure shows nothing when you do it |
We can also run a clojure script from the command line. We therefore typically store our code in files with the .clj extension. We can then load a lisp file via a command line parameter
1 2 | > clj -M random2.clj 4 |
In Clojure, we can use comments to add documentation to the code inline
1 2 3 | ;;; Single-line comments start with a semicolon; use four for file-level ;;; comments, three for section descriptions, two inside definitions, and one ;;; for single lines. |
Hello, World! ( because it’s mandatory )
1 2 3 | user=> (println "Hello, World!") Hello, World! nil |
And that’s it, that’s Lisp ( or Clojure ) in a nutshell. An expression is a list which the Clojure interpreter evaluates and the result displayed. Each expression is a list surrounded by (). In this instance the expression calls the function println ( which displays text to the console ) with the parameter “Hello, World!”.
Documentation
Before we get into the details of programming in Lisp, the Clojure REPL provides some useful functions to get documentation and help on the libraries and functions you will be using.
1 2 3 4 5 6 7 | user=> ( doc + ) ------------------------- clojure.core/+ ([] [x] [x y] [x y & more]) Returns the sum of nums. (+) returns 0. Does not auto-promote longs, will throw on overflow. See also: +' nil |
1 2 3 | ;; Find function matching regex user=> (apropos "+") (clojure.core/+ clojure.core/+' clojure.core/read+string clojure.spec.alpha/+ clojure.spec.alpha/rep+impl) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ;; Find documentation user=> (find-doc "trim") clojure.core/subvec ([v start] [v start end]) Returns a persistent vector of the items in vector from start (inclusive) to end (exclusive). If end is not supplied, defaults to (count vector). This operation is O(1) and very fast, as the resulting vector shares structure with the original and no trimming is done. clojure.string/trim ([s]) Removes whitespace from both ends of string. : : |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ;; List all functions in a namespace user=> (dir clojure.repl) apropos demunge dir dir-fn doc find-doc pst root-cause set-break-handler! source source-fn stack-element-str thread-stopper |
1 2 3 4 5 6 7 8 | ;; See function source code user=> (source dir) (defmacro dir "Prints a sorted directory of public vars in a namespace" [nsname] `(doseq [v# (dir-fn '~nsname)] (println v#))) |
1 2 3 4 | ;; Remember Clojure is built on top of the JVM that underlying is all ;; of the Java eco system. We can therefore access all of the Javadoc system user=> (javadoc "string") ;; Will display the javadoc info for Java Class String |
Basic Numbers, Types and Operations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ;; Numbers 42 ; integer -1.5 ; floating point 22/7 ; ratio ;; Add two numbers user=> ( + 2 2 ) 4 ;; Multiple 3 numbers user=> ( * 2 3 4 ) 60 ;; Add and multiply multiple numbers user=> ( * ( + 1 2 ) ( + 2 3 ) ( + 3 4 ) ) 105 type? zero? pos? neg? even? odd? number? integer? float? |
1 2 3 4 | ;; Characters "hello" ; string \e ; character #"[0-9]+" ; regular expression |
1 2 3 4 5 6 7 8 | ;; Symbols and Idents map ; symbol + ; symbol - most punctuation allowed clojure.core/+ ; namespaced symbol nil ; null value true false ; booleans :alpha ; keyword :release/alpha ; keyword with namespace |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ;; Strings "Hello World" ;; A string ;; String Operations (str "Hello, " "World!"). ;; Concatenates 2 strings (format "Hello , %s" "World") ;; Format using Java String options (count "1234567890") ;; Count the number of chars subs compare lower-case upper-case join split split-lines reverse replace trim triml trimr |
Variables
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ;; Variable Types short. ;; This is used to represent a short number. For example, 10. int ;; This is used to represent whole numbers. For example, 1234. long ;; This is used to represent a long number. For example, 10000090. float ;; This is used to represent 32-bit floating point numbers. For example, 12.34. char ;; This defines a single character literal. For example, ‘/a’. Boolean ;; This represents a Boolean value, which can either be true or false. String ;; These are text literals which are represented in the form of chain of characters. For example, “Hello World”. ;; Declaring variables user=>( def x 7 ) ; Define x as a var with value 7 #'user/x user=>( + x x ) ; Use the var x in an expressions 14 user=>(println "The value of x is " x) The value of x is 7 user=>(println "The value of 2x is " ( + x x)) The value of 2x is 142 (type x) => java.lang.Long |
1 2 3 4 5 6 7 8 9 10 11 | ;; Lexical Bindings (let [s (foo whatever)] ;; s is bound here ) ;; but not here ;; But... (def s (foo whatever)) ;; s is bound here |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ;; Arithmetic Operators (+ 2 2) (- 1 3) (* 3 4) (/ 4 2) (inc 5) (dec 7) (max 3 4 7 8) (min 3 4 7 8) (rem 3 2) (mod 2 1) |
1 2 3 4 5 6 7 8 | ;; Relational Operators (= 2 3) (not= 3 3) (< 3 2) (<= 3 3) (> 10 7) (>= 7 7) |
1 2 3 4 5 | ;; Logical Operators (and true false) (or true true) (not true) |
1 2 3 4 5 6 | ;; Bitwise operators (bit-and 1 1) (bit-or 1 1) (bit-xor 1 1) (bit-not 1) |
1 2 3 4 5 6 7 | ;; Atoms (def myatom (atom 1)) ;; Create an atom (reset! myatom 2) ;; Not atomic (compare-and-set! myatom 2 3) ;; Atomic operation, only if old value is true (swap! myatom inc) ;; Atomic swap based on function |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ;; Loops ;; while (def x (atom 1)) ;; Atom because we are changing value (while ( < @x 5 ) (do (println @x) (swap! x inc)))) ;; Atomic function to incr by 1 ;; doseq (doseq [n [0 1 2]] (println n))) ;; dotimes (dotimes [n 5] (println n))) ;; loop (loop [x 10] (when (> x 1) (println x) (recur (- x 2)))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ;; Decisions ;; if if ( = 2 2) ;; If (println "Values are equal") if ( = 2 2) ;; If else (println "Values are equal") (println "Values are not equal"))) if ( and (= 2 2) (= 3 3)) ;; Multiple boolean (println "Values are equal") (println "Values are not equal"))) ;; nested if (def x 10) (if (> x 9) (if (= x 10) (println "eq 10")) ) ;; if-do (def x 2) (def y 2) if (= x y) (do(println "Both the values are equal") (println "true")) (do(println "Both the values are not equal") (println "false")))) ;; case (case x 5 (println "x is 5") ;; == 5 10 (println "x is 10") ;; == 10 (println "x is neither 5 nor 10"))). ;; Default value ;; cond (cond (= x 5) (println "x is 5") (= x 10)(println "x is 10") :else (println "x is not defined"))) |
Functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | ;; Define a function user=>(defn say [text] (str "I said: " text)) #'user/say user=> (say "What?" ) "I said: What?" user=> ;; Multi-arity functions ;; Arity is the number of arguments or operands taken by a function, ;; operation or relation in logic, mathematics, and computer science. user=>(defn messenger ([] (messenger "Hello world!")) ([msg] (println msg))) #'user/messenger user=> (messenger) Hello world! nil user=> (messenger "Keith") Keith nil ;; Variadic Functions (defn hello [greeting & remaining] (println greeting remaining)) user=> (hello "jane" "rod" "freddy") jane (rod freddy) nil ;; Anonymous Function (fn [message] (println message) ) ; Cannot be called ( (fn [message] (println message)) "Hello world!" ) ; Called immediately (def greet (fn [name] (str "Hello, " name))). ; defn vs fn ;; Higher Order Functions map reduce remove filter iterate |
1 2 3 4 5 6 | ;; Predicates every-pred? every? some not-any? |
1 2 3 4 5 6 7 8 | ;; Recursion ;; Tail Recusion (loop [i 0] (when (< i 5) (println i) (recur (inc i)))) |
1 2 3 4 5 6 | ;; Macros (defmacro Simple [] (println "Hello")) (macroexpand '(Simple))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ;; Lists (list 1 2 3 4) ;; Creates a lisst (1 2 3 4) (list* 1 [2,3]) ;; (1 2 3) (first '(1 2 3)) ;; 1 (rest '1 2 3)) ;; (2 3) (nth '(1 2 3) 2) ;; 3 (nth '(1 2 3) 10) ;; IndexOutOfBounds Exception (cons 0 (list 1 2,3)) ;; (0 1 2 3) (conj (list 1 2,3) 4 5) ;; (5 4 1 2 3) ;; Where is car and cdr ? ;; Clojure does not support the ( X . Y ) structure in which ;; car and cdr operatre |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ;; Sets (def mySet (set '(1 1 2 2))) ;; => 'user/mySet mySet ;; => #{1 2} (type mySet) ;; => clojure.lang.PersistentHashSet (sorted-set 3 2 1) (get (set '(3 2 1)) 2) (contains? (set '(3 2 1)) 2) (conj (set '(3 2 1)) 5)) (disj (set '(3 2 1)) 2) (set/union #{1 2} #{3 4})) ;; (:require [clojure.set :as set]) (set/difference #{1 2} #{2 3})) ;; (:require [clojure.set :as set]) (set/intersection #{1 2} #{2 3})) ;; (:require [clojure.set :as set]) (set/subset? #{1 2} #{1 2 3})) ;; (:require [clojure.set :as set]) (set/superset? #{1 2 3} #{1 2})) ;; (:require [clojure.set :as set]) |
1 2 3 4 5 6 7 8 9 10 11 | ;; Vectors (:require [clojure.set :as set]) (vector 1 2 3) (vector-of :int 1 2 3)) (nth (vector 1 2,3) 0) (get (vector 3 2 1) 2) (conj (vector 3 2 1) 5)) (pop (vector 3 2 1))) (subvec (vector 1 2 3 4 5 6 7) 2 5)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ;; Maps (:require [clojure.set :as set]) (hash-map "z" "1" "b" "2" "a" "3") ;; => {z 1, b 2, a 3} (sorted-map "z" "1" "b" "2" "a" "3") ;; => {a 3, b 2, z 1} (def demokeys (hash-map "z" "1" "b" "2" "a" "3")) (def demokeys1 (hash-map "a" 2 "h" 5 "i" 7)) (get demokeys "b")) (contains? demokeys "b") (find demokeys "b") (keys demokeys) (vals demokeys) (dissoc demokeys "b")) (merge demokeys demokeys1)) (merge-with + demokeys demokeys1) (select-keys demokeys ["z" "a"])) (set/rename-keys demokeys {"z" "newz" "b" "newb" "a" "newa"}) (set/map-invert demokeys) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ;; Sequences (seq [1 2 3])) (def seq1 (seq [1 2])) (def seq2 (seq [3 4])) (cons 0 (seq [1 2 3])) (conj [1 2 3] 4)) (concat seq1 seq2)) (distinct (seq [1 1 2 2])) (reverse (seq [1 2 3])) (first seq1) (last seq1) (rest seq1) (sort seq1) (drop 2 seq1) (take 2 seq1) (take-last 2 seq1) (split-at 2 seq1) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ;; Destructuring (def my-vector [1 2 3 4]) (let [[a b c d] my-vector] ;; => a=1, b=2, c=3, d=4 (def my-vector [1 2 3 4]) (let [[a b c d e] my-vector]) ;; => a=1, b=2, c=3, d=nil (def my-vector [1 2 3 4]) (let [[a b & the-rest] my-vector] (def my-map {"a" 1 "b" 2}) (let [{a "a" b "b"} my-map] ;; => a=1, b=2 (def my-map {"a" 1 "b" 2}) (let [{a "a" b "b" c "c"} my-map]) ;; => a=1, b=2, c=nil |
1 2 3 4 5 6 | ;; Regular Expressions (re-pattern "\\d+")) (re-find pat "abc123de")) (clojure.string/replace "abc123de" pat "789") (clojure.string/replace-first "abc123de123" pat "789") |
For more information on regular expressions, see the crib sheet Regular Expressions
1 2 3 4 5 6 7 | ;; Meta Data (def my-map (with-meta [1 2 3] {:prop "values"})) ;; => [1, 2, 3] (meta my-map)) ;; => {:prop values} (def my-map (with-meta [1 2 3] {:prop "values"})) (def new-map (vary-meta my-map assoc :newprop "new values")) |
1 2 3 4 5 6 7 8 9 10 | ;; Structures (defstruct Employee :EmployeeName :Employeeid) (struct Employee "John" 1) (struct-map Employee :EmployeeName "John" :Employeeid 1) (:Employeeid Employee) ;; Because structs are immutable (def emp (struct-map Employee :EmployeeName "John" :Employeeid 1)) (def newemp (assoc emp :EmployeeRank "A")) ;; Creates a new struct |
1 2 3 4 5 6 7 8 9 10 11 | ;; I/O (read-line) (let [console (. System console) pwd (.readPassword console "tell me your password: ")] (println "your password is " pwd)) (let [reader (java.io.BufferedReader. *in*) ln (.readLine reader)] (println "your line is " ln)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ;; File I/O ;; Read contents of a file into a string (def string1 (slurp "Example.txt")) ;; Read contents of a file one line at a time (with-open [rdr (clojure.java.io/reader "Example.txt")] (reduce conj [] (line-seq rdr)))) ;; with-open opens a file into buffered stream called rdr ;; Returns the lines of text from rdr as a lazy sequence of string ;; Reduce applies conj to the vector using the sequence ;; Write string to a file (spit "Example.txt" "This is a string") ;; Write strings one at a time to a file (with-open [w (clojure.java.io/writer "Example.txt" :append true)] (.write w (str "hello" "world")))) ;; File exists (.exists (clojure.java.io/file "Example.txt")) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | ;; Namespaces (:require [clojure.set :as set]) (*ns*) (ns clojure.myown (:require [clojure.set :as set])) (alias 'string 'clojure.examples.hello) (ns-unalias 'clojure.examples.hello 'string)) (all-ns) (find-ns 'clojure.string) (ns-name 'clojure.string) (ns-aliases 'clojure.core) (ns-map 'clojure.core) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ;; Exception Handling (try (code under execution) (catch Exception name (Exception handling code)) ) (try (/ 1 0) (catch ArithmeticException e (println "Weird arithmetics"))) (try (code under execution) (catch Exception1 name (Exception handling code)) (catch Exception2 name (Exception handling code)) ) (println "Weird arithmetics"))) (try (code under execution) (catch Exception1 name (Exception handling code)) (catch Exception2 name (Exception handling code)) (finally (final code)) ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ;; Java Interface ;; Java.lang objects (.toUpperCase "Hello World") ;; Any Java method on a string (.indexOf "Hello World","e") ;; Java method with parameters ;; Ysing the . special form (let [d (java.util.Date.)] ;; New instance of Date assigned to d (. d getTime)) ;; call getTime on d ;; Create Java objects (def str1 (new String "Hello")) (def my-int(new Integer 1)) ;; Import Java LIbrary (import java.util.Stack) (.. System getProperties (get "java.version")) |
1 2 3 4 5 6 7 8 9 | ;; Date & Time (def date (.toString (java.util.Date.))) (println date)) (def date (.format (java.text.SimpleDateFormat. "MM/dd/yyyy") (new java.util.Date))) (println date)) (def date (.getTime (java.util.Date.))) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ;; Agents (def counter (agent 0)) (send counter + 1) ;; => @counter == 1 (send counter + 1) ;; => @counter == 1 (send-off counter + 100) (await-for 100 counter) (await counter) (def my-date (agent(java.util.Date.))) (send my-date + 100) (await-for 100 my-date) (println (agent-error my-date))) (shutdown-agents) |
1 2 3 4 5 6 7 8 9 10 | ;; Watches (def x (atom 0)) (add-watch x :watcher (fn [key atom old-state new-state] (println "The value of the atom has been changed") (println "old-state" old-state) (println "new-state" new-state))) (remove-watch x :watcher) |
1 | ;; References Variables |
Clojure Core Libraries
Clojure ships with a large number of core libraries. They can be found at https://clojuredocs.org/core-library