I want to make an R function that returns a list of functions that each operate on a different part of a list. However, because of R's rules on scope, this seems impossible. Here's an example:
functiontest = function() {
foo = list()
for(i in 1:3) {
fixer = function(s) { return(
function() {
return(s)
}
)}
foo[[i]] = fixer(i)
}
return(foo)
}
functiontest()[[2]]() #returns 3
Even killing off the named lambda "fixer" and using an immediate function doesn't save me:
functiontest = function() {
foo = list()
for(i in 1:3) {
foo[[i]]=(function(s) { return(
function() {
return(s)
}
)})(i)
}
return(foo)
}
functiontest()[[2]]() #returns 3
I want this to return 2. How can I refactor this code so that this will happen?
Thanks.
First of all, the problem you have faced has nothing to do with the scoping. Second, I don't think it is a good idea to call something idiotic just because you don't understand it.
In your example, all functions in a list return the same value because of the lazy evaluation mechanism in R. See help(force), which specifically addresses your question. In short, you need to force evaluation of the function at the time you call it, which can be done by adding force:
functiontest = function() {
foo = list()
for(i in 1:3) {
fixer = function(s) {
force(s) ### <<<---This is the only difference from your code ###
return(
function() {
return(s)
}
)}
foo[[i]] = fixer(i)
}
return(foo)
}
or, using a more concise syntax:
functiontest <- function() lapply(1:3, function(s) {s; function() s})
Example of use:
> L <- functiontest()
> L[[1]]()
[1] 1
> L[[2]]()
[1] 2
> L[[3]]()
[1] 3
Related
foo <- function() {
# how to know what environment_of_caller is
}
caller <- function() {
# environment_of_caller
foo()
}
A function that I'm writing needs to the know the environment of its caller. Can that be done without passing the environment in as an argument?
Assuming that you really need to do this, the function parent.frame() gives it.
foo <- function() {
parent.frame()$a
}
caller <- function() {
a <- 1
foo()
}
caller()
## [1] 1
however, normally one would write it like this (only foo is changed) as it gives the desired functionality but also the flexibility to change the environment used.
foo <- function(envir = parent.frame()) {
envir$a
}
caller <- function() {
a <- 1
foo()
}
caller()
## [1] 1
I have this code:
for(plot in plotResults) {
.session$plots[[plot$id]] <- if (!is.null(plot[["grob"]])) {
plot[["grob"]]
} else {
plot[["plot"]]
}
ggsave(paste0(plot$id, ".png"), .session$plots[[plot$id]])
...
}
the file get generated but when I save the ggplot object to mongo using this tools:
serializeObj <- function(obj) {
if (is.list(obj)) {
structure(lapply(obj, serializeObj), names = serializeNames(obj))
} else {
rawToChar(serialize(obj, NULL, ascii = TRUE))
}
}
deserializeObj <- function(obj) {
if (is.list(obj)) {
structure(lapply(obj, deserializeObj), names = unserializeNames(obj))
} else if (is.serialized(obj)) {
unserialize(charToRaw(obj))
} else {
obj
}
}
## Replaces characters illegal in Mongo names with "safe" substitutes
safeNames <- function(value) {
gsub("\\.", "___DOT___", value)
}
## Replaces back "safe" substitutes
unsafeNames <- function(value) {
gsub("___DOT___", ".", value)
}
serializeNames <- function(obj) {
lapply(names(obj), safeNames)
}
unserializeNames <- function(obj) {
lapply(names(obj), unsafeNames)
}
I'm running serializeObj before I save to mongo and then later deserializeObj after read from mongo. And after read instead of ggplot object I've got list and I can't call ggsave on that list.
How can I serialize object of some type like ggplot and then deserialize it to get the same object?
This work, instead of recursive serializer/deserializer only one level is needed and also no need to serialize names because they don't have dots anymore only nested plot data had them.
serializePlots <- function(data) {
lapply(data, function(plot) {
rawToChar(serialize(plot, NULL, ascii = TRUE))
})
}
unserializePlots <- function(data) {
lapply(data, function(obj) {
if (is.serialized(obj)) {
unserialize(charToRaw(obj))
} else {
obj
}
})
}
As pointed in your comments, you are losing information about the classes of the ggplot object. You should probably make sure to store this information and recover it when you unserialize. For example, I wrote three simple f(x) that may help.
#### custom f(x)
serializeGGClasses <- function(obj) {
serializeObj(sapply(obj, class))
}
unserializeGGClasses <- function(obj) {
lapply(obj, (function(el) {unserialize(charToRaw(el)) } ))
}
restoreGGClasses <- function(plot.obj, class.obj) {
class(plot.obj) <- c("gg", "ggplot")
for (i in 1:length(plot.obj)) {
class(plot.obj[[i]]) <- class.obj[[i]]
}
return(plot.obj)
}
###
Now, you could serialize all classes of the ggplot object and store them in your db. When you retrive the object, you need to restore them
library(ggplot2)
a<- qplot(1, 1)
class(a) # "gg" "ggplot"
is.list(a) # TRUE
b <- serializeObj(a)
b.class <- serializeGGClasses(a)
# Unserialize
c <- deserializeObj(b)
c.class <- unserializeGGClasses(b.class)
# Right now, "c" is just a list
final.c <- restoreGGClasses(plot.obj = c,
class.obj = c.class)
# "final.c" can be plotted
final.c
I'm having a little trouble understanding why, in R, the two functions below, functionGen1 and functionGen2 behave differently. Both functions attempt to return another function which simply prints the number passed as an argument to the function generator.
In the first instance the generated functions fail as a is no longer present in the global environment, but I don't understand why it needs to be. I would've thought it was passed as an argument, and is replaced with aNumber in the namespace of the generator function, and the printing function.
My question is: Why do the functions in the list list.of.functions1 no longer work when a is not defined in the global environment? (And why does this work for the case of list.of.functions2 and even list.of.functions1b)?
functionGen1 <- function(aNumber) {
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
functionGen2 <- function(aNumber) {
thisNumber <- aNumber
printNumber <- function() {
print(thisNumber)
}
return(printNumber)
}
list.of.functions1 <- list.of.functions2 <- list()
for (a in 1:2) {
list.of.functions1[[a]] <- functionGen1(a)
list.of.functions2[[a]] <- functionGen2(a)
}
rm(a)
# Throws an error "Error in print(aNumber) : object 'a' not found"
list.of.functions1[[1]]()
# Prints 1
list.of.functions2[[1]]()
# Prints 2
list.of.functions2[[2]]()
# However this produces a list of functions which work
list.of.functions1b <- lapply(c(1:2), functionGen1)
A more minimal example:
functionGen1 <- function(aNumber) {
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#Error in print(aNumber) : object 'a' not found
Your question is not about namespaces (that's a concept related to packages), but about variable scoping and lazy evaluation.
Lazy evaluation means that function arguments are only evaluated when they are needed. Until you call myfun it is not necessary to evaluate aNumber = a. But since a has been removed then, this evaluation fails.
The usual solution is to force evaluation explicitly as you do with your functionGen2 or, e.g.,
functionGen1 <- function(aNumber) {
force(aNumber)
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#[1] 1
How can I get the current function within a function for recursive applying? Here is a trivial example:
myfun <- function(x) {
if(is.list(x)){
lapply(x, myfun)
} else {
length(x)
}
}
I would like to make it anonymous instead, however I don't know how to tell lapply to use the current function when it has no name. I tried Recall but that doesn't work:
(function(x) {
if(is.list(x)){
lapply(x, Recall)
} else {
length(x)
}
})(cars)
Also match.call()[[1]] doesn't help for anonymous functions.
It is sys.function(0) , for example to compute the square of a list recursively :
(function(x) {
if(length(x)>2){
lapply(x,sys.function(0))
} else {
x^2
}
})(list(1,2,3))
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
What you are looking for is, I think, sys.function:
> (function() print(sys.function(1)))()
function() print(sys.function(1))
Recall actually says:
local for another way to write anonymous recursive functions.
The idea is that you define the name locally:
local(myfun <- function(...) { ... myfun(...) ... })
and it is not defined outside.
What type of object is passed to myFunc as x? It doesn't seem to be an expression, nor a function and str just evaluates it. I understand that I can use force() to evaluate. I'm wondering if there's some way to gather more information about x without evaluating it.
myFunc = function( x )
{
is.expression( x )
is.function( x )
str( x )
}
myFunc( { x = 5; print( x + 1 ) } )
You can use match.call for extracting the arguments:
myFunc <- function( x ) {
x <- match.call()$x
print(class(x))
print(typeof(x))
print(mode(x))
print(storage.mode(x))
print(is.expression(x))
print(is.call(x))
if (is.call(x)) print(x[[1]])
}
myFunc({x = 5; print("a")})
myFunc(expression(x))
x <- factor(1)
myFunc(x)
myFunc(1)
Probably I need to say that { is a function in R, so {...} is no more than call.
Updated: why x is not function while { is function:
f <- function(x) {
x <- match.call()$x
print(eval(x[[1]]))
print(is.function(eval(x[[1]])))
}
f({1})
I think class would do the trick... See docs.
EDIT: According to the docs,
for {, the result of the last expression evaluated
Which means the class is the class resulting from the evaluation, which is why it not showing up as an "expression". It is being passed after evaluation.
Dason just posted a similar response to this on Talkstats.com for determining if an object is a data frame or a list (click here for a link to that post). I just extended it to an expression which I think suits your needs.
j.list <- function(list, by = NULL){
print("list")
print(list)
}
j.data.frame <- function(df, ..., by = NULL){
print("data frame")
print(df)
}
j.expression <- function(expression, by = NULL){
print("expression")
print(expression)
}
j <- function(x, ...){
UseMethod("j")
}
j(list(test = "this is a list"))
j(data.frame(test = 1:10))
j(expression(1+ 0:9))