Exercise 1

The Problem

Write a function called pattern() that when given a character will will print out the character in a pattern like this:

*
**
***
**
*

That is: a row of one, then a row of two, then a row of three, then a row of two, and finally a row of one.

The function should take one parameter called char. The default value of this parameter should be *. Typical examples of use should be as follows:

pattern()
## *
## **
## ***
## **
## *
pattern(char = "x")
## x
## xx
## xxx
## xx
## x

A Solution

Here is the code for the required function:

pattern <- function(char = "*") {
  cat(rep(char, times = 1), "\n", sep = "")
  cat(rep(char, times = 2), "\n", sep = "")
  cat(rep(char, times = 3), "\n", sep = "")
  cat(rep(char, times = 2), "\n", sep = "")
  cat(rep(char, times = 1), "\n", sep = "")
}

Let’s make sure that it works:

pattern()            # using the default character *
## *
## **
## ***
## **
## *
pattern(char = "x")  # using another character
## x
## xx
## xxx
## xx
## x

It seems awkward to have to repeat those calls to cat() in the body of pattern(). And what if we want a 1-2-3-4-3-2-1 pattern instead? In Chapter 4 we will see how to write a more general solution.

Exercise 2

The Problem

Write a function called reverse() that, given any vector, returns a vector with the elements in reverse order. It should take one parameter called vec. The default value of vec should be the vector c("Bob", "Marley"). Typical examples of use should be:

reverse()
## [1] "Marley" "Bob"
reverse(c(3,2,7,6))
## [1] 6 7 2 3

A Solution

This problem looks more difficult than Exercise 1, so we should take our time and build up to the function in a sequence of small steps.

Step One: Reverse a Specific Vector

In order to reverse the following vector of length five:

firstFiveLetters <- c("a", "b", "c", "d", "e")

we simply subset the vector in reverse order:

firstFiveLetters[5:1]
## [1] "e" "d" "c" "b" "a"

Step Two: Generalize

In the above code we can get the 5 as length(firstFiveLetters), the following code would have worked just as well:

firstFiveLetters[length(firstFiveLetters):1]
## [1] "e" "d" "c" "b" "a"

Let’s try this out on some other vector called vec:

vec <- c("Raj", "Bettina", "Salome")  # here you can set the vector
vec[length(vec):1]                    # this line does the work of reversing
## [1] "Salome"  "Bettina" "Raj"

Step Three: Encapsulate Into a Function

Now we see how to write our function. The vec in first line of the the previous block of code becomes the parameter of the function. The line of code that does the work of the function goes into the function-body. Here is the result:

reverse <- function(vec = c("Bob", "Marley")) {
  vec[length(vec):1]
}

Step Four: Test the Function

Let’s try it out to make sure it works:

reverse() # should reverse c("Bob", "Marley")
## [1] "Marley" "Bob"
reverse(c("Raj", "Bettina", "Salome"))
## [1] "Salome"  "Bettina" "Raj"

Looks good!

Moral: If you can’t see how to write a function right away, then follow the four-step process above!

Exercise 3

The Problem

A vector is said to be a palindrome if reversing its elements yields the same vector. Thus, c(3,1,3) is a palindrome, but c(3,1,4) is not a palindrome.

Write a function called isPalindrome() that, when given any vector, will return TRUE if the vector is a palindrome and FALSE if it is not a palindrome. The function should take a single parameter called vec, with no default value. Typical examples of use should be:

isPalindrome(vec = c("Bob", "Marley", "Bob"))
## [1] TRUE
isPalindrome(c(3,2,7,4,3))
## [1] FALSE

A Solution

Again, let’s follow the four-step process.

Step One: Determine Whether a Specific Vector is a Palindrome

A vector is a palindrome provided that when you reverse it the result is equal to the original vector at all places. Let’s take a specific vector:

myLittleVector <- c(3,2,7,4,3)

Now let’s check to see if it’s equal to its reverse:

myLittleVector == reverse(myLittleVector)
## [1]  TRUE FALSE  TRUE FALSE  TRUE

Note that this logical vector gives a TRUE when an element of myLittleVector is equal to the corresponding element of the reverse of the vector. The vector is built up like this:

How the logical vector was determined.
original vector reversed vector elements same? myLittleVector == reverse(myLittleVector)
3 3 yes TRUE
2 4 no FALSE
7 7 yes TRUE
4 2 no FALSE
3 3 yes TRUE

Clearly, a vector will be a palindrome if all of the elements of the logical vector are TRUE.

Recall that the function all() takes a logical vector and returns TRUE if and only if all of the elements of the logical vector are TRUE. Thus, the following code does the work of checking whether myLittleVector is a palindrome:

all(myLittleVector == reverse(myLittleVector))
## [1] FALSE

Step Two: Generalize

We can write a two line program that solves the problem for any vector:

vec <- c("b", "c", "f", "c", "b")  # a palindrome!
all(vec == reverse(vec))
## [1] TRUE

All user would have to do, in order to work with another vector, if to change the binding on the first line.

Step Three: Encapsulate Into a Function

Now we see how to write our function:

isPalindrome <- function(vec = c("Bob", "Marley")) {
  all(vec == reverse(vec))
}

Let’s try it out:

isPalindrome(vec = c("Bob", "Marley", "Bob"))  # should be TRUE
## [1] TRUE
isPalindrome(c(3,2,7,4,3))                     # should be FALSE
## [1] FALSE
isPalindrome()                                 # default should return FALSE
## [1] FALSE

Exercise 4

The Problem

The eighteenth-century mathematician Leonhard Euler discovered that:

\[\frac{\pi^2}{6} = \sum_{k=1}^{k=\infty} \frac{1}{k^2}.\] It follows that \[\pi = \sqrt{\left(\sum_{k=1}^{k=\infty} \frac{6}{k^2}\right)}.\] Use this fact to write a function called eulerPI() that will approximate \(\pi\). The function should take a single parameter n, which is the number of terms in the infinite series that are to be summed to make the approximation. The default value of n should be 10,000.

A Solution

Remember how the madhavaPI() function worked:

madhavaPI <- function(n = 1000000) {
  k <- 1:n
  terms <- (-1)^(k+1)*4/(2*k-1)
  sum(terms)
}

For our approximation, we need the square root of the sum of the first n terms, not just the sum of the first n terms. Hence we will try:

eulerPi <- function(n = 10000) {
  k <- 1:n
  terms <- 6/k^2
  sqrt(sum(terms))
}

Let’s check it out:

eulerPi(n = 10)
## [1] 3.049362
eulerPi()         #using our default value of 10000
## [1] 3.141497

Looks good!

Caution

Make sure that your code correctly represents the required order of arithmetical operations. We know that

\[\pi = \sqrt{\left(\sum_{k=1}^{k=\infty} \frac{6}{k^2}\right)}.\]

That is, we sum terms, then we take the square root. It is not possible to switch the order, that is:

\[\pi \neq \sum_{k=1}^{k=\infty} \sqrt{\frac{6}{k^2}}.\]

Thus the following function would deliver incorrect approximations to \(\pi\):

eulerPiWrong <- function(n = 10000) {
  k <- 1:n
  sum(sqrt(6/k^2))
}

We can see this with a quick check:

eulerPiWrong()
## [1] 23.97464