Name:
Andrew ID:
Collaborated with:

This lab is to be done in class (completed outside of class if need be). You can collaborate with your classmates, but you must identify their names above, and you must submit your own lab as an knitted HTML file on Canvas, by Sunday 11:59pm, this week. Make sure to complete your weekly check-in (which can be done by coming to lecture, recitation, lab, or any office hour), as this will count a small number of points towards your lab score.

This week’s agenda: practicing debugging with cat(), print(), and browser(); simple testing.

Bug hunt practice

In this section of the lab, you will fix a bunch of buggy function definitions. Probably the easiest workflow is to define the function in your console, and then run the sample commands—they will either give errors or produce the wrong outputs. Using any combination of: reading the error messages, traceback(), and cat() or print(), you must find and fix the bugs. Sometimes it can also help to try multiple different inputs, i.e., try new function calls, rather than just looking at the sample calls given to you, in order to determine the bugs. You shouldn’t show any of your debugging work in your final knitted answers—so, don’t show calls to traceback(), and don’t leave any cat() or print() calls in the final, fixed function. (You don’t have to do anything yet, this was just to setup this section of the lab.)

# Function: cols.with.ab.zeros, to retrieve columns of matrix that have between
#   a and b zeros, each
# Inputs:
# - my.mat: the original matrix 
# - a: lower bound for number of zeros allowed; default is 0
# - b: upper bound for number of zeros allowed; default is Inf
# Output: the new matrix

cols.with.ab.zeros = function(my.mat, a=0, b=Inf) {
  zeros.per.column = colSums(mat != 0)
  i.to.keep = a <= zeros.per.column && zeros.per.column <= b
  return(my.mat[i.to.keep,])
}

mat = matrix(c(0,0,1,0,1,1,1,1,1), 3, 3)
identity.mat = diag(1, 3)
cols.with.ab.zeros(mat) # Should get back original matrix
##      [,1] [,2] [,3]
## [1,]    0    0    1
## [2,]    0    1    1
## [3,]    1    1    1
cols.with.ab.zeros(mat, a=1, b=2) # Should get back first 2 columns of mat
##      [,1] [,2] [,3]
## [1,]    0    0    1
## [2,]    0    1    1
## [3,]    1    1    1
cols.with.ab.zeros(mat, a=2, b=2) # Should get just 1st column of mat; note
##      [,1] [,2] [,3]
  # this should still be a matrix though, and not a numeric vector!
cols.with.ab.zeros(identity.mat, a=2, b=2) # Should get back original matrix
##      [,1] [,2] [,3]
# Function: list.extractor, to extract elements of a list
# Inputs:
# - my.list: the original list 
# - i.to.keep: vector of indices, corresponding to elements of the list we
#   want to keep. Default is NULL, in which case this argument is ignored
# - i.to.remove: vector of indices, corresponding to elements of the list we
#   want to remove Default is NULL, in which case this argument is ignored.
#   NOTE: if both i.to.keep and i.to.remove are non-NULL, then the first 
#   one should take precedence (i.e., we don't remove anything)
# Output: the new list

list.extractor = function(my.list, i.to.keep=NULL, i.to.remove=NULL) {
  if (i.to.keep!=NULL) {
    L = my.list[[i.to.keep]]
  }
  if (i.to.remove!=NULL) {
    L = my.list[[-i.to.remove]]
  }
  return(L)
}

cool.list = list(ints=1:10, lets=letters[1:8], fracs=1:7/7,
                 bools=sample(c(TRUE,FALSE), 5, replace=TRUE))
list.extractor(cool.list, i.to.keep=c(1,3)) # Should get list with ints, fracs
## Error in if (i.to.keep != NULL) {: argument is of length zero
list.extractor(cool.list, i.to.remove=4) # Should get list without bools
## Error in if (i.to.keep != NULL) {: argument is of length zero
list.extractor(cool.list, i.to.keep=2:4, i.to.remove=4) # Should get list with
## Error in if (i.to.keep != NULL) {: argument is of length zero
  # lets, fracs, and bools (the i.to.remove argument should be ignored)
# Function: random.walk, to run a simple random walk over the reals, which
#   terminates when it reaches 0
# Inputs:
# - x.start: starting position. Default is 5
# - plot.walk: should the result be plotted? Default is TRUE
# - seed: integer seed to pass to set.seed(). Default is NULL, which means
#   effectively no seed is set
# Output: a list with elements x.vals, the values visited by the random walk,
#   and num.steps, the number of steps taken before termination

random.walk = function(x.start=5, plot.walk=TRUE, seed=NULL) {
  if (!is.null(seed)) set.seed(seed) # Set the seed, if we need to
  x.vals = x.start
  while (TRUE) {
    r = runif(1, -2, 1)
    if (tail(x.vals+r,1) <= 0) break
    else x.vals = c(x.vals, x.vals+r)
  }
  if (plot.walk <- TRUE) 
    plot(x.vals, xlab="Iteration", ylab="Random walk values", type="o")
  return(x.vals=x.vals, num.steps=length(x.vals))
}

random.walk(x.start=5, seed=3)$num.steps # Should print 8 (this is how many
## Error in return(x.vals = x.vals, num.steps = length(x.vals)): multi-argument returns are not permitted

  # steps it took the random walk), and produce a plot
random.walk(x.start=10, seed=7)$num.steps # Should print 14 (this is how many
## Error in return(x.vals = x.vals, num.steps = length(x.vals)): multi-argument returns are not permitted
  # steps it took the random walk), and produce a plot
random.walk(x.start=10, plot.walk=FALSE, seed=7)$num.steps # Should print 14 
## Error in return(x.vals = x.vals, num.steps = length(x.vals)): multi-argument returns are not permitted

  # (this is how many steps it took the random walk), and not produce a plot

Browsing practice

add.up.inv.powers = function(n, verbose=FALSE) {
  x = 0
  for (i in 1:n) {
    x = x + i^(1/i)
    if (verbose) roman.cat(i)
  }
  if (verbose) cat("\n")
  return(x)
}

roman.cat = function(num) {
  roman.num = as.roman(num)
  roman.str = as.character(roman.num)
  cat(roman.str, "... ")
}

add.up.inv.powers(n=3, verb=FALSE)
## [1] 3.856463
add.up.inv.powers(n=5, verb=FALSE)
## [1] 6.650406
add.up.inv.powers(n=10, verb=FALSE)
## [1] 13.15116

Browsing for bugs

fibonacci = function(n) {
  my.fib = c(1,1)
  for (i in 2:(n-1)) my.fib[i+1] = my.fib[i] + my.fib[i-1]  
  return(my.fib[i])
}

fibonacci(1) # Should be 1
## Error in my.fib[i + 1] = my.fib[i] + my.fib[i - 1]: replacement has length zero
fibonacci(2) # Should be 1
## Error in my.fib[i + 1] = my.fib[i] + my.fib[i - 1]: replacement has length zero
fibonacci(3) # Should be 2
## [1] 1
fibonacci(5) # Should be 5
## [1] 3
fibonacci(9) # Should be 34
## [1] 21
sentence.flipper = function(str) {
  str.words = strsplit(str, split=" ")  
  rev.words = lapply(str, word.flipper)  
  str.flipped = paste(rev.words, collapse=" ")
  return(str.flipped)
}

word.flipper = function(str) {
  chars = strsplit(str, split="")
  chars.flipped = rev(chars)
  str.flipped = paste(chars.flipped, collapse="")
  return(str.flipped)
}

# Should be "eht kciuq nworb xof depmuj revo eht yzal god"
sentence.flipper("the quick brown fox jumped over the lazy dog") 
## [1] "c(\"t\", \"h\", \"e\", \" \", \"q\", \"u\", \"i\", \"c\", \"k\", \" \", \"b\", \"r\", \"o\", \"w\", \"n\", \" \", \"f\", \"o\", \"x\", \" \", \"j\", \"u\", \"m\", \"p\", \"e\", \"d\", \" \", \"o\", \"v\", \"e\", \"r\", \" \", \"t\", \"h\", \"e\", \" \", \"l\", \"a\", \"z\", \"y\", \" \", \"d\", \"o\", \"g\")"
# Should be "ot eb ro on ot eb taht si eht noitseuq"
sentence.flipper("to be or no to be that is the question") 
## [1] "c(\"t\", \"o\", \" \", \"b\", \"e\", \" \", \"o\", \"r\", \" \", \"n\", \"o\", \" \", \"t\", \"o\", \" \", \"b\", \"e\", \" \", \"t\", \"h\", \"a\", \"t\", \" \", \"i\", \"s\", \" \", \"t\", \"h\", \"e\", \" \", \"q\", \"u\", \"e\", \"s\", \"t\", \"i\", \"o\", \"n\")"
# Redefine sentence.flipper() here

# Should be "olleh ssenkrad ym dlo dneirf", 
#           "ev'i emoc ot kaeps htiw uoy niaga"
sentence.flipper(c("hello darkness my old friend",
                   "i've come to speak with you again"))
## [1] "c(\"h\", \"e\", \"l\", \"l\", \"o\", \" \", \"d\", \"a\", \"r\", \"k\", \"n\", \"e\", \"s\", \"s\", \" \", \"m\", \"y\", \" \", \"o\", \"l\", \"d\", \" \", \"f\", \"r\", \"i\", \"e\", \"n\", \"d\") c(\"i\", \"'\", \"v\", \"e\", \" \", \"c\", \"o\", \"m\", \"e\", \" \", \"t\", \"o\", \" \", \"s\", \"p\", \"e\", \"a\", \"k\", \" \", \"w\", \"i\", \"t\", \"h\", \" \", \"y\", \"o\", \"u\", \" \", \"a\", \"g\", \"a\", \"i\", \"n\")"
# Should be "reven annog evig uoy pu",
#           "reven annog tel uoy nwod",
#           "reven annog nur dnuora dna tresed uoy"
sentence.flipper(c("never gonna give you up",
                   "never gonna let you down",
                   "never gonna run around and desert you"))
## [1] "c(\"n\", \"e\", \"v\", \"e\", \"r\", \" \", \"g\", \"o\", \"n\", \"n\", \"a\", \" \", \"g\", \"i\", \"v\", \"e\", \" \", \"y\", \"o\", \"u\", \" \", \"u\", \"p\") c(\"n\", \"e\", \"v\", \"e\", \"r\", \" \", \"g\", \"o\", \"n\", \"n\", \"a\", \" \", \"l\", \"e\", \"t\", \" \", \"y\", \"o\", \"u\", \" \", \"d\", \"o\", \"w\", \"n\") c(\"n\", \"e\", \"v\", \"e\", \"r\", \" \", \"g\", \"o\", \"n\", \"n\", \"a\", \" \", \"r\", \"u\", \"n\", \" \", \"a\", \"r\", \"o\", \"u\", \"n\", \"d\", \" \", \"a\", \"n\", \"d\", \" \", \"d\", \"e\", \"s\", \"e\", \"r\", \"t\", \" \", \"y\", \"o\", \"u\")"
# Define sentence.scrambler() here

sentence.scrambler(c("I have no theorems, well",
  "I do have theorems, but none of them are named Fienberg's Theorem",
  "Even if there were a Fienberg's Theorem, it probably wouldn't be important",
  "What's important is the attitude, for what statistics is",
  "and how it's recognized by other people outside of our field"))
## Error in sentence.scrambler(c("I have no theorems, well", "I do have theorems, but none of them are named Fienberg's Theorem", : could not find function "sentence.scrambler"

Testing practice