For loops that check for empty range

Viewed 245

When dealing with recursive equations in mathematics, it is common to write equations that hold over some range k = 1,...,d with the implicit convention that if d < 1 then the set of equations is considered to be empty. When programming in R I would like to be able to write for loops in the same way as a mathematical statement (e.g., a recursive equation) so that it interprets a range with upper bound lower than the lower bound as being empty. This would ensure that the syntax of the algorithm mimics the syntax of the mathematical statement on which it is based.

Unfortunately, R does not interpret the for loop in this way, and so this commonly leads to errors when you program your loops in a way that mimics the underlying mathematics. For example, consider a simple function where we create a vector of zeros with length n and then change the first d values to ones using a loop over the elements in the range k = 1,...,d. If we input d < 1 into this function we would like the function to recognise that the loop is intended to be empty, so that we would get a vector of all zeros. However, using a standard for loop we get the following:

#Define a function using a recursive pattern
MY_FUNC <- function(n,d) {
  OBJECT <- rep(0, n);
  for (k in 1:d) { OBJECT[k] <- 1 }
  OBJECT }

#Generate some values of the function
MY_FUNC(10,4);
[1] 1 1 1 1 0 0 0 0 0 0

MY_FUNC(10,1);
[1] 1 0 0 0 0 0 0 0 0 0

MY_FUNC(10,0);
[1] 1 0 0 0 0 0 0 0 0 0
#Not what we wanted

MY_FUNC(10,-2);
[1] 1 1 1 1 1 1 1 1 1 1
#Not what we wanted

My Question: Is there any function in R that performed loops like a for loop, but interprets the loop as empty if the upper bound is lower than the lower bound? If there is no existing function, is there a way to program R to read loops this way?

Please note: I am not seeking answers that simply re-write this example function in a way that removes the loop. I am aware that this can be done in this specific case, but my goal is to get the loop working more generally. This example is shown only to give a clear view of the phenomenon I am dealing with.

3 Answers

EDIT

If you don't want to return an error when negative input is given you can use pmax with seq_len

MY_FUNC <- function(n,d) {
  OBJECT <- rep(0, n);
  for (k in seq_len(pmax(0, d))) { OBJECT[k] <- 1 }
  OBJECT
}

MY_FUNC(10, 4)
#[1] 1 1 1 1 0 0 0 0 0 0
MY_FUNC(10, 1)
#[1] 1 0 0 0 0 0 0 0 0 0
MY_FUNC(10, 0)
#[1] 0 0 0 0 0 0 0 0 0 0
MY_FUNC(10, -2)
#[1] 0 0 0 0 0 0 0 0 0 0

Previous Answer

Prefer seq_len over 1:d and it takes care of this situation

MY_FUNC <- function(n,d) {
  OBJECT <- rep(0, n);
  for (k in seq_len(d)) { OBJECT[k] <- 1 }
  OBJECT 
}

MY_FUNC(10, 4)
#[1] 1 1 1 1 0 0 0 0 0 0
MY_FUNC(10, 1)
#[1] 1 0 0 0 0 0 0 0 0 0
MY_FUNC(10, 0)
#[1] 0 0 0 0 0 0 0 0 0 0

MY_FUNC(10, -2)

Error in seq_len(d) : argument must be coercible to non-negative integer

There is imho no generic for-loop doing what you like but you could easily make it by adding

if(d > 0) break

as the first statement at the beginning of the loop.

The function can be vectorized

MY_FUNC <- function(n,d) {
 rep(c(1, 0), c(d, n -d))
  }

MY_FUNC(10, 4)
#[1] 1 1 1 1 0 0 0 0 0 0
MY_FUNC(10, 1)
#[1] 1 0 0 0 0 0 0 0 0 0
MY_FUNC(10, 0)
#[1] 0 0 0 0 0 0 0 0 0 0
MY_FUNC(10, -2)

Error in rep(c(1, 0), c(d, n - d)) : invalid 'times' argument

Related