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

Let’s use the step-by-step procedure. Assume that we have already finished the first step (reading the directions carefully).

Step Two: Write a Program to Solve for a Specific Example

Let’s set up an example, using the same name as the parameter for the desired function:

char <- "x"

Now let’s think about how to get the lines, one at a time. The first line is easy enough:

cat(char)
## x

For the second line, how about this:

cat(char, char)
## x x

Nope, not quite: we don’t want that space between the two chars. So let’s try this instead:

cat(char, char, sep = "")
## xx

Much better.

But wait: the more characters we have to put on a line, the more tiring it will be to type a bunch of chars separted by commas. Let’s use rep() to abbreviate our work:

cat(rep(char, times = 2), sep = "")
## xx

Then we can easily get the line with three characters:

cat(rep(char, times = 3), sep = "")
## xxx

We go on to the next line, which is back to two characters:

cat(rep(char, times = 2), sep = "")
## xx

Then the line with one character:

cat(char)
## x

So it seems that the “work” is done by the following sequence of commands:

cat(char)
cat(rep(char, times = 2), sep = "")
cat(rep(char, times = 3), sep = "")
cat(rep(char, times = 2), sep = "")
cat(char)

Step Three: Encapuslate Into a Function

Here’s our first stab at the function itself:

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

Note that we are making the default "*", as required by the specs.

Step Four: Test the Function

Let’s try it out:

pattern(char = "m")
## mmmmmmmmm

Ooops! All of the characters are on the SAME line!

Back to the drawing board …

Step Two Again: Program for a Specific Example

What went wrong in the function? After all, each line of the program seemed to give a different line of output.

The thing is that when you run any expression from “top level” (i.e, in the Console) then the Console will automatically put you on a new line when you are done. But when a function is running the expressions of the function are evaluated one after another, without any reference to the Console. You need to specify the move to a new line in the commands themselves if you want to see new lines when the function runs.

So let’s try inserting them:

char <- "x"
cat(char, "\n")
## x
cat(rep(char, times = 2), "\n", sep = "")
## xx
cat(rep(char, times = 3), "\n", sep = "")
## xxx
cat(rep(char, times = 2), "\n", sep = "")
## xx
cat(char, "\n")
## x

This seems to work! Just for fun, let’s make all of the commands follow the same pattern:

cat(rep(char, times = 1), "\n", sep = "")
## x
cat(rep(char, times = 2), "\n", sep = "")
## xx
cat(rep(char, times = 3), "\n", sep = "")
## xxx
cat(rep(char, times = 2), "\n", sep = "")
## xx
cat(rep(char, times = 1), "\n", sep = "")
## x

This works, too.

Step Three Again: Encapsulate Your Work

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 = "")
}

Step Four Again: Test

Let’s make sure that it works:

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

Works great!

Step Five: Read the Directions Again.

OK, I read them again, and it seems that I have done everything I needed to do. I’m done

Note

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 3

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 is just as difficult than Exercise 1, so we should take our time and build up to the function in a sequence of small steps. Again we will assume that we have done Step One, reading the directions.

Step Two: Program to Reverse a Specific Vector

In order to reverse the following vector of length five:

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

we simply subset the vector in reverse order:

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

But we need the program to work for vectors of any length.

So how about this idea:

n <- length(vec)
vec[n:1]
## [1] "e" "d" "c" "b" "a"

Looks good!

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")) {
  n <- length(vec)
  vec[n: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!

Exercise 4

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 step-wise process, assuming as always that we have read the directions.

Step Two: 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:

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

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

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

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

How the logical vector was determined.
original vector reversed vector elements same? vec == reverse(vec)
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 vec is a palindrome:

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

Step Three: Encapsulate Into a Function

Now we see how to write our function:

isPalindrome <- function(vec) {
  all(vec == reverse(vec))
}

Note that this time the directions said that there should be NO default value!

Step Four: Test

Let’s try it out on several examples:

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

Note

R has a function called identical() that returns TRUE when two objects are the same and FALSE otherwise. For example:

identical(c("a", "b"), letters[1:2])
## [1] TRUE
identical(letters, LETTERS)
## [1] FALSE

One could use identical() to write another version of isPalindrome():

isPalindrome <- function(vec) {
  identical(vec, reverse(vec))
}
isPalindrome(vec = c("Bob", "Marley", "Bob"))  # should be TRUE
## [1] TRUE
isPalindrome(c(3,2,7,4,3))                     # should be FALSE
## [1] FALSE

Exercise 5

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

We’ll probably end up with a similar function, but let’s be careful and follow the steps.

Step Two: Program to Solve for a Specific Example

Let’s set up a specific number of terms to add. How about ten terms?

n <- 10

Noe we make the term numbers:

k <- 1:n

Let’s print this out to see if it looks right:

k
##  [1]  1  2  3  4  5  6  7  8  9 10

Looks good. Now we go for the actual terms that we need to add:

terms <- 6 / k^2

Notice the 6. We are following the formula for \(\pi\) given in the directions!

Now we add the terms, and then take the square root of them:

sqrt(sum(terms))
## [1] 3.049362

It’s not to close to \(\pi\), but we only used ten terms, Let’s check our program with 1000 terms:

n <- 1000
k <- 1:n
terms <- 6 / k^2
sqrt(sum(terms))
## [1] 3.140638

This has got to be right!

Step Three: Encapsulate

Here’s our function:

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

Step Four: Test

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