map and reduceletYou 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+, -, *, /
defnspecifies that we are defining a function.forward-rightis 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-lenfunction 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.cljforward-right function (below) which appeared in the slide.walk.cljforward-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.cljforward-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
defwhen 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
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 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.
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 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
+andstrandreduce. 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
maporreduce, 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"
letWhen 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.
letWe can assign a name to value using
letlikedef. 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.lettakes a vector of alternating names and values.t1is 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,t1andt2are defined.
Go to
walk.cljand writeoppositefunction. Then, evaluateoppositefunction at the last line of the function definition. Also, evaluate usage example ofoppositefunction.
;; 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.