41  41. for loops

A “for loop” is similar to a “while loop” in that it causes a body of code to be repeated more than once. The for loop is DIFFERENT from the while loop in HOW it accomplishes the repetition.

If you need code to be repeated you can ALWAYS use a WHILE loop. However, sometimes the while loop can be a little confusing. A “for loop” is an alternative to a “while loop” that is usually easier to understand than the equivalent code that uses a while loop (we’ll explain why later).

Unfortunately, you cannot always use a FOR loop. A for loop can only be used if it’s possible to know before the loop starts exactly how many times the loop will need to iterate (i.e. “go around”). The reason for this will be explained later.

It’s probably easiest to understand how the for loop works if you examine the example code that appears below. However, the following is an attempt to explain in words how the for loop works. I recommend that you look at the examples below and then come back and read this more detailed explanation.

Code of “for loops” is arranged in the following format.

    for ( VARIABLE in  VECTOR_OR_LIST  ){
       
       # This area between the curly braces is the "body" of the loop.
       # The code in the "body" is repeated once for each value in the VECTOR_OR_LIST.
       # Each time this code is executed the VARIABLE is automatically
       # set to the next value in the VECTOR_OR_LIST
       
       # Code in the body may refer to the VARIABLE but there is no
       # requierment that they do.

    }

NOTES:

  1. VARIABLE is the name of a variable. This variable is created just for the purpose of the for loop. You cannot refer to this variable outside of the code for the for loop. (If the same name is used for a variable outside of the for loop it is a different variable).

  2. VECTOR_OR_LIST is a vector or a list. This could be a variable name that was created before the for loop started or it could be the result a function that creates a vector or a list.

  3. The code executes in the following way:

    step 1. The first value in the vector (or list) is assigned to the variable.

    step 2. The body of the loop (i.e. code between {curly braces}) is executed. [The code in the body may refer to the variable but it doesn’t have to.]

    step 3. After the {body} finishes executing once, the next value from the vector (or list) is assigned to the variable.

    step 4. The body of the for loop is then executed again. However, this time the value of the variable is the 2nd value from the vector (or list).

    step 5. The for loop keeps replacing the value of the variable with the next value from the vector (or list) and then doing the code in the {body} until it runs out of values from the vector (or list).

    step 6. After all values from the vector (or list) have been processed, the loop is finished and execution continues with the line after the end of the body of the loop - i.e. the line after the “}”

41.1 Example - countdown

Everything you need to know about a for loop is in the first line of the loop (i.e. the line that starts with the word “for”). In the following example, the first line says: for(num in 10:1). See below for more explanation.

countdown <- function (){
  for (num in 10:1){  # each time through the loop another value from 10:1 is assigned to num
    cat(num," ")      # display num followed by a space
    Sys.sleep(1.5)   # sleep for 1.5 seconds
  }
  
  cat("blastoff!")    # after the loop has finished, display "blastoff!"
}
countdown()
10  9  8  7  6  5  4  3  2  1  blastoff!

In this example, num is a variable and 10:1 is a vector, i.e. c(10,9,8,7,6,5,4,3,2,1). The for loop automatically assigns a value from the vector to the variable. Then it does the body. Then it assigns the next value from the vector to the variable and does the body again. It keeps doing this until all of the values have been processed by the body of the loop. Specifically, in this example:

  • The 1st value from the vector (i.e. 10) is assigned to the variable, num Then the body of the loop is executed.

  • The 2nd value from the vector (i.e. 9) is assigned to the variable, num Then the body of the loop is executed.

  • The 3rd value from the vector (i.e. 8) is assigned to the variable, num Then the body of the loop is executed.

  • etc …

  • The 10th value from the vector (i.e. 1) is assigned to the variable, num Then the body of the loop is executed.

  • At this point, the loop is finished and the function continues with the code after the body of the loop.

41.2 You can rewrite any for loop as a while loop

A for loop can ALWAYS be rewritten as a while loop (but not necessarily not vice versa - see more discussion below). The following code rewrites the above example to use a while loop.

countdownWithWhile <- function (){
  num = 10          # setup the variables to be used in the condition of the while
  
  while(num >= 1){  # condition that's TRUE when loop should run and FALSE when loop should end
    cat(num, " ")
    Sys.sleep(0.25)

    num <- num - 1  # change some variable that is part of the condition
  }                 # END OF WHILE - code below will only happen after while finishes
  
  cat("blastoff!")  
}

countdownWithWhile()
10  9  8  7  6  5  4  3  2  1  blastoff!

A while loop version is usually a little harder to understand than an equivalent for loop.

To understand how many times a for loop will iterate (i.e. go around) you just have to look at the first line of the for loop.

By contrast, to understand how a while loop works, you also have to look at

  • the first line of the loop
  • the code before the while loop that sets up the variables that the loop needs and
  • you also have to track how the variables used in the condition of the while loop are changed by the code in the body of the while loop.

A for loop is usually easier to understand because, all of this info is found in the first line of the for loop.

41.3 A “for loop” can use ANY vector or list

A “for loop” can use ANY vector or list

Other examples of vectors - character and logical vectors - numeric vectors that don’t count by ones

countByTwos_for = function(){
  vec = seq(2,10, by=2)
  for ( num in vec){
    cat("I like number", num, "\n")
  }
}
countByTwos_for()
I like number 2 
I like number 4 
I like number 6 
I like number 8 
I like number 10 

The following is the same example, rewritten to use a while loop.

# same example with a while loop
countByTwos_while = function(){
  num = 2
  while ( num <= 10){
    cat("I like number", num, "\n")
    num = num + 2
  }
}
countByTwos_while()
I like number 2 
I like number 4 
I like number 6 
I like number 8 
I like number 10 

41.3.1 Example: A for loop with a character vector

The following uses a character vector in the for loop:

#.................
# With a for loop
#.................
anotherExample_for = function( foods ){

  cat("Hello. \n")
  
  for (item in foods) {
    cat(item, "is yummy.\n")
    cat("Everyone likes", item, ".\n")
    cat("I hope we have", item, "for supper!\n\n")
  }
  
  cat("bye bye.\n")
}

anotherExample_for (c("pizza", "french fries", "burger", "chicken", "ice cream"))
Hello. 
pizza is yummy.
Everyone likes pizza .
I hope we have pizza for supper!

french fries is yummy.
Everyone likes french fries .
I hope we have french fries for supper!

burger is yummy.
Everyone likes burger .
I hope we have burger for supper!

chicken is yummy.
Everyone likes chicken .
I hope we have chicken for supper!

ice cream is yummy.
Everyone likes ice cream .
I hope we have ice cream for supper!

bye bye.
anotherExample_for (c("cake", "lasanga", "chullent"))
Hello. 
cake is yummy.
Everyone likes cake .
I hope we have cake for supper!

lasanga is yummy.
Everyone likes lasanga .
I hope we have lasanga for supper!

chullent is yummy.
Everyone likes chullent .
I hope we have chullent for supper!

bye bye.
anotherExample_for (character(0))
Hello. 
bye bye.
anotherExample_for( list("apple", "orange"))   # for loops also work with lists
Hello. 
apple is yummy.
Everyone likes apple .
I hope we have apple for supper!

orange is yummy.
Everyone likes orange .
I hope we have orange for supper!

bye bye.

41.3.2 same example with a while loop

As we said above we can always rewrite a for loop as a while loop (but necessarily not vice versa - see more discussion below). The following shows the same example but this time with a while loop.

The following code uses a while loop to process each position in the vector (or the list). This is what the for loop actually does for you, but we can do it ourselves.

The following shows a general way to convert ANY for loop into a while loop. We highlighted the lines of code in this version that are different from the previous version. All other lines of code are EXACTLY the same.

anotherExample_while = function( foods ){
  
  cat("Hello. \n")
  
  position = 1                    # new line
  while(position<=length(foods)){ # new line
    
    item = foods[[position]]  # new line; [[ ]] works with both lists and vectors
    
    cat(item, "is yummy.\n")
    cat("Everyone likes", item, ".\n")
    cat("I hope we have", item, "for supper!\n\n")
    
    position = position + 1       # new line
  }
  cat("bye bye.\n")
}

# This produces the exact same results as the for loop
anotherExample_while (c("pizza", "french fries", "burger", "chicken", "ice cream"))
Hello. 
pizza is yummy.
Everyone likes pizza .
I hope we have pizza for supper!

french fries is yummy.
Everyone likes french fries .
I hope we have french fries for supper!

burger is yummy.
Everyone likes burger .
I hope we have burger for supper!

chicken is yummy.
Everyone likes chicken .
I hope we have chicken for supper!

ice cream is yummy.
Everyone likes ice cream .
I hope we have ice cream for supper!

bye bye.
anotherExample_while (c("cake", "lasanga", "chullent"))
Hello. 
cake is yummy.
Everyone likes cake .
I hope we have cake for supper!

lasanga is yummy.
Everyone likes lasanga .
I hope we have lasanga for supper!

chullent is yummy.
Everyone likes chullent .
I hope we have chullent for supper!

bye bye.
anotherExample_while (character(0))
Hello. 
bye bye.
anotherExample_while( list("apple", "orange")) # careful 
Hello. 
apple is yummy.
Everyone likes apple .
I hope we have apple for supper!

orange is yummy.
Everyone likes orange .
I hope we have orange for supper!

bye bye.

41.4 Recipe to convert any for loop into a while loop

Any code with a “for loop” can be converted to equivalent code with a while loop by following these steps:

  • step 1: before the while loop copy the vector (or list) from the for loop in to a new variable, eg. vecOrList

  • step 2: create a variable for the position in the vector (or list) to be processed at each iteration through the body of the loop

  • step 3: write the while loop with the condition:
    while ( position <= length(vecOrList) )

  • step 4: Write a line at the end of the body of code for the while loop that increments (i.e. adds 1 to) the position variable

We demonstrate this by following these steps to rewrite the code from the countByTwos_for function to an equivalent function with a while loop.

Rewrite the for countByTwos function to use a while loop in a way that we can apply the same approach to convert ANY for loop into an equivalent while loop. Note that this version is not the same as the previous version that used a while loop. Both versions work, but the previous version is probably easier to understand. The approach taken with this version can be applied to any for loop.

countByTwos_while_version2 = function(){
 
 # step 1: copy the vector from the for loop in to a variable
 vec = seq(2,10, by=2)   
 
 # step 2: create a variable for the position in the vector (or list)
 #         to be processed at each iteration through the body of the loop
 position = 1
 
 while ( position <= length(vec)){  # step 3: write the condition for the while
  
  cat("I like number", vec[position], "\n")
  
  # step 4: at the end of the body of the loop add one to the position
  #         variable
  position = position + 1
 }
}
countByTwos_while_version2()
I like number 2 
I like number 4 
I like number 6 
I like number 8 
I like number 10 

41.5 — Practice —

##################################################################.
# QUESTION
##################################################################.
# 
# Write a function that takes a matrix, m, as an argument. 
# The function should return a new matrix that
# 
#    multiplies the 1st row by 10
#    multiplies the 2nd row by 100
#    multiplies the 3rd row by 1000
#    etc ... for all rows of the matrix
#
# (a) - Write the function using a for loop
# (b) - Write the function using a while loop
#
##################################################################.
# ANSWER - for loop

multRows_for = function ( m ) {
 
 multiplier = 10
 for(row in    1:nrow(m) ){
  
  m[row,] = m[row,] * multiplier
  
  multiplier = multiplier * 10
 }
 
 return(m)
}
# Test the answer:
#
# Here is some data to start you off. The code should work 
# will all possible matrices. Here are some to start you off in 
# testing your work. 
m = matrix(seq(1, 18, 1) , nrow=3, ncol=6)
m
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    4    7   10   13   16
[2,]    2    5    8   11   14   17
[3,]    3    6    9   12   15   18
multRows_for(m)
     [,1] [,2] [,3]  [,4]  [,5]  [,6]
[1,]   10   40   70   100   130   160
[2,]  200  500  800  1100  1400  1700
[3,] 3000 6000 9000 12000 15000 18000
# Another example
set.seed(100)  # you can pick any seed 
m = matrix(trunc(runif(min=1, max=5, 15)), nrow=5, ncol=3)
m
     [,1] [,2] [,3]
[1,]    2    2    3
[2,]    2    4    4
[3,]    3    2    2
[4,]    1    3    2
[5,]    2    1    4
multRows_for(m)
      [,1]  [,2]  [,3]
[1,] 2e+01 2e+01 3e+01
[2,] 2e+02 4e+02 4e+02
[3,] 3e+03 2e+03 2e+03
[4,] 1e+04 3e+04 2e+04
[5,] 2e+05 1e+05 4e+05
# ANSWER - for loop

multRows_while = function ( m ) {
 
 multiplier = 10
 row = 1
 while(row <= nrow(m)){
  m[row,] = m[row,] * multiplier
  
  multiplier = multiplier * 10
  
  row = row + 1
 }
 
 return(m)
}
# Test the answer:
#
# Here is some data to start you off. The code should work 
# will all possible matrices. Here are some to start you off in 
# testing your work. 
m = matrix(seq(1, 18, 1) , nrow=3, ncol=6)
m
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    4    7   10   13   16
[2,]    2    5    8   11   14   17
[3,]    3    6    9   12   15   18
multRows_while(m)
     [,1] [,2] [,3]  [,4]  [,5]  [,6]
[1,]   10   40   70   100   130   160
[2,]  200  500  800  1100  1400  1700
[3,] 3000 6000 9000 12000 15000 18000
# Another example
set.seed(100)  # you can pick any seed 
m = matrix(trunc(runif(min=1, max=5, 15)), nrow=5, ncol=3)
m
     [,1] [,2] [,3]
[1,]    2    2    3
[2,]    2    4    4
[3,]    3    2    2
[4,]    1    3    2
[5,]    2    1    4
multRows_while(m)
      [,1]  [,2]  [,3]
[1,] 2e+01 2e+01 3e+01
[2,] 2e+02 4e+02 4e+02
[3,] 3e+03 2e+03 2e+03
[4,] 1e+04 3e+04 2e+04
[5,] 2e+05 1e+05 4e+05
##################################################################.
# QUESTION
##################################################################.
# 
# Write a function that takes a matrix, m, as an argument. 
# The function should return a new matrix that
# 
#    adds 2 (i.e. 1+1) to the value in position 1,1 
#    adds 3 (i.e. 1+2) to the value in position 1,2 
#    adds 4 (i.e. 1+3) to the value in position 1,3 
#    ... and similarly for the rest of the values in row 1
#
#    adds 3 (i.e. 2+1) to the value in position 2,1 
#    adds 4 (i.e. 2+2) to the value in position 2,2 
#    adds 5  (i.e.2+3) to the value in position 2,3 
#    ... and similarly for the rest of the values in row 2
#
#    etc ... for all rows in the matrix
#
# Use nested loops for your answer
#
# (a) - Write the function using nested for loops
# (b) - Write the function using nested while loops
# (c) - Write the function using nested loops, one should be a for loop
#       and one a while loop (your choice which is which)
#
##################################################################.
# Answer - for

changeRows_for = function( m ){
 
 for( row in 1:nrow(m)){
  
   for (col in 1:ncol(m)){
     m[row, col] = m[row,col] + row + col
   }
 }
 return(m)
}

# Test the function
m = matrix( 0 , nrow=3, ncol=6)
m      
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    0    0    0    0    0    0
[2,]    0    0    0    0    0    0
[3,]    0    0    0    0    0    0
changeRows_for(m)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    2    3    4    5    6    7
[2,]    3    4    5    6    7    8
[3,]    4    5    6    7    8    9
# Another test
set.seed(100)  # you can pick any seed 
m = matrix(trunc(runif(min=1, max=5, 15)), nrow=5, ncol=3)
m
     [,1] [,2] [,3]
[1,]    2    2    3
[2,]    2    4    4
[3,]    3    2    2
[4,]    1    3    2
[5,]    2    1    4
changeRows_for(m)
     [,1] [,2] [,3]
[1,]    4    5    7
[2,]    5    8    9
[3,]    7    7    8
[4,]    6    9    9
[5,]    8    8   12
# Another test
m = matrix(seq(10, 180, 10) , nrow=3, ncol=6)
m
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]   10   40   70  100  130  160
[2,]   20   50   80  110  140  170
[3,]   30   60   90  120  150  180
changeRows_for(m)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]   12   43   74  105  136  167
[2,]   23   54   85  116  147  178
[3,]   34   65   96  127  158  189
# Answer - while

changeRows_while = function( m ){
 
 row = 1
 while( row <= nrow(m) ){
 
   col = 1  
   while(col <= ncol(m)){
     m[row, col] = m[row,col] + row + col
     col = col+1 
   }
  
   row = row + 1
 }
 
 return (m)
}

m = matrix( 0 , nrow=3, ncol=6)
m      
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    0    0    0    0    0    0
[2,]    0    0    0    0    0    0
[3,]    0    0    0    0    0    0
changeRows_while(m)
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    2    3    4    5    6    7
[2,]    3    4    5    6    7    8
[3,]    4    5    6    7    8    9

41.6 You CANNOT use a for loop for some problems

41.6.1 guessingGame - can’t use for loop

#-----------------------------------------------------------------
# SOMETIMES you MUST use a while loop.
#-----------------------------------------------------------------
# Some problems that cannot be coded with a for loop
# but rather require that you use a WHILE loop.
#
# To use a for loop, you must be able to construct a vector that 
# contains all possible values that you will loop through.
#
# However, sometimes, you don't know what those values are or how
# many times you will need to process the loop.
#-----------------------------------------------------------------

# EXAMPLE - guessing game cannot be done with a for loop.
# The loop can go on forever if the user keeps getting the wrong answer.

# The following function is coded with a WHILE loop.
# (you cannot write this function with a for loop)

guessingGame <- function(low=1, high=10){
  num <- sample(low:high, 1)
  numGuesses <- 1
  guess <- as.numeric( readline( paste0(
             "guess a number between ", low, " and ", high, ": " )))
  while(guess != num) {
    if (guess < num){
      guess <- as.numeric( readline("higher, guess again: ") )
    } else if (guess > num) {
      guess <- as.numeric( readline("lower, guess again: ") )
    }
    numGuesses <- numGuesses + 1
  } 
  cat("You got it in", numGuesses, "guesses.")
}

#guessingGame()  # run this in RStudio

41.6.2 firstNPrimes - can’t use for loop

# EXAMPLE - firstNPrimes cannot be done with a for loop. I have 
# no idea how many numbers I'll have to check to find the 1000th prime
# or the millionth prime. There is no simple formula that will give
# that answer. Therefore, there is no way to know how long the vector 
# should be for the for loop.

41.7 With a for loop you must use a variable even when there is no need for the value.

With a for loop you must use a variable even when there is no need for the value.

The following for loop does NOT make reference to the num variable at all. However, it is still required to be specified in the code.

sayHello = function( numTimes ) {
  
  for (num in 1:numTimes){
    name = readline("What is your name? ")
    cat("Hello ", name, ".\n", sep="")
    cat("I'm fine.\n")
    cat("How are you doing?\n\n")
  }
  
  cat("bye bye.")
}

sayHello(3)  # say hello three times
What is your name?  Alice 
Hello Alice.
I'm fine.
How are you doing?

What is your name?  Bob 
Hello Bob.
I'm fine.
How are you doing?

What is your name?  Carol 
Hello Carol.
I'm fine.
How are you doing?

bye bye.

41.8 NEVER change the value of the for loop variable/vector in the body

DO NOT CHANGE THE VALUE OF THE FOR LOOP VARIABLE IN THE BODY!!!

DO NOT CHANGE THE VALUE OF THE FOR LOOP VECTOR IN THE BODY!!!

  • The code in the body should NEVER change the value of the for loop variable directly.

    All changes to the value of the for loop variable should only be done automatically by the for loop mechanism, i.e. the next value from the vector is AUTOMATICALLY assigned to the for loop variable each time through the loop.

    Explicitly changing the value of the for loop variable in the body of the loop is very confusing and is considered VERY VERY sloppy coding by programmers everywhere.

  • Similarly, the code in the body should NEVER change the value of the for loop vector. The vector should have a value when the for loop starts and the value of the vector should NEVER change as the for loop is executing.

    Changing the value of the vector of the for loop in the body of the loop is very confusing and is considered VERY VERY sloppy coding by programmers everywhere.

41.9 DIFFERENCE BETEWEN FOR LOOPS AND WHILE LOOPS

A for loop is a convenience but NOT a necessity.

It IS TRUE that any code written with a for loop CAN be converted to use a while loop instead.

HOWEVER, it is NOT TRUE that any code written with a while loop can be converted to use a for loop.

A for loop can only be used when you can anticipate how many times the code will loop before the loop starts.

For example, the guessing game program that we wrote in an earlier class cannot be written with a for loop since it is impossible to know in advance how many times the loop will need to “go around”.

isPrime CAN be written with a for loop since you know that you even before the for loop needs to “go around” at most sqrt(n) times.

firstNprimes cannot be written with a for loop since we have no idea in advance how large the nth prime will be, e.g. how large is the one millionth prime number??? Therefore we cannot anticipate before the loop starts how many times the code needs to be repeated.

41.10 Practice - Some “toy” funcitons to “draw” with character values.

The following functions are classic exercises for students who are trying to really understand how “nested loops” wprk. The functions don’t really do anything so useful. However thinking through how these functions work will sharpen your thought process about nested loops.

Going through these exercises in similar to how someone learning to play a musical instrument will practice “playing scales”. No one is really interested in playing scales but the exercise sharpens the mind.

41.10.1 drawing a box with numbers

#-------------------------------------------------------------------------
# QUESTION: Write a function:   box = function(rows, cols)
# to draw a box in the following pattern using nested for loops
#
#    > box (rows = 3 , cols = 4)
#    3333
#    2222
#    1111
#-------------------------------------------------------------------------
box = function(rows, cols){
  
  for (rowNumber in rows:1){   # count down - we're showing row numbers in reverse
    for (colNumber in 1:cols){ # we can count up or down - we're not using colNumber
      
      cat(rowNumber)
      
    }
    cat("\n")
  }
}

#debugonce(box)

box(3, 4)
3333
2222
1111
box(4, 10)
4444444444
3333333333
2222222222
1111111111
#-------------------------------------------------------------------------
# QUESTION - rewrite the box function from the previous question
#            to use nested while loops
#-------------------------------------------------------------------------
boxWithWhile = function( rows, cols){

  rowNum = rows
  colNum = 1      
  while (rowNum >= 1){
    
    colNum = 1
    while (colNum <= cols){
      
      cat(rowNum)
      colNum = colNum + 1
    }
    cat("\n")
   
    rowNum = rowNum - 1 
  }
}

boxWithWhile(3,4)
3333
2222
1111

41.10.2 drawing a triangle with numbers

#-------------------------------------------------------------------------
# QUESTION - Write a function
#
#    triangle = function(size)
#
# that draws a triangle in the following pattern using nested for loops.
#
#   > triangle(3)
#   1
#   21
#   321
#-------------------------------------------------------------------------
triangle = function(size){

  for(row in 1:(size*2)){
    
    for(col in row:1){
      
      cat(col)
      
    }
    
    cat("\n")
  }
}

triangle(3)
1
21
321
4321
54321
654321
triangle(5)
1
21
321
4321
54321
654321
7654321
87654321
987654321
10987654321
#-------------------------------------------------------------------------
# QUESTION - rewrite the triangle function from the previous question
#            to use nested while loops
#   > triangle(3)
#   1
#   21
#   321
#-------------------------------------------------------------------------
triangle = function(size){
  row = 1
  while(row <= size) {

    col = row
    while(col >= 1) {
      
      cat(col)  
      col = col - 1
    }
    
    cat("\n")
    row = row + 1 
  }
  
}

triangle(3)
1
21
321

41.10.3 triangle with a different pattern

#-------------------------------------------------------------------------
# QUESTION - Write a function
#
#    triangle = function(size)
#
# that draws a triangle in the following pattern using nested for loops.
#
#   > triangle(3)
#     1
#    21
#   321
#
#   > triangle(4)
#      1
#     21
#    321
#   4321
#
# HINT: you can think of this "triangle" as a "box" but where some of the 
# spots are actually spaces. It might help to view the spaces as periods
# so that you can see them. Think about how to draw each of the rows - 
# you can split up each row into (a) drawing the periods and (b) drawing the 
# numbers
#
#   > triangle(3)
#   ..1
#   .21
#   321
#
#   > triangle(4)
#   ...1
#   ..21
#   .321
#   4321
#-------------------------------------------------------------------------
triangle <- function(size) {
  for (i in 1:size) {

    # draw spaces
    if (size - i > 0) {
      for (s in 1:(size - i)) {
        cat(" ")
      }
    }
    
    # draw numbers
    for (n in i:1) {
      cat(n)
    }
    cat("\n")
  }
}

# Examples
triangle(3)
  1
 21
321
triangle(4)
   1
  21
 321
4321

41.10.4 QUESTION: isPrime(num)

#----------------------------------------------------
# Write isPrime(num) 
#
# Part a - do it using a while loop (we did this in an earlier class)
# Part b - do it using a for loop
#----------------------------------------------------
isPrime <- function(num) {
  divisor <- 2

  while ( divisor < sqrt(num) ){
    if (num %% divisor == 0){
      return(FALSE)
    }
    divisor <- divisor + 1
  }
  
  return(TRUE)
}

isPrime(35) # FALSE
[1] FALSE
isPrime(37) # TRUE
[1] TRUE
isPrime_for = function( num ){

  for(divisor in 2:sqrt(num)) {
    if ( num %% divisor == 0){
     return (FALSE)
    }
  }
  return(TRUE)
}


isPrime_for(49) # FALSE
[1] FALSE
isPrime_for(37) # TRUE
[1] TRUE

41.10.5 QUESTION: myUnlist(SOME_LIST)

#------------------------------------------------------------
# QUESTION
#
# The unlist function can be used on a dataframe (since a dataframe is a list).
# (see example below)
#
# Write a function myUnlist that does the same thing as the unlist 
# function. However, your function does NOT need to create names for
# the answer vector.
#
# ARGUMENTS: lst is a list (remember a dataframe is also a list) 
#
# The function should return a character vector that combines all
# the values from the list (or all the columns from the dataframe)
# into a single vector. 
#------------------------------------------------------------
myUnlist = function( lst ) {
 
 answer = vector()
 
 for( listEntry in lst ){
  
    answer = c(answer, listEntry)
  
 }
 
 answer
 
}

students <- data.frame(student=c("joe", "sue"),
                       test1= c(100,90),
                       test2 = c(85, 95),
                       honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
  student test1 test2 honors
1     joe   100    85  FALSE
2     sue    90    95   TRUE
myUnlist(students)
[1] "joe"   "sue"   "100"   "90"    "85"    "95"    "FALSE" "TRUE" 
myUnlist <- function ( lst ){
  answer <- character(0)  
  position <- 1
  while ( position <= length(lst)  )  {
    answer <- c( answer ,   lst[[position]]   )
    position <- position + 1
  }

  return(answer)
}

students <- data.frame(student=c("joe", "sue"),
                       test1= c(100,90),
                       test2 = c(85, 95),
                       honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
  student test1 test2 honors
1     joe   100    85  FALSE
2     sue    90    95   TRUE
myUnlist(students)
[1] "joe"   "sue"   "100"   "90"    "85"    "95"    "FALSE" "TRUE" 
#------------------------------------------------------------
# QUESTION
#
# Rewrite myUnlist to use a for loop
#------------------------------------------------------------
myUnlist <- function ( lst ){
  answer <- character(0)
  for( value in lst ){
    answer <- c(answer , value)
  }
  return(answer)
}

students <- data.frame(student=c("joe", "sue"),
                       test1= c(100,90),
                       test2 = c(85, 95),
                       honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
  student test1 test2 honors
1     joe   100    85  FALSE
2     sue    90    95   TRUE
myUnlist(students)
[1] "joe"   "sue"   "100"   "90"    "85"    "95"    "FALSE" "TRUE" 

41.10.6 QUESTION: dfToVec - only character columns

#------------------------------------------------------------
# Write a function, dfToVec, that converts the contents of a 
# dataframe into a vector (same as the myUnlist function above.
# However, modify the code so that the vector 
# that is returned ONLY INCLUDES the values that
# are in character columns of the dataframe.
#------------------------------------------------------------
dfToVec <- function ( df ){
  answer <- character(0)
  
  for ( column in df ) {
    if ( is.character(column) ){
      answer <- c(answer, column)
    }
  }
  
  return ( answer )
}

students <- data.frame(student=c("joe", "sue"),
                       test1= c(100,90),
                       test2 = c(85, 95),
                       year = c("fr", "so"),
                       honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
  student test1 test2 year honors
1     joe   100    85   fr  FALSE
2     sue    90    95   so   TRUE
dfToVec(students)
[1] "joe" "sue" "fr"  "so" 

41.10.7 QUESTION: dfToVec - only positive numbers, TRUE values and words that start with “a”

#------------------------------------------------------------
# QUESTION
#
# Write a function that takes a dataframe, df
# and returns a character vector.
# 
# The vector should contain
#  - all positive numbers from numeric columns
#  - all TRUE values from logical columns
#  - all character values that start with an "a" from character columns
#------------------------------------------------------------
dfToVec <- function( df ){
  
  answer <- character(0)
  
  for ( column in df ) {  # start of "outer" for loop
    
    for ( value in column) {  # start of "inner" for loop
      
      if( is.character(value) ){
        if ( value >= "a" & value < "b" ){
          answer <- c(answer, value)
        }
      } else if ( is.numeric(value)) {
        if( value > 0){
          answer <- c(answer, value)
        }
      } else if (is.logical(value) ) {
        if (value == TRUE){
          answer <- c(answer, value)
        }
        
      }  
      
    } # end of "inner" for loop
    
  } # end of "outer" for loop
  return(answer)
}

students <- data.frame(student=c("joe", "alice", "mike", "anne"),
                       test1= c(100,90,-20,-30),
                       test2 = c(-10, -40, 85, 95),
                       favFood = c("orange", "pear", "apple", "artichoke"),
                       honors = c(TRUE, FALSE, FALSE, FALSE), stringsAsFactors = FALSE)
students
  student test1 test2   favFood honors
1     joe   100   -10    orange   TRUE
2   alice    90   -40      pear  FALSE
3    mike   -20    85     apple  FALSE
4    anne   -30    95 artichoke  FALSE
dfToVec(students) # "alice" "anne" "100" "90" "85" 95" "TRUE"
[1] "alice"     "anne"      "100"       "90"        "85"        "95"       
[7] "apple"     "artichoke" "TRUE"     
#------------------------------------------------------------
# another way
#------------------------------------------------------------
#    loop through the column numbers  (outer loop)
#       loop through the row numbers    (inner loop)
#          check a particular value from a given row,column

dfToVec <- function( df ){
  
  answer <- character(0)
  
  for ( colNumber in 1:ncol(df)) {  # "outer" for loop
    
    for ( rowNumber in 1:nrow(df)) {  # "inner" for loop
      
      value = df[rowNumber, colNumber]  # get a single value
      
      if( is.character(value) ){
        if ( value >= "a" & value < "b" ){
          answer <- c(answer, value)
        }
      } else if ( is.numeric(value)) {
        if( value > 0){
          answer <- c(answer, value)
        }
      } else if (is.logical(value) ) {
        if (value == TRUE){
          answer <- c(answer, value)
        }
      }
      
    }  # end of "inner" for loop
    
  }  # end of "outer" for loop
  
  return(answer)
}


students <- data.frame(student=c("joe", "alice", "mike", "anne"),
                       test1= c(100,90,-20,-30),
                       test2 = c(-10, -40, 85, 95),
                       favFood = c("orange", "pear", "apple", "artichoke"),
                       honors = c(TRUE, FALSE, FALSE, FALSE), stringsAsFactors = FALSE)
students
  student test1 test2   favFood honors
1     joe   100   -10    orange   TRUE
2   alice    90   -40      pear  FALSE
3    mike   -20    85     apple  FALSE
4    anne   -30    95 artichoke  FALSE
dfToVec(students) # "alice" "anne" "100" "90" "85" 95" "TRUE"
[1] "alice"     "anne"      "100"       "90"        "85"        "95"       
[7] "apple"     "artichoke" "TRUE"     

41.11 strsplit function

We will use this function below in some loop examples. Therefore let’s cover the basics of how this function works it here first before using it in a more involved loop example below.

#...............
# strsplit 
#...............

# strsplit will split up each value in a 
# character vector into multiple values.
#
# IMPORTANT: The return value of strsplit is a LIST.
# View the help page by typing: 
#
#    ?strsplit

# multiple values in the original vector
people = c("Cohen,Sam","Jones,Bob","Andrews,Claire")
people
[1] "Cohen,Sam"      "Jones,Bob"      "Andrews,Claire"
length(people) 
[1] 3
splitPeople <-strsplit(people,",")
splitPeople
[[1]]
[1] "Cohen" "Sam"  

[[2]]
[1] "Jones" "Bob"  

[[3]]
[1] "Andrews" "Claire" 
# print the first name (i.e. the 2nd position) for "Andrews,Claire"
splitPeople[[3]][2]
[1] "Claire"
# use a colon as a separator
places <- c("New York:NY:USA", "Jerusalem::IL", "LA:CA:USA", "Miami:FL:USA")
places
[1] "New York:NY:USA" "Jerusalem::IL"   "LA:CA:USA"       "Miami:FL:USA"   
strsplit(places, ":")
[[1]]
[1] "New York" "NY"       "USA"     

[[2]]
[1] "Jerusalem" ""          "IL"       

[[3]]
[1] "LA"  "CA"  "USA"

[[4]]
[1] "Miami" "FL"    "USA"  
# Note that strsplit returns a LIST even if there is only one 
# value in the character vector
fruit = c("apple,orange,pear")
fruit
[1] "apple,orange,pear"
length(fruit)
[1] 1
splitFruit <- strsplit(fruit,   ",")
splitFruit
[[1]]
[1] "apple"  "orange" "pear"  
# display just the 2nd fruit (notice the [[double-brackets]][single-brackets])
splitFruit[[1]][2]
[1] "orange"
# If the separator is the "empty string" (i.e. "") then 
# every character (e.g. letter, digit, space,!,@,#,$,%, etc)
# is split into separate character values
words = c("racecar", "pineapple", "cart")
strsplit(words,"")
[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"

[[2]]
[1] "p" "i" "n" "e" "a" "p" "p" "l" "e"

[[3]]
[1] "c" "a" "r" "t"
# Remember that strsplit returns a list EVEN if there
# is only one character value being split. You will need to 
# keep this in mind when writing the code.
word <- "racecar"
y <- strsplit(word,"")
y
[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
y[1]       # still a list 
[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
y[[1]]     # the vector without the surrounding list
[1] "r" "a" "c" "e" "c" "a" "r"
unlist(y)  # the vector without the surrounding list
[1] "r" "a" "c" "e" "c" "a" "r"
# display the 2nd letter of the word
y[[1]][2]    # one way
[1] "a"
unlist(y)[2] # another way
[1] "a"
z <- y[[1]]  # you could use a variable
z[2]
[1] "a"

41.12 QUESTION: isPalindrome(x)

#--------------------------------------------------------------
# A plaindrome is a word that reads the same forwards and 
# backwards. 
# Example:   racecar is a palindrome
#            pineapple is not
#            abcdxba is NOT a palindrome
#            abcdcba is a palindrome
#
# Write a function isPalindrome(x)
# 
# ARGUMENTS
#   - x is expected to be a character vector with one item in it
#
# isPalindrome(x) should return TRUE if x is a palindrome
# and return FALSE if it isn't
#
# PART A - code this using a while loop
#
# PART B - code this using a for loop
#--------------------------------------------------------------

41.12.1 isPalindrome(x) with a while loop

isPalindrome <- function(x) {
  if (!is.character(x) | length(x) !=1){
    stop("x must have a single character value")
  }
  
  lets <- strsplit(x, "")[[1]]   # get the individual letters in a vector
  
  left  = 1
  right = length(lets)
  
  while ( left < right) {
    if(lets[left] != lets[right]){
      return(FALSE)
    }
    left = left + 1
    right = right - 1
  }
  
  return(TRUE)
}

isPalindrome("abcdxba")  # FALSE
[1] FALSE
isPalindrome("abcdcba")  # TRUE
[1] TRUE
isPalindrome("racecar")  # TRUE
[1] TRUE
isPalindrome("amanaplanacanalpanama")  # TRUE
[1] TRUE
isPalindrome("a man a plan a canal panama")  # FALSE - because of the spaces
[1] FALSE
#.........................................................
# Rewrite the function so that it removes spaces before
# checking to see if x is a palindrome
#.........................................................
isPalindrome <- function(x) {
  if (!is.character(x) | length(x) !=1){
    stop("x must have a single character value")
  }
  
  lets <- strsplit(x, "")[[1]]
  
  lets <- lets [ lets != " " ]  # get rid of the spaces from lets
  
  left  = 1
  right = length(lets)
  
  while ( left < right) {
    if(lets[left] != lets[right]){
      return(FALSE)
    }
    left = left + 1
    right = right - 1
  }
  
  return(TRUE)
}

isPalindrome("a man a plan a canal panama")  # TRUE
[1] TRUE
#................................................
#
# Rewrite the code to use a for loop
#
#................................................

isPalindrome <- function(x) {

  if (!is.character(x) | length(x) !=1){
    stop("x must have a single character value")
  }
  
  lets <- strsplit(x, "")[[1]]   # get the individual letters in a vector
  
  # get rid of the spaces from lets
  lets <- lets [ lets != " " ]

  for ( num in 1:trunc(length(lets)/2) ){
    # note: num will start at 1, then 2, then 3, up until 1/2 the length of the word
    # exmpale: for "racecar" num will be 1 then 2 then 3 then stop
    first = num
    last = length(lets)-num+1
    if( lets[first] !=  lets[last]){
      return(FALSE)
    }
  }
  return(TRUE)  
  
}

isPalindrome("abcdxba")  # FALSE
[1] FALSE
isPalindrome("abcdcba")  # TRUE
[1] TRUE
isPalindrome("racecar")  # TRUE
[1] TRUE
isPalindrome("amanaplanacanalpanama")  # TRUE
[1] TRUE
isPalindrome("a man a plan a canal panama")  # TRUE
[1] TRUE

41.13 QUESTION: checkPalindromes()

Write a function, checkPalindromes = function() that asks the user to enter a word. The function should then call the isPalindrome function with the word the user entered and report on whether the word is a palindrome or not. The user should be asked to enter additional words until the user types the word “end” to end the program.

# Ask the user for a word to check it its a palindrome
checkPalindromes = function(){
  
  # keep asking the user for a word and then reply if the 
  # word is a palindrome or not.
  # Ask the user if they want to keep going or type "end" to exit.
  
  word = readline("Enter a word to check or 'end' to stop:")
  while(word != "end"){
    if (isPalindrome(word)){
      cat(word, "is a palindrome\n\n")
    } else {
      cat (word, "is NOT a palindrome\n\n")
    }

    word = readline("Enter a word to check or 'end' to stop:")
    
  }
}
# Test the function (note)
checkPalindromes()
Enter a word to check or 'end' to stop: racecar 
racecar is a palindrome

Enter a word to check or 'end' to stop: racecars 
racecars is NOT a palindrome

Enter a word to check or 'end' to stop: abcddcba 
abcddcba is a palindrome

Enter a word to check or 'end' to stop: abcdba 
abcdba is NOT a palindrome

Enter a word to check or 'end' to stop: end 

© 2025 Y. Rosenthal. All rights reserved.