How to assign the results of a function that returns a list of objects?

Asked

Viewed 2,755 times

11

In R we can make a function return more than one object through a list. But how to assign these objects to two distinct variables?

Example:

f<-function(){
  primeiro<-1:10
  segundo<-11:21
  return (list(primeiro,segundo))  
}

With the above function I can assign and access the objects as follows.

d<-f()
d[1]
d[2]

But I cannot assign these values to different variables through a list

list(a,b)<-f()

I want to assign to each variable - "a" and "b" - one of the result objects of function f(). The Python equivalent would be:

def f():
    primeiro=range(1,11)
    segundo=range(11,20)
    return (primeiro,segundo)

(a,b)=f()        
print a
print b

3 answers

5


The solution that I found simpler and more interesting was to define another binary operator (such as,%<-% signaling a modification of the <- ) apply the assign in multiple arguments with the mapply:

`%<-%` <- function(variaveis, valores) {
      mapply(assign, as.character(substitute(variaveis)[-1]), valores, MoreArgs= list(envir = parent.frame()))
      invisible()
    }

Thus:

list(a,b,c,d) %<-% rnorm(4)

It will generate four random variables,a, b, c, and d, with the four values of rnorm, for example.

An advantage of mapply is that it will already recycle the values if they are fewer than the variables, for example:

list(a,b,c,d) %<-% c(1,2)

Will generate a=1, b=2, c=1, and d=2, because the values are recycled.

It will also handle the case that the number of values is greater than the number of variables, using the latest numbers of values. For example:

list(a,b,c,d,e) %<-% 1:10

Will assign the values from 6 to 10 to the variables.

In short, it is a binary operator with intuitive symbol (%<-%), with simple definition - only one line - and that already deals with some exceptions in a natural way to R. However, I would avoid frequently using this type of operation without first testing well to see if there are other side effects or unpredictable behaviors.

3

Not all programming languages offer a succinct way to assign values to more than one variable at a time - in a way, Python is an exception.

That question in Stackoverflow in English documents some of the ways to approximate multiple assignment in R. If you really want you can use something suggested there but the impression I have is that maybe it is worth writing in the simplest way and with less magic, despite being a little longer

d <- f()
a <- d[1]
b <- d[2]

3

In that reply in the SOEN the author proposes a structure to allow such constructions. Here is the original post (also in English). The suggested code is:

list <- structure(NA,class="result")
"[<-.result" <- function(x,...,value) {
   args <- as.list(match.call())
   args <- args[-c(1:2,length(args))]
   length(value) <- length(args)
   for(i in seq(along=args)) {
     a <- args[[i]]
     if(!missing(a)) eval.parent(substitute(a <- v,list(a=a,v=value[[i]])))
   }
   x
}

And some examples of use:

list[a, b] <- funcaoRetornandoDoisValores()
list[a] <- funcaoRetornandoDoisValores()
list[a, ] <- funcaoRetornandoDoisValores()
list[, b] <- funcaoRetornandoDoisValores()

list[QR,,QRaux]  <- qr(c(1,1:3,3:1))
list[,Green,Blue]  <- col2rgb("aquamarine")

a <- 1; b <- 2
list[a,b] <- list(b,a) # Troca a e b

require(chron)
list[Month, Day, Year] <- month.day.year(unclass(Sys.Date()))

(Note that you can get all return values, or ignore those that do not interest you)

There are several other options in the same linked question (and @missingno’s answer) but this form was the one that in my opinion produces cleaner code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.