Why is while (is.null(...)) often combined with num_to_schoice?

51 Views Asked by At

Why is the while loop necessary in the following code chunk? Taken from the dist3 exercise provided in the R/exams package.

sc <- NULL
while (is.null(sc)) {
  p <- c(sample(1:3, 1), sample(1:5, 1))
  q <- c(sample(4:5, 1), sample((1:5)[-p[2]], 1))
  sol <- sqrt(sum((p - q)^2))

  err <- c(sqrt(sum((p + q)^2)), sqrt(sum(abs(p - q))))
  err <- err[abs(err - sol) > 0.1]
  if (length(err) > 1) err <- sample(err, 1)

  sc <- num_to_schoice(sol, wrong = err,
    range = c(0.1, 10), delta = 0.3, digits = 3)
}
1

There are 1 best solutions below

0
On

In general the function num_to_schoice() - that in this example is responsible for updating the variable sc upon which the while loop conditions - is not guaranteed to return a list with $questions texts and corresponding logical $solutions. Instead, it may return NULL along with a warning when it cannot set up the questions/solutions with the properties specified (range, minimum distance, etc.). See below for some illustrations.

Therefore, to guarantee that an exercise that relies on num_to_schoice() always yields a valid solution, the single-choice list sc is first initialized with NULL and then parameters are resampled and num_to_schoice() recalled until sc is not NULL anymore (but a list with questions/solutions). (Remark: I use this setup routinely in my own exercises and often do not check whether any problems may actually occur. In this particular setup it seems that the problem with a NULL value can never occur.)

For illustration, consider the following simple examples where the correct solution is 10, we want a delta of at least 1 between the correct and any false solution, and we use different ranges for the false solutions.

  • First, let's use the range [1, 20] which always finds some set of false solutions.

    set.seed(2021)
    num_to_schoice(10, range = c(1, 20), delta = 1, digits = 0)
    ## $solutions
    ## [1] FALSE FALSE FALSE  TRUE FALSE
    ## 
    ## $questions
    ## [1] "$16$" "$17$" "$7$"  "$10$" "$13$"
    
  • In contrast, the interval [9, 11] is never large enough for four false solutions with a minimal distance of 1.

    num_to_schoice(10, range = c(9, 11), delta = 1, digits = 0)
    ## NULL
    ## Warning message:
    ## In num_to_schoice(10, range = c(9, 11), delta = 1, digits = 0) :
    ##   specified 'range' is too small for 'delta'
    
  • Finally, the interval [7, 13] may or may not be large enough. This depends on an initial step carried out in num_to_schoice() where the function first decides how many of the four false solutions should be on the left vs. on the right of the correct solution. This is built into the function to avoid systematically generating question lists where the correct solution is (one of) the smallest or (one of) the largest.

    num_to_schoice(10, range = c(7, 13), delta = 1, digits = 0)
    ## $solutions
    ## [1] FALSE FALSE  TRUE FALSE FALSE
    ## 
    ## $questions
    ## [1] "$7$"  "$13$" "$10$" "$11$" "$9$" 
    num_to_schoice(10, range = c(7, 13), delta = 1, digits = 0)
    ## NULL
    ## Warning message:
    ## In num_to_schoice(10, range = c(7, 13), delta = 1, digits = 0) :
    ##   specified 'range' is too small for 'delta'
    

More impossible setups can occur when wrong solutions are pre-specified that are too close to the correct solution and/or make the interval too small etc. See the examples in ?num_to_schoice for more illustrations.