Grace Murray Hopper popularised the term bug after in 1947 her team traced an error in the Mark II to a moth trapped in a relay.
even_or_odd <- function(num) {
if (num %% 2 == 0) {
return("even")
} else {
return("odd")
}
}
even_or_odd(42.7)
[1] "odd"
even_or_odd('42')
Error in num%%2: non-numeric argument to binary operator Traceback: 1. even_or_odd("42")
even_or_odd <- function(num) {
num <- as.integer(num) # We expect input to be integer or convertible into one
if (num %% 2 == 0) {
return("even")
} else {
return("odd")
}
}
even_or_odd(42.7)
[1] "even"
even_or_odd('42')
[1] "even"
Fixing a buggy program is a process of confirming, one by one, that the many things you believe to be true about the code actually are true. When you find that one of your assumptions is not true, you have found a clue to the location (if not the exact nature) of a bug.
Norman Matloff
print()
¶print()
statement can be used to check the internal state of a program during evaluationls()
(and get()
/mget()
) to reveal all local objectsdebug()
/debugonce()
)calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
if (n %% 2 == 1) {
med <- a[m]
} else {
med <- a[m-1:m]
}
return(med)
}
v1 <- c(1, 2, 3)
v2 <- c(0, 1, 1, 2)
calculate_median(v1)
[1] 2
calculate_median(v2)
[1] 0
print()
example¶calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
print(m)
if (n %% 2 == 1) {
med <- a[m]
} else {
med <- a[m-1:m]
}
return(med)
}
v1 <- c(1, 2, 3)
v2 <- c(0, 1, 1, 2)
calculate_median(v1)
[1] 2
[1] 2
calculate_median(v2)
[1] 2
[1] 0
print()
and ls()
example¶calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
# Analogous to Python's print(vars())
# Print all objects in function environment
print(mget(ls()))
if (n %% 2 == 1) {
med <- a[m]
} else {
med <- a[m-1:m]
}
return(med)
}
calculate_median(v1)
$a [1] 1 2 3 $m [1] 2 $n [1] 3
[1] 2
calculate_median(v2)
$a [1] 0 1 1 2 $m [1] 2 $n [1] 4
[1] 0
print()
example continued¶calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
if (n %% 2 == 1) {
med <- a[m]
} else {
print(m-1:m)
med <- a[m-1:m]
}
return(med)
}
calculate_median(v1)
[1] 2
calculate_median(v2)
[1] 1 0
[1] 0
calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
if (n %% 2 == 1) {
med <- a[m]
} else {
med <- a[m:m+1]
}
return(med)
}
calculate_median(v1)
[1] 2
calculate_median(v2)
[1] 1
browser()
- pauses the execution at a dedicated line in code (breakpoint)debug()
/undebug()
- (un)sets a flag to run function in a debug mode (setting through)debugonce()
- triggers single stepping through a functioncalculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
if (n %% 2 == 1) {
med <- a[m]
} else {
browser()
med <- a[m-1:m]
}
return(med)
}
## Example for running in RStudio
calculate_median(v2)
Called from: calculate_median(v2) debug at <text>#9: med <- a[m - 1:m] debug at <text>#11: return(med)
[1] 0
Command | Description |
---|---|
n(ext) |
Execute next line of the current function |
s(tep) |
Execute next line, stepping inside the function (if present) |
c(ontinue) |
Continue execution, only stop when breakpoint in encountered |
f(inish) |
Finish execution of the current loop or function |
Q(uit) |
Quit from the debugger, executed program is aborted |
Extra: Hadley Wickham - Conditions
42 + "ab" # Throws an error
Error in 42 + "ab": non-numeric argument to binary operator Traceback:
as.numeric(c("42", "55.3", "ab", "7")) # Triggers a warning
Warning message in eval(expr, envir, enclos): “NAs introduced by coercion”
[1] 42.0 55.3 NA 7.0
library("dplyr") # Shows a message
Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union
stop("This is what an error looks like")
Error in eval(expr, envir, enclos): This is what an error looks like Traceback: 1. stop("This is what an error looks like")
warning("This is what a warning looks like")
Warning message in eval(expr, envir, enclos): “This is what a warning looks like”
message("This is what a message looks like")
This is what a message looks like
stop()
if (c(TRUE, TRUE, FALSE)) {
print("This used to word pre R-4.2.0")
}
Error in if (c(TRUE, TRUE, FALSE)) {: the condition has length > 1 Traceback:
# Will become an error in future versions of R
c(TRUE, FALSE) && c(TRUE, TRUE)
Warning message in c(TRUE, FALSE) && c(TRUE, TRUE): “'length(x) = 2 > 1' in coercion to 'logical(1)'” Warning message in c(TRUE, FALSE) && c(TRUE, TRUE): “'length(x) = 2 > 1' in coercion to 'logical(1)'”
[1] TRUE
anscombes_quartet <- readr::read_csv("../data/anscombes_quartet.csv")
Rows: 44 Columns: 3 ── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Delimiter: "," chr (1): dataset dbl (2): x, y ℹ Use `spec()` to retrieve the full column specification for this data. ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
try()
for errorssuppressWarnings()
for warningssuppressMessages()
for messagessuppressMessages(anscombes_quartet <- readr::read_csv("../data/anscombes_quartet.csv"))
# But some functions would provide arguments to silence messages
# This should be preferred to heavy-handed suppressMessages()
anscombes_quartet <- readr::read_csv("../data/anscombes_quartet.csv", show_col_types = FALSE)
# suppressPackageStartupMessages() - a variant for package startup messages
# But suppressMessages() would also work.
suppressPackageStartupMessages(library("dplyr"))
f1 <- function(x) {
log(x)
10
}
f1("x")
Error in log(x): non-numeric argument to mathematical function Traceback: 1. f1("x")
f2 <- function(x) {
try(log(x))
10
}
f2("y")
Error in log(x) : non-numeric argument to mathematical function
[1] 10
tryCatch()
define exiting handlerswithCallingHandlers()
define calling handlerstryCatch(
error = function(cnd) {
# code to run when error is thrown
},
code_to_run_while_handlers_are_active
)
withCallingHandlers(
warning = function(cnd) {
# code to run when warning is signalled
},
message = function(cnd) {
# code to run when message is signalled
},
code_to_run_while_handlers_are_active
)
tryCatch()
are called exiting handlers.f3 <- function(x) {
tryCatch(
error = function(e) NA,
log(x)
)
}
f3("x")
[1] NA
# Infinite loop, analogous to while (TRUE)
repeat {
num <- readline("Please, enter a number:")
if (num != "") {
withCallingHandlers(
warning = function(cnd) {
print("This is not a number. Please, try again.")
},
num <- as.numeric(num)
)
} else {
print("No input provided. Please, try again.")
}
if (!is.na(num)) {
print(paste0("Your input '", as.character(num), "' is recorded"))
break
}
}
Please, enter a number:f [1] "This is not a number. Please, try again."
Warning message in withCallingHandlers(warning = function(cnd) {: “NAs introduced by coercion”
Please, enter a number:43 [1] "Your input '43' is recorded"
testthat
library Extra: Hadley Wickham - Testing
library("testthat")
Attaching package: ‘testthat’ The following object is masked from ‘package:dplyr’: matches
calculate_median <- function(a) {
a <- sort(a)
n <- length(a)
m <- (n + 1) %/% 2
if (n %% 2 == 1) {
med <- a[m]
} else {
med <- a[m:m+1]
}
return(med)
}
testthat::test_that("The length of result is 1", {
testthat::expect_equal(length(calculate_median(c(0, 1, 1, 2))), 1L)
testthat::expect_equal(length(calculate_median(c(1, 2, 3))), 1L)
testthat::expect_equal(length(calculate_median(c("a", "bc", "xyz"))), 1L)
})
Test passed 🥳
testthat::test_that("The result is numeric", {
testthat::expect_true(is.numeric(calculate_median(c(0, 1, 1, 2))))
testthat::expect_true(is.numeric(calculate_median(c(1, 2, 3))))
testthat::expect_true(is.numeric(calculate_median(c("a", "bc", "xyz"))))
})
── Failure (<text>:4:3): The result is numeric ───────────────────────────────── is.numeric(calculate_median(c("a", "bc", "xyz"))) is not TRUE `actual`: FALSE `expected`: TRUE
Error: ! Test failed Traceback: 1. testthat::test_that("The result is numeric", { . testthat::expect_true(is.numeric(calculate_median(c(0, 1, . 1, 2)))) . testthat::expect_true(is.numeric(calculate_median(c(1, 2, . 3)))) . testthat::expect_true(is.numeric(calculate_median(c("a", . "bc", "xyz")))) . }) 2. (function (envir) . { . handlers <- get_handlers(envir) . errors <- list() . for (handler in handlers) { . tryCatch(eval(handler$expr, handler$envir), error = function(e) { . errors[[length(errors) + 1]] <<- e . }) . } . attr(envir, "withr_handlers") <- NULL . for (error in errors) { . stop(error) . } . })(<environment>)