I know that if I have a function that returns multiple values, I should do return(list(foo, bar)). But what do I do on the recieving end? fn <- function(x) { return(list(foo, bar)) } I know that at this point I could say values.list <- fn(x) and then access values.list[1] values.list[2] But that's hideous. I'd rather be able to say something like list(local_foo, local_bar) <- fn(x) and have the right thing happen. I realize that it's my responsibility to not screw up and say instead list(local_bar, local_foo) Any suggestions? -JT
On Wed, 23 Jun 2004, Jack Tanner wrote:> I know that if I have a function that returns multiple values, I should > do return(list(foo, bar)). But what do I do on the recieving end? > > fn <- function(x) { > return(list(foo, bar)) > } > > I know that at this point I could say > > values.list <- fn(x) > > and then access > > values.list[1] > values.list[2] > > But that's hideous. I'd rather be able to say something like > > list(local_foo, local_bar) <- fn(x) > > and have the right thing happen. I realize that it's my responsibility > to not screw up and say instead > > list(local_bar, local_foo) > > Any suggestions? >How about naming the list elements for clarity then? fn <- function(x) { foo <- x; bar <- 1/x; return(list(foo = foo, bar = bar)); } x <- 10; values.list <- fn(x); print(values.list$foo); print(values.list$bar); ---------------------------------------------------------- SIGSIG -- signature too long (core dumped)
Here are two approaches assuming foo is "zz" and bar is 3. FIRST You could pass the return variables in the argument list and then assign them in the caller's frame like this: fn <- function(x,y) { assign(as.character(substitute(x)), "zz", sys.frame(-1)) assign(as.character(substitute(y)), 3, sys.frame(-1)) } fn(a,b) # sets a to "zz" and b to 3 SECOND You can make this a bit prettier, though not perfect, like this: "list2<-" <- function(x,y,value) { assign(as.character(substitute(y)), value[[2]], sys.frame(-1)) value[[1]] } fn <- function()list("zz",3) a <- 1 # first arg must exist prior to invoking list2. Its value not important. list2(a,b) <- fn() The two problems with list2 are: 1. the first argument must exist prior to invoking list2 although its actual value is immaterial since it just gets overwritten anyways. 2. It only works for 2 args although you could write a list3, list4, etc. Maybe someone could comment on these deficiencies. Jack Tanner <ihok <at> hotmail.com> writes: : : I know that if I have a function that returns multiple values, I should : do return(list(foo, bar)). But what do I do on the recieving end? : : fn <- function(x) { : return(list(foo, bar)) : } : : I know that at this point I could say : : values.list <- fn(x) : : and then access : : values.list[1] : values.list[2] : : But that's hideous. I'd rather be able to say something like : : list(local_foo, local_bar) <- fn(x) : : and have the right thing happen. I realize that it's my responsibility : to not screw up and say instead : : list(local_bar, local_foo) : : Any suggestions? : : -JT :
My $0.02: If Jack feels that the fact that functions usually return a single list, and one needs to access the components of the list separately, is somehow hideous, then I'd rather suggest that R is perhaps the wrong language for him. To me the suggested `workarounds' are by far much more hideous... They turn perfectly tranparent code into... gee, I don't even know how to begin describing them... Andy> From: Gabor Grothendieck > > > Here are two approaches assuming foo is "zz" and bar is 3. > > FIRST > > You could pass the return variables in the argument list and then > assign them in the caller's frame like this: > > fn <- function(x,y) { > assign(as.character(substitute(x)), "zz", sys.frame(-1)) > assign(as.character(substitute(y)), 3, sys.frame(-1)) > } > fn(a,b) # sets a to "zz" and b to 3 > > SECOND > > You can make this a bit prettier, though not perfect, like this: > > > "list2<-" <- function(x,y,value) { > assign(as.character(substitute(y)), value[[2]], sys.frame(-1)) > value[[1]] > } > fn <- function()list("zz",3) > a <- 1 # first arg must exist prior to invoking list2. Its > value not important. > list2(a,b) <- fn() > > > The two problems with list2 are: > > 1. the first argument must exist prior to invoking list2 although its > actual value is immaterial since it just gets overwritten anyways. > > 2. It only works for 2 args although you could write a list3, > list4, etc. > > Maybe someone could comment on these deficiencies. > > > Jack Tanner <ihok <at> hotmail.com> writes: > > : > : I know that if I have a function that returns multiple > values, I should > : do return(list(foo, bar)). But what do I do on the recieving end? > : > : fn <- function(x) { > : return(list(foo, bar)) > : } > : > : I know that at this point I could say > : > : values.list <- fn(x) > : > : and then access > : > : values.list[1] > : values.list[2] > : > : But that's hideous. I'd rather be able to say something like > : > : list(local_foo, local_bar) <- fn(x) > : > : and have the right thing happen. I realize that it's my > responsibility > : to not screw up and say instead > : > : list(local_bar, local_foo) > : > : Any suggestions? > : > : -JT > : > > ______________________________________________ > R-help at stat.math.ethz.ch mailing list > https://www.stat.math.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide! > http://www.R-project.org/posting-guide.html > >
Rolf Turner wrote:> > fn <- function(x) { > list(local_foo=foo, local_bar=bar) > } > >OK, that works nicely. (And thanks to Paul Roebuck, who mentioned the values.list$foo notation.)>(the ``return(...)'' is brought to you by your Department of >Redundancy Department.) > >Aha! LISP reards its head. So what happens if I do fn <- function(x) { some stuff ... end of stuff list(foo = foo, bar = bar) } return.value <- fn(x) Will return.value ever contain any residuals from "stuff", or will it always be exactly the result of the last statement, list(foo...) here?>The very fact that you are talking about ``multiple values'' >indicates that you're not really understanding what's going on. >Functions in R always return just ***one*** value. That value is an >object which may have an arbitrarily complicated (list structure). >Related objects get bundled up into lists, giving you just one object >to deal with. Then you can pass that one object to another function, >which can then pick apart that object and use the components in the >appropriate way without you, the user, having to fiddle with them. > >Fine, but the whole notion of loose coupling goes out the window if you're passing lists of related (but very different) objects from function to function, and each function has to be aware of the order of the items in the list. Liaw, Andy wrote:> If Jack feels that the fact that functions usually return a single > list, and > one needs to access the components of the list separately, is somehow > hideousI don't; I think that this is fine when you can access the components of the list by name only, without regard to order. Thanks to all for your help.
Gabor Grothendieck <ggrothendieck at myway.com> wrote: fn <- function(x,y) { assign(as.character(substitute(x)), "zz", sys.frame(-1)) assign(as.character(substitute(y)), 3, sys.frame(-1)) } fn(a,b) # sets a to "zz" and b to 3 "list2<-" <- function(x,y,value) { assign(as.character(substitute(y)), value[[2]], sys.frame(-1)) value[[1]] } fn <- function()list("zz",3) a <- 1 # first arg must exist prior to invoking list2. Its value not important. list2(a,b) <- fn() There is still another way, which doesn't use substitute, as.character, or assign. Instead of returning two results, accept an extra argument which is a function that decides what to do with them. > f <- function(handler) { handler("zz", 3) } > f(function(x, y) { a <<- x; b <<- y }) > a [1] "zz" > b [1] 3 > g <- function() { + u <- v <- NULL # make it clear these are local to g + f(function(x,y) {u <<- x; v <<- y}) + list(u, v) # see what we got + } > g() [[1]] [1] "zz" [[2]] [1] 3 Whenever I would have wanted to pass "a variable" to an R function, this is what I do instead. It works so well that I am still happily ignorant of how to use substitute(). It's also much more compiler-friendly.
Possibly Parallel Threads
- again, a question between R and C++
- apply function over same column of all objects in a list
- substitute "x" for "pattern" in a list, while preservign list "structure". lapply, gsub, list...?
- Creating object referant from argument name
- Assigning a vector to every element of a list.