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
Let’s use the step-by-step procedure. Assume that we have already finished the first step (reading the directions carefully).
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 char
s 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)
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.
Let’s try it out:
pattern(char = "m")
## mmmmmmmmm
Ooops! All of the characters are on the SAME line!
Back to the drawing board …
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.
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
Works great!
OK, I read them again, and it seems that I have done everything I needed to do. I’m done
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.
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
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.
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!
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]
}
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!
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
Again, let’s follow the step-wise process, assuming as always that we have read the directions.
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:
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
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!
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
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
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.
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.
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!
Here’s our function:
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!
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