Home > OS >  Syntax in R for breaking up LHS of assignment over multiple lines
Syntax in R for breaking up LHS of assignment over multiple lines

Time:02-23

What is the syntax in R to break up the LHS of an assignment over multiple lines? In other programming languages you can do it.

One use case of this (and please don't let this example distract from the general syntactical question), is that because (I believe) R doesn't support loop placeholder variables by reference, so when working with very deep nested data structures (like recursive lists of data frames of lists) in a for loop you can sometimes have a very a long LHS of an assignment, e.g.

results$cases[[i]]$samples[[j]]$portions[[k]]$analytes[[l]]$column <- x

I know, you might say why not use nested lapplys or something, but that's a separate discussion.

I tried wrapping the LHS in parentheses and you get

Error: could not find function "(<-"

CodePudding user response:

You can put a line break between any 2 characters that aren't part of a name, and that doesn't leave a syntactically complete expression before the line break (so that the parser knows to look for more). None of these look great, but basically after any [[ or $ or before ]] you can put a line break. For example:

results$
  cases[[i]]$
    samples[[j]]$
      portions[[k]]$
        analytes[[l]]$
          column <- x

Or going to the extreme, putting in every syntactically valid line break (without introducing parentheses which would let you do even more):

results$
  cases[[
    i
  ]]$
    samples[[
      j
    ]]$
      portions[[
        k
      ]]$
        analytes[[
          l
        ]]$
          column <-
            x

With parentheses, we lose the "doesn't leave a syntactically complete expression" rule, because the expression won't be complete until the parenthses close. You can add breaks anywhere except in the middle of a name (object or function name). I won't bother with nested indentation for this example.

(
  results
  $
  cases
  [[
    i
  ]]
  $
  samples
  [[
    j
  ]]
  $
  portions
  [[
    k
  ]]
  $
  analytes
  [[
    l
  ]]
  $
  column
  <-
  x
)

If you want to bring attention to the x being assigned, you could also use right assignment.

x -> results$cases[[i]]$samples[[j]]$
       portions[[k]]$analytes[[l]]$column

CodePudding user response:

Just to mention: the element operator [[ supports recursive indexing of lists, as documented in ?Extract under "Recursive (list-like) objects".

One caveat is that you must provide an integer or character index for each level of recursion. Mixtures aren't supported, so you must index by integer if any sublist does not have a names attribute.

x <- list(a = list(a1 = 1, a2 = 2), b = list(b1 = 3, b2 = 4))

x[[c(1L, 1L)]]
## [1] 1

x[[c("a", "a2")]]
## [1] 2

x[[c(2L, 1L)]] <- 30
x[[c(2L, 1L)]]
## [1] 30

x[[c("b", "b2")]] <- 40
x[[c("b", "b2")]]
## [1] 40

x[[list(1L, "a1")]]
## Error in x[[list(1L, "a1")]] : invalid subscript type 'list'

You can always implement accessors that do support indices of mixed type:

rget <- function(x, i) {
    if (is.list(i)) {
        for (j in i) {
            x <- x[[j]]
        }
        x
    } else {
        x[[i]]
    }
}

`rset<-` <- function(x, i, value) {
    if (is.list(i)) {
        y <- x
        n <- length(i)
        ii <- integer(n)
        for (k in seq_len(n)) {
            j <- i[[k]]
            ii[k] <- if (is.character(j)) match(j, names(y)) else j
            y <- y[[j]]
        }
        x[[ii]] <- value
    } else {
        x[[i]] <- value
    }
    x
}

Then:

rget(x, list(1L, "a1"))
## [1] 1

rset(x, list(1L, "a1")) <- 10
rget(x, list(1L, "a1"))
## [1] 10

You would just have to accept the overhead of the for loops.

  • Related