Function Returns, Default Values

(Sections 3.3 - 3.4)

What a Function Returns

A Power Function

pow <- function(x,y) {
  x^y
}

Check to see that it works:

pow(2,3)
[1] 8

pow() Returns a Value

a <- pow(2,4)  # binds a to the value 16
cat("I have", a, "cats.")
I have 16 cats.

In R, a function returns the value of the final expression that it evaluates.

This Is Important!!

So let’s say it again:

In R,

a function returns

the value of the final expression

that it evaluates.

Example

f <- function(x) {
  2*x + 3
  45
  "hello"
  x^2
}

f(4)
[1] 16

Observe that:

  • 2*4 + 3, 45 and “hello” vanish
  • only \(4^2 = 16\) is returned

Forcing Return with return()

The function return() forces R to stop executing code in the body of the function:

g <- function(x) {
  val <- 3*x +7
  return(val)
  "Hello!"
}

g(1)
[1] 10

Optional Use of return()

The following two functions do the same thing:

f1 <- function(x) x^2
f2 <- function(x) return(x^2)

People with experience in other programming languages often like to end their functions with return(), but in R it’s not necessary.

A Talky Function

talkySquare <- function(x) {
  result <- x^2
  cat("The square of ", x, " is:  ", result, ".\n", sep = "")
  result  # returns the square!
}

This function announces the result, but also returns the result.

a <- talkySquare(4)
The square of 4 is:  16.
a
[1] 16

Side-Effects

The announcement of the square was a side-effect.

Side-Effect

Any result produced outside of the run-time environment of a function, other than the value that the function returns.

We’ll learn about run-time environments soon.

Practice

Recall g():

g <- function(x) {
  val <- 3*x +7
  return(val)
  "Hello!"
}

Rewrite g() so that it cats()s “Hello” to the Console but also returns val.

Printing

Grumpy Square

grumpySquare <- function(x) {
  "OK, OK, I'm getting to it ... "
  x^2
}

The grumpy message won’t show.

Could cat() the Message …

grumpySquare <- function(x) {
  cat("OK, OK, I'm getting to it ... ")
  x^2
}

grumpySquare(4)
OK, OK, I'm getting to it ... 
[1] 16

Could also print() the Message

grumpySquare <- function(x) {
  print("OK, OK, I'm getting to it ... ")
  x^2
}

grumpySquare(4)
[1] "OK, OK, I'm getting to it ... "
[1] 16

This is another way to force the side-effect.

Implicit Calls to print()

R will evaluate the following expression, and then print the result:

2 + 2
[1] 4

It’s as if we had written:

print(2 + 2)
[1] 4

In the body of a function, you need print() or cat() to make the result show in the Console. (Unless it’s the value of the final expression evaluated!)

What Does cat() Return?

cat() is used mainly for its side-effect: putting stuff out to the console.

But all functions return a value.

So what does cat() return?

Let’s Investigate

cat_return <- cat("Hello\n")
Hello
cat_return
NULL

So cat() returns NULL, which is R’s way of saying “nothing”. (But it is still a value.)

Default Values

Madhava Series for \(\pi\)

Sometime around the year 1400CE, the South Indian mathematician Madhava discovered the following infinite-series formula for \(\pi\), the ratio of the circumference to the diameter of a circle:

\[\pi = \frac{4}{1} - \frac{4}{3} + \frac{4}{5} - \frac{4}{7} +\frac{4}{9} - \cdots\]

In mathematics courses we learn to write the sum like this:

\[\pi = \sum_{k=1}^{\infty} (-1)^{k+1}\frac{4}{2k-1}.\]

Explanation

\[\pi = \sum_{k=1}^{\infty} (-1)^{k+1}\frac{4}{2k-1}.\]

  • The \(\Sigma\) sign stands for “sum”: it means that we plan to add up a lot of terms.
  • The expression \((-1)^{k+1}\frac{4}{2k-1}\) after the sum-sign stands for all of the terms that will be added up to make the infinite series.
  • Underneath the sum-sign, \(k=1\) says that in the expression we will start by letting \(k\) be 1.
  • If we let \(k=1\), then the expression becomes \[(-1)^2\frac{4}{2 \cdot 1 -1} = \frac{4}{1} = 4,\] the first term in the series.

Explanation

\[\pi = \sum_{k=1}^{\infty} (-1)^{k+1}\frac{4}{2k-1}.\]

  • If we let \(k=2\), then the expression becomes \[(-1)^3\frac{4}{2 \cdot 2 -1} = -\frac{4}{3}\] the second term in the series.
  • If we let \(k=3\), then the expression becomes \[(-1)^4\frac{4}{2 \cdot 3 -1} = \frac{4}{5}\] the third term in the series.

Explanation

\[\pi = \sum_{k=1}^{\infty} (-1)^{k+1}\frac{4}{2k-1}.\]

  • The \(k = \infty\) above the sum-sign says that we are to keep on going like this, increasing \(k\) by 1 every time, without stopping.
  • In this way we get the entire infinite series.

The more terms you add up before stopping, the closer to \(\pi\) you will get.

Try It!

\[\pi \approx \sum_{k=1}^{4} (-1)^{k+1}\frac{4}{2k-1}.\]

4 - 4/3 + 4/5 - 4/7
[1] 2.895238

Not so great …

Try Some More!

\[\pi \approx \sum_{k=1}^{8} (-1)^{k+1}\frac{4}{2k-1}.\]

4 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + 4/13 - 4/15
[1] 3.017072

Better …

Try it Again?

\[\pi \approx \sum_{k=1}^{100} (-1)^{k+1}\frac{4}{2k-1}.\]

No way: too much typing!

Function to Approximate \(\pi\)

# sum the first n terms of the Madhava series
madhavaPI <- function(n) {
  # make a vector of all of the k's we need:
  k <- 1:n
  # make a vector of the first n terms of the sum:
  terms <- (-1)^(k+1)*4/(2*k-1)
  # return the sum of the terms:
  sum(terms)
}

Try it Out

madhavaPI(n = 1000)
[1] 3.140593

Study How the Function Works

To watch a function do its work step-by-step, run it in “debug”mode:

debug(madhavaPI)
madhavaPI(n = 5)

To set the function to run normally again, run:

undebug(madhavaPI)

Inconvenience: We Must Provide n

We must provide an argument for the parameter n, or there will be an error:

madhavaPI()
Error in madhavaPI(): argument "n" is missing, with no default

Default Values

We can get around this by providing a default value for the parameter:

madhavaPI <- function(n = 10000) {
  k <- 1:n
  terms <- (-1)^(k+1)*4/(2*k-1)
  sum(terms)
}
madhavaPI()  # will use n = 10000
[1] 3.141493
madhavaPI(2000) # overrides default, uses n = 2000
[1] 3.141093

Definition

Default Value

A value for a parameter of a function that is provided when the function is defined.

This value will become the value assigned to the parameter when the function is called, unless the user explicitly assigns some other value as the argument.

Practice

Recall manyCat():

manyCat <- function(word, n) {
  wordWithNewline <- paste(word, "\n", sep = "")
  lines <- rep(wordWithNewline, times = n)
  cat(lines, sep = "")
}

Rewrite it so that:

  • the default value for word is “Kansas”, and
  • the default value for n is 3