- We use
defnto define a function.
- We give it a name so we may call it later i.e.
- A Vector is used to specify the function’s arguments i.e.
- A String can be provided for a description e.g.
- The body is the form (thing in parentheses) that follows i.e.
(+ x y)
- We call the function by creating a form with it’s name and arguments i.e.
(add 1 2)
(defn add ; name "Adds together two numbers" ; documentation [x y] ; arguments (+ x y)) ; body (add 1 2) ;=> 3 (add (add 1 2) 4) ;=> 7
Create a new function,
add-one, that takes a single argument
and adds one to it.
It should call our
Some of the most powerful functions you can use with collections can take other functions as arguments. This is one of the most magical things about Clojure and many other programming languages. It’s a complicated idea that may not make sense at first. Let’s look at an example to learn more about it.
Reference: Higher-order Function
mapis a function that takes another function, along with a collection. It calls the function provided to it on each member of the collection, then returns a new collection with the results of those function calls. This is a weird concept, but it is at the core of Clojure and functional programming in general.
(map count ["a" "abc" "abcdefg"]) ;=> (1 3 7) (map even? [0 1 2 3 4]) ;=> (true false true false true)
Let’s look at another function that takes a function. This one is
reduce, and it is used to turn collections into a single value.
reducetakes the first two members of the provided collection and calls the provided function with those members. Next, it calls the provided function again–this time, using the result of the previous function call, along with the next member of the collection.
reducedoes this over and over again until it finally reaches the end of the collection.
(reduce + [30 60 90]) ;=> 180 (reduce str ["h" "e" "l" "l" "o"]) ;=> "hello"
When you are creating functions, you may want to assign names to values in order to reuse those values or make your code more readable. Inside of a function, however, you should not use
def, like you would outside of a function. Instead, you should use a special form called
Reference: Assignment let
(defn average [values] (let [c (count values) s (reduce + values)] (/ s c))) (average [1.0 1.0 2.0 3.0 5.0]) ;=> 2.4
(remove even? [1 2 3 4 5 6]) ;=> (1 3 5) (defn less-than-10? [x] (< x 10)) (filter less-than-10? [8 9 10 11]) ;=> (8 9)
So far, all the functions we’ve seen have had names, like
reduce. However, functions don’t need to have names, just like values don’t need to have names. We call functions without names anonymous functions. An anonymous function is created with
fn, like so:
Reference: Anonymous Function
(fn [s1 s2] (str s1 " " s2))
Before we go forward, you should understand that you can always feel free to name your functions. There is nothing wrong at all with doing that. However, you will see Clojure code with anonymous functions, so you should be able to understand it.
(defn join-with-space [s1 s2] (str s1 " " s2))
Why would you ever need anonymous functions? Anonymous functions can be very useful when we have functions that take other functions. Such as
reduce, which we learned in Functions section. Let’s look at usage examples of anonymous functions:
(filter (fn [x] (< x 10)) [8 9 10 11]) ;=> (8 9)
(def one 1) (defn add-one [x] (+ x one)) ; depends on `one` having been declared (add-one 5) ;=> 6 (def one 2) (add-one 5) ;=> 7 oh dear :(
(println "I'm impure") ; writing to the console is a side-effect (rand) ; reading (from a random number generator) is too