map
and reduce
let
You have already seen some functions, such as
count
,conj
,first
, andrest
. All the arithmetic we did used functions, as well:+
,-
,*
, and/
. What does it mean to be a function, though?
A function is an independent, discrete piece of code that takes in some values (called arguments) and returns a value.
Reference: Basics of Function
count
, conj
, first
+
, -
, *
, /
defn
specifies that we are defining a function.forward-right
is the name of this function.- The string on the next line is the documentation for the function, which explains what the function does. This is optional.
[turtle]
is the list of arguments. Here, we have one argument calledturtle
.(forward turtle 60) (right turtle 135)
is the body of the function. This is what executes when we use the function.
(defn forward-right
"Moves specified turtle forward and tilts its head"
[turtle]
(forward turtle 60)
(right turtle 135))
forward-right
function To use
forward-right
, we call the function, just like we’ve done with all the functions we’ve already used.
(forward-right :trinity) ;=> {:trinity {:angle 135}}
(forward-right :neo) ;=> {:neo {:angle 135}}
Functions can also take more than one argument. Let’s make a
forward-right-with-len
function that takes a forward length, in addition to the turtle.
(defn forward-right-with-len
"Given turtle and length, forward the turtle and tilts its head"
[turtle len]
(forward turtle len)
(right turtle 135))
(forward-right-with-len :trinity 90) ;=> {:trinity {:angle 135}}
(forward-right-with-len :neo 80) ;=> {:neo {:angle 135}}
walk.clj
forward-right
function (below) which appeared in the slide.walk.clj
forward-right
function and hit Eval Selection(forward-right :trinity)
on right REPL pane(defn forward-right
"Moves specified turtle forward and tilts its head"
[turtle]
(forward turtle 60)
(right turtle 135))
walk.clj
forward-right-with-len-ang
functon that takes three
arguments, turtle, len, and angle (extension of forward-right-with-len
)forward-right-with-len-ang
function and hit Reload Selection(forward-right-with-len-ang :trinity 60 120)
Function names are symbols, just like the symbols we used with
def
when assigning names to values.
Symbols have to begin with a non-numeric character, and they can contain alphanumeric characters, along with *, +, !, -, _, and ?. This flexibility is important with functions, as there are certain idioms we use.
Clojure has two type of functions:
- function that returns a value,
- function that returns true or false. The second type is called predicates.
In Clojure,
=
is a predicate function, which may be a surprising fact. Other than that, like many other computer languages, Clojure has predicate functions to test greater than, less than or such. Mostly predicate functions end with?
.
=
,not=
>
,<
,>=
,<=
true?
,false?
,empty?
,nil?
,vector?
,map?
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. That’s a complicated idea, also, may not make sense at first. Let’s look at an example and learn more about that.
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 inc [1 2 3]) ;=> (2 3 4)
(map (partial + 90) [0 30 60 90]) ;=> (90 120 150 180)
References: partial
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 str (turtle-names)) ;=> ":trinity:neo:oracle:cypher"
(reduce + [30 60 90]) ;=> 180
average
that takes a vector of maps.[{:angle 30} {:angle 90} {:angle 50}]
as input.Calculate average value of :angle.
map
, reduce
and count
.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:
(map (fn [t] (forward t 45)) (turtle-names))
;=> ({:trinity {:length 45}} {:neo {:length 45}} {:oracle {:length
45}} {:cypher {:length 45}})
(reduce (fn [x y] (+ x y)) [1 2 3]) ;=> 6
(reduce (fn [a b] (str a ", " b)) (map name (turtle-names)))
;=> "trinity, neo, oracle, cypher"
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
.
let
We can assign a name to value using
let
likedef
. When a name is assigned to a value, the name is called a symbol.
Reference: Assignment let
(let [mangoes 3
oranges 5]
(+ mangoes oranges))
;=> 8
let
example
This is the most complicated function we’ve seen so far, so let’s go through each step. First, we have the name of the function, the documentation string, and the arguments, just as in other functions
Next, we see
let
.let
takes a vector of alternating names and values.t1
is the first name, and we assign the result of(first names)
to it. We also assign the result of(last names)
tot2
.
After the vector of names and values, there is the body of the
let
. Just like a the body of a function, this executes and returns a value. Within thelet
,t1
andt2
are defined.
Go to
walk.clj
and writeopposite
function. Then, evaluateopposite
function at the last line of the function definition. Also, evaluate usage example ofopposite
function.
;; function definition
(defn opposite
"Given a collection of turtle names, moves two of them in different directions."
[names]
(let [t1 (first names)
t2 (last names)]
(forward t1 40)
(backward t2 30)))
;; function usage
(opposite (turtle-names))
Return to the first slide, or go to the curriculum outline.