Functions

  • What are functions?
  • Naming functions
  • [bonus section] Functions that take other functions
    • map and reduce
  • [bonus section] Anonymous function
  • [bonus section] Assignment: let

What are functions?

You have already seen some functions, such as count, conj, first, and rest. 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
  • +, -, *, /
  • A piece of code that takes values and returns a value

An example function

  • 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 called turtle.
  • (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))

How to use 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}}

A function with multiple arguments

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}}

EXERCISE 1: Move turtles using function

  • Go to walk.clj
  • Write a function, forward-right appeared in the slide.
(defn forward-right
  "Moves specified turtle forward and tilts its head"
  [turtle]
  (forward turtle 60)
  (right turtle 135))
  • On the last line of the function, hit Ctrl + Enter or Cmd + Enter (no shift key) to evaluate the function
  • Write a line that uses the function, for example: (forward-right :trinity)
  • Evaluate the line at least 8 times

EXERCISE 2: Move turtles using function with parameters

  • Go to walk.clj
  • Write a function, forward-right-with-len-ang that takes three arguments, turtle, len, and angle (extension of forward-right-with-len)
  • On the last line of the function, evaluate it by hitting Cmd + Enter or Ctrl + Enter
  • Write a line that uses the function, for example: (forward-right-with-len-ang :trinity 60 120)
  • Evaluate the line many times by hitting Cmd + Enter or Ctrl + Enter

Naming functions

Names are Symbols

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.

Two types of functions

Clojure has two type of functions:

  1. function that returns a value,
  2. function that returns true or false. The second type is called predicates.
Predicate function examples

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?

[Bonus section]

Functions that take other functions

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 name (turtle-names)) ;=> ("trinity" "neo" "oracle" "cypher")
(map (partial + 90) [0 30 60 90]) ;=> (90 120 150 180)

References: name, partial

reduce function

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.

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

EXERCISE 3 [BONUS]: Find the average

  • Create a function called average that takes a vector of maps.
  • Use [{:angle 30} {:angle 90} {:angle 50}] as input.
  • Calculate average value of :angle.

  • Hint: You will need to use the functions map, reduce and count.

[Bonus section]

Anonymous functions

Functions without names

So far, all the functions we’ve seen have had names, like + and str and 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))

vs. not anonymous functions

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))

Anonymous function usage examples

Why would you ever need anonymous functions? Anonymous functions can be very useful when we have functions that take other functions. Such as map or reduce, 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"

[Bonus section]

Assignment: 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 called let.

Assigning names to values: let

We can assign a name to value using let like def. 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) to t2.

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 the let, t1 and t2 are defined.

Go to walk.clj and write opposite function. Then, evaluate opposite function at the last line of the function definition. Also, evaluate usage example of opposite 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.