Functions
Prerequisites: Conditionals
defn defines a named function:
(defn greet
"Returns a greeting for name."
[name]
(str "Hello, " name "!"))
(defn add [a b]
(+ a b))
(println (greet "Ada"))
(println (add 2 3))
Hello, Ada!
5
- The optional string after the name is a docstring, shown by
(doc greet). - Parameters go in a vector
[name]. - The value of the last expression is returned — there's no
returnkeyword.
Anonymous functions
(def square (fn [x] (* x x)))
(println (square 5))
(println (map (fn [x] (* x 10)) [1 2 3]))
(println (map #(* % 10) [1 2 3])) ; short form
(println (map #(+ %1 %2) [1 2 3] [10 20 30]))
25
(10 20 30)
(10 20 30)
(11 22 33)
(fn [params] body)creates a function without a name.#( ... )is shorthand:%is the single argument,%1,%2are numbered ones. Keep#()for tiny functions; don't nest them.- Functions are values: you can store them, pass them, and return them.
Multiple arities
(defn greet
([] (greet "world"))
([name] (greet name "Hello"))
([name greeting] (str greeting ", " name "!")))
(println (greet))
(println (greet "Ada"))
(println (greet "Ada" "Welcome"))
Hello, world!
Hello, Ada!
Welcome, Ada!
One function can have several bodies for different numbers of arguments — Clojure's way of providing default values.
Variadic functions
(defn total [& numbers]
(apply + numbers))
(defn log [level & parts]
(println (str "[" level "]") (apply str parts)))
(println (total 1 2 3 4))
(println (total))
(log "INFO" "Server started on port " 8080)
10
0
[INFO] Server started on port 8080
& numbers collects the remaining arguments into a sequence (or nil
if there are none). apply calls a function with the items of a
collection as its arguments.
Pre- and postconditions
(defn percentage [part whole]
{:pre [(number? part) (pos? whole)]}
(* 100.0 (/ part whole)))
(println (percentage 1 4))
(println (try (percentage 1 0)
(catch AssertionError e (.getMessage e))))
25.0
Assert failed: (pos? whole)
A map of :pre (and :post) checks right after the parameters guards a
function's inputs (and outputs).
Common mistakes
- Calling a function with the wrong number of arguments throws an
ArityException— the message tells you the function and the count. - Wrapping a value in extra parentheses:
((+ 1 2))tries to call the number 3.
Practice
Write palindrome? that ignores case, and test it with "Level" and
"Clojure".
Show solution
(require '[clojure.string :as str])
(defn palindrome? [s]
(let [lower (str/lower-case s)]
(= lower (str/reverse lower))))
(println (palindrome? "Level"))
(println (palindrome? "Clojure"))
true
false