count
, conj
, first
+
, -
, *
, /
- We use
defn
to define a function.- We give it a name so we may call it later i.e.
add
- A Vector is used to specify the function’s arguments i.e.
[x y]
- A String can be provided for a description e.g.
"Adds together..."
- 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 add
function.
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
map
function
map
is 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)
reduce
functionLet’s look at another function that takes a function. This one is
reduce
, and it is used to turn collections into a single value.
reduce
takes 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.reduce
does 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"
average
reduce
and count
.let
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 calledlet
.
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
even?
(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
+
andstr
andreduce
. 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 withfn
, 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
map
orreduce
, 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
Return to the first slide, or go to the curriculum outline.