library(tidyverse)

hw01_team01/hw1.qmd

Task 1 - Implement fizzbuzz

Write up

In order to write our fizzbuzz function, we took a series of steps. We began by thinking broadly about the errors we needed to throw before the actual function could run. We determined that the following would be necessary: checking for non-numeric types of inputs, negative values, inputs that are not coercible to integers without rounding or truncating, infinite values, and NAs and NaNs.

Once we came up with the criteria, we wrote the necessary code to check for this using stopifnot() statements and decided on helpful error messages to include. We then worked on setting up the format for the function. We used a for-loop to check each element of the vector and determine what it needs to be replaced with. We had to consider the order of the if else statements to ensure that each number was being labelled correctly.

Throughout this entire process, we were checking individual parts of our code using test cases in the console. Then we ran the good and bad test cases and discovered that we had too many double negatives in our stopifnot() statements, which we resolved. After debugging our code, we ran all of the test cases, and added our own, to ensure that our function was running properly.

Function

fizzbuzz = function(input) {
  n = length(input)
  return_vector = vector(mode = "character", length = n)
  
  #stop cases for the entire vector
  stopifnot("Input vector has non-numeric types" = is.numeric(input))
  stopifnot("Input vector has values less than 0" = (input >= 0))
  stopifnot("Input vector has values not coercible to integer type" = (input %% 1 == 0))
  stopifnot("Input vector has infinite values" = !is.infinite(input))
  stopifnot("Input vector has NA values" = !is.na(input))
  stopifnot("Input vector has NaN values" = !is.nan(input))
  
  for (integer in 1:n) {
    value = input[integer]
    
    if ((value %% 3 == 0) & (value %% 5 == 0)) {
      return_vector[integer] = "FizzBuzz"
    }
    else if (value %% 3 == 0) {
      return_vector[integer] = "Fizz"
    }
    else if (value %% 5 == 0) {
      return_vector[integer] = "Buzz"
    }
    
    #integer not divisible by either 3 or 5
    else {
      return_vector[integer] = as.character(value)
    }
  }
  return (return_vector)
}
fizzbuzz(1:5)
[1] "1"    "2"    "Fizz" "4"    "Buzz"
fizzbuzz(5:1)
[1] "Buzz" "4"    "Fizz" "2"    "1"   
fizzbuzz(-1)
Error in fizzbuzz(-1): Input vector has values less than 0

hw01_team02/hw1.qmd

Task 1 - Implement fizzbuzz

Write up

The idea behind this function is to check each value within the input to ensure that they each fit the parameters given and if they do not to throw an error explaining exactly why they do not. In order for this function to work, however, the mode of the entire input must be numeric, and so we used an if logic statement to throw an error at the beginning rather than include it within the for loop for efficiency’s sake. Within the for loop, we are checking each value within the input vector individually. We first check to ensure that the vlaue is greater than 0. Then, we check to ensure the value is finite. If the value contains a decimal i.e. is a double, we multiply that value by 10 and then ensure that the remainder of that when divided by 10 is 0 to show that the value can be treated as an integer. All of these checks, if failed, have associated and descriptive error messages describing what the issue for the individual value was. As for performing the fizzbuzz creation, assuming that the value passed all of the checks, checks if the value is divisible cleanly by 3, 5, both, or neither and return Fizz, Buzz, FizzBuzz, or the value as a character as outlined by the requirements and then assigning that tothe output vector in the same location as the value within the input vector. We used else if statements, but given the structure of the code we could have used just if statements since each of the if statements would save over the previous entry within the output vector (i.e. if the value was 15, the corresponding entry in output would be Fizz, Buzz, and then FizzBuzz since 15 is divisible by 3, 5, and 3 and 5).

Function

fizzbuzz = function(input) {
  
  output = c()
  
  if(mode(input) != "numeric"){
    stop("Input must be a numeric vector (either double or integer type is allowed)")
  }
  for (n in input) {
    if (n < 0){
      stop("All input values must be >= 0")
    }
    if (is.infinite(n) || is.nan(n) || is.na(n)){
      stop("All input values must be finite")
    }
    if (typeof(n) == "double"){
      if ((n * 10) %% 10 != 0){
        stop(" all values must be coercible to integer without rounding or truncating")
      }
    }
    if (n %% 3 == 0) {
      output[n] = "Fizz"
    }
    else if (n %% 5 == 0) {
      output[n] = "Buzz"
    }
    else if (n %% 3 == 0 & n %% 5 == 0) {
      output[n] = "FizzBuzz"
    }
    else if (n %% 3 != 0 & n %% 5 != 0){
      output[n] = as.character(n)
    }
  }
  
}
fizzbuzz(1:5)
fizzbuzz(5:1)
fizzbuzz(-1)
Error in fizzbuzz(-1): All input values must be >= 0

hw01_team03/hw1.qmd

Task 1 - Implement fizzbuzz

Write up

The fizzbuzz function presented aims to tackle the FizzBuzz problem. This problem involves iterating over a sequence of numbers and applying specific rules: for multiples of 3, the number is replaced with “Fizz”; for multiples of 5, “Buzz”; and for multiples of both 3 and 5, “FizzBuzz”. The function is designed to accept a numeric vector and modify it according to these rules.

Initially, the function enforces a strict input validation. It asserts that the input must be numeric, either of type double or integer, reflecting the numeric nature of the FizzBuzz problem. This is crucial as the operation of checking divisibility does not semantically apply to non-numeric types. Furthermore, the function scrutinizes each element within the input vector to confirm that it is an integer and non-negative, aligning with the traditional boundaries of the FizzBuzz problem. Should any element deviate from these constraints by being non-integer or negative, the function promptly halts execution and raises an error, thereby enforcing the integrity of the input data.

Upon validating the input, the function proceeds to apply the FizzBuzz logic. It accurately identifies multiples of 3, 5, and 3 & 5 using the modulo operator, replacing the appropriate numbers with “Fizz”, “Buzz”, or “FizzBuzz”. If indivisible by either number, it simply returns the number back.

Notably, the function modifies the input vector in place but lacks an explicit return statement. In R, functions return the result of the last evaluated expression, which, in this case, would be the modified input vector. However, explicitly stating this return would enhance the clarity and readability of the function.

In summary, the fizzbuzz function embodies a methodical approach to solving the FizzBuzz problem, with a strong emphasis on input validation to ensure the data’s compatibility with the problem’s numerical and logical constraints.

Function

fizzbuzz = function(input) {
  
  if (!is.numeric(input)) {
    stop("Input must be a numeric vector")
  }
  if (any(is.nan(input))) stop("No input values can be NaN")
  if (any(input < 0)) stop("All input values must be >=0")
  if (any(!is.finite(input))) stop("All input values must be finite")
  if (any(input!=as.integer(input))) stop("All input values must be coercible to integer without rounding or truncating")
  ret <- input
  for (i in seq_along(input)) {
    
    if (input[i] %% 15 == 0) {
      ret[i] = "FizzBuzz"
    }
    else if (input[i] %% 3 == 0) {
      ret[i] = "Fizz"
    }
    else if (input[i] %% 5 == 0) {
      ret[i] = "Buzz"
    }
    else {
      ret[i] = as.character(input[i])
    }
  }
  return(ret)
}
fizzbuzz(1:5)
[1] "1"    "2"    "Fizz" "4"    "Buzz"
fizzbuzz(5:1)
[1] "Buzz" "4"    "Fizz" "2"    "1"   
fizzbuzz(-1)
Error in fizzbuzz(-1): All input values must be >=0

hw01_team04/hw1.qmd

Task 1 - Implement fizzbuzz

Write up

We began writing the FizzBuzz function by first accounting for the requirements for the function specified in the README document with instructions. We initially had decided to place the errors such as checking for all numeric inputs, NA inputs, and infinite inputs within the for loop as the loop iterates through all of the elements in the input. We realized that this wasn’t the most efficient way to write the code as it won’t throw the errors immediately as specified in the problem. To fix this, we decided to put the errors that could be evaluated using the any() wrapper function to check whether the vector is numeric, doesn’t contain any NA inputs, and only contains finite inputs before the for loop so that these errors would be caught immediately.

Once we wrote the code to catch these errors, we next started the for loop which loops through every value in the input using seq_along() and then we indexed the input to get a single value x which was evaluated through the for loop. Within this for loop, we included code to catch errors for the individual input values such as values which are negative and those which are doubles but aren’t coercible to an integer without truncating. After we had checked for these errors, we wrote an if statement checking whether the numeric input x is divisible by 3 and 5. If this is the case, then the original input at the index i is replaced as “FizzBuzz.” If this statement is not true, we used an else if statement to evaluate whether the numeric input x is divisible by 3 and if so then the input at index i is replaced by “Fizz”. Lastly, we used an else if to check whether the numeric input x is divisible by 5 and if so the original input is replaced by “Buzz” at index i. Outside of the for loop, we have our original input returned with a character type as specified by the instructions.

Once we finished writing the code described above, we then used the good input test cases to check whether our code ran correctly. The main modifications we made from the initial code to the final code was placing more of the code to catch the errors before the loop in order to increase efficiency. We also added a test case for when the input is 0 and realized that fizzbuzz(0) returned “FizzBuzz” when we expected it to return 0. We realized this is because 0 is considered a multiple of both 3 and 5 so 0 %% 3 == 0 and 0 %% 5 == 0 meaning 0 is assigned the string “FizzBuzz.” Our group discussed and decided that we wanted to hard code for the case when the input is 0 and just have “0” returned because we didn’t think it made sense in this case for 0 to be considered a multiple of 3 and 5 because we think that only conventional multiples of 3 and 5 should be changed to “FizzBuzz.”

After modifying the function to include the case when the input is 0, we moved on to evaluating the bad inputs and tested the inputs on our code. We realized that the way our code was written wouldn’t throw an error if the input contained a sequence of negative to positive values such as fizzbuzz(-2:6) so we went through and modified our code to ensure that each input is being tested in the for loop to determine whether it is negative and throw an error for any input which is negative. We wrote similar test cases to ensure that a vector being passed in with some numeric and some character values will throw an error since the entire vector must be numeric.

Lastly, after adding the additional test cases and discussing whether we had covered all possibilities, we made the code more efficient by moving some of the checks for errors outside of the for loop. For instance, initially we had the checks for numeric, na values, and infinite values all in the for loop which wasn’t efficient because if the NA was the last value, it would iterate through all of the other individual inputs before reaching the NA and triggering the error. Using the any() wrapper function, we were able to include these checks outside of the for loop so that they wouldn’t require iterating through the for loop to find individual input values which violated our set of possible inputs. Below is a description of our final code after we made modifications and adjustments to account for edge cases and improving efficiency.

Function

fizzbuzz <- function(input) {
  if (!is.numeric(input)) {
    stop("Input vector must be numeric")
  }
  if (any(is.na(input))) {
    stop("All input values must be integers or doubles (no NAs allowed)")
  }
  if (any(is.infinite(input))) {
    stop("All input values must be finite")
  }
  for (i in seq_along(input)) {
    x <- input[i]
    if (x < 0) {
      stop("All input values must be greater than or equal to 0")
    }
    if ((is.double(x) == TRUE)) {
      if (x %% 1 != 0) {
        stop("All input values must be coercible to an integer without truncating")
      }
    }
    if (x == 0) {
      input[i] <- "0"
    } else if ((as.numeric(x) %% 3 == 0) & (as.numeric(x) %% 5 == 0)) {
      input[i] <- "FizzBuzz"
    } else if (as.numeric(x) %% 3 == 0) {
      input[i] <- "Fizz"
    } else if (as.numeric(x) %% 5 == 0) {
      input[i] <- "Buzz"
    }
  }
  as.character(input)
}
fizzbuzz(1:5)
[1] "1"    "2"    "Fizz" "4"    "Buzz"
fizzbuzz(5:1)
[1] "Buzz" "4"    "Fizz" "2"    "1"   
fizzbuzz(-1)
Error in fizzbuzz(-1): All input values must be greater than or equal to 0

hw01_team05/hw1.qmd

Task 1 - Implement fizzbuzz

Write up

The fizzbuzz function takes the value “input” as its only argument. The first half of the function checks whether the input is “good” or “bad” and reacts accordingly. First, the function stores the length of the input vector in variable “len”, and checks whether or not the length of the input vector is 0. If the input vector is length zero, the execution of the function is stopped and “input must be not empty!” is printed, indicating that the user inputted an empty vector. If the vector is not of length zero, the function checks if any of the values in vector input are equal to infinity or negative infinity - if they are, the function stops execution and prints “input value must be finite.” Otherwise, the function continues and checks if any of the values in the vector are NA. If at least one value is NA, the function stops execution and prints “input value must not be NA.” If no values are NA, the function then checks if the vector is numeric, indicating it contains numeric values. If not, it stops execution and prints “input value must be numeric.” Otherwise the function will check if the numeric input is of type double and if the input vector casted as integers is not equal to the original integer vector. If it is not equal to the original integer vector, execution is stopped and “input value must be coercible to integer without rounding” is printed. This condition is in place to make sure double values in the vectors only have 0’s after the decimal, and are not changed when made an integer. The last condition checked is whether any integer is less than 0 - if so, execution is stopped and “input value must be non-negative” is printed.

The following section completes the necessary tasks of the FizzBuzz function. We start by initializing an empty vector fb. It first implements a for loop that iterates through the indices of the input vector. The first two lines of this for loop check if the vector value at the given index is divisible by five and divisible by three, and storing the boolean values in five_mult and three_mult respectively. If both are true, the fb vector adds “FizzBuzz” at that particular index. If only three_mult is true, indicating that the value is divisible by three, “Fizz” is added to the fb vector at that given index. If only five_mult is true, indicating that the value is only divisible by five, “Buzz” is added to the fb vector at the given index. Finally, if three_mult and five_mult are both false, indicating the value is not divisible by three and is not divisible by five, we place that value in the fb vector at the given index. Lastly, we replace the fb vector with the same vector but with character elements and return the fb vector.

Function

fizzbuzz = function(input) {
  
  len <- length(input)
  if (len == 0) {
    stop("input must be not empty!")
  } else if (any(input == Inf | input == -Inf)) {
    stop("input value must be finite")
    } else if (any(is.na(input) == TRUE)) {
      stop("input value must not be NA")
      } else if (is.numeric(input) == TRUE) {
        if (typeof(input) == "double") {
          if (any(as.integer(input) != input)) {
            stop("input value must be coercible to integer without rounding")
            } else if (any(input < 0)) {
              stop("input value must be non-negative")
            }
        }
      } else {
        stop("input value must be numeric")
        }
  
  fb <- c()

  for(val in 1:len) {
    five_mult <- input[val] %% 5 == 0
    three_mult <- input[val] %% 3 == 0
    if(five_mult == TRUE && three_mult == TRUE) {
      fb[val] <- "FizzBuzz"
    } else if(three_mult == TRUE) {
      fb[val] <- "Fizz"
    } else if(five_mult == TRUE) {
      fb[val] <- "Buzz"
    } else {
      fb[val] <- input[val]
    }
  }
  fb <- as.character(fb)
  return(fb)
}
fizzbuzz(1:5)
[1] "1"    "2"    "Fizz" "4"    "Buzz"
fizzbuzz(5:1)
[1] "Buzz" "4"    "Fizz" "2"    "1"   
fizzbuzz(-1)
Error in fizzbuzz(-1): input value must be non-negative