R optim(): unexpected behavior when working with parent environments - r

Consider the function fn() which stores the most recent input x and its return value ret <- x^2 in the parent environment.
makeFn <- function(){
xx <- ret <- NA
fn <- function(x){
if(!is.na(xx) && x==xx){
cat("x=", xx, ", ret=", ret, " (memory)", fill=TRUE, sep="")
return(ret)
}
xx <<- x; ret <<- sum(x^2)
cat("x=", xx, ", ret=", ret, " (calculate)", fill=TRUE, sep="")
ret
}
fn
}
fn <- makeFn()
fn() only does the calculation when a different input value is provided. Otherwise, it reads ret from the parent environment.
fn(2)
# x=2, ret=4 (calculate)
# [1] 4
fn(3)
# x=3, ret=9 (calculate)
# [1] 9
fn(3)
# x=3, ret=9 (memory)
# [1] 9
When plugin fn() into optim() to find its minimum, the following unexpected behavior results:
optim(par=10, f=fn, method="L-BFGS-B")
# x=10, ret=100 (calculate)
# x=10.001, ret=100.02 (calculate)
# x=9.999, ret=100.02 (memory)
# $par
# [1] 10
#
# $value
# [1] 100
#
# (...)
Is this a bug? How can this happen?
Even when using the C-API of R, I have a hard time to imagine how this behavior can be achieved. Any ideas?
Note:
works:
library("optimParallel") # (parallel) wrapper to optim(method="L-BFGS-B")
cl <- makeCluster(2); setDefaultCluster(cl)
optimParallel(par=10, f=fn)
works:
optimize(f=fn, interval=c(-10, 10))
works:
optim(par=10, fn=fn)
fails:
optim(par=10, fn=fn, method="BFGS")
works:
library("lbfgs"); library("numDeriv")
lbfgs(call_eval=fn, call_grad=function(x) grad(func=fn, x=x), vars=10)
works:
library("memoise")
fn_mem <- memoise(function(x) x^2)
optim(par=10, f=fn_mem, method="L-BFGS-B")
Tested with R version 3.5.0.

The problem is happening because the memory address of x is not updated when it is modified on the third iteration of the optimization algorithm under the "BFGS" or "L-BFGS-B" method, as it should.
Instead, the memory address of x is kept the same as the memory address of xx at the third iteration, and this makes xx be updated to the value of x before the fn function runs for the third time, thus making the function return the "memory" value of ret.
You can verify this by yourself if you run the following code that retrieves the memory address of x and xx inside fn() using the address() function of the envnames or data.table package:
library(envnames)
makeFn <- function(){
xx <- ret <- NA
fn <- function(x){
cat("\nAddress of x and xx at start of fn:\n")
cat("address(x):", address(x), "\n")
cat("address(xx):", address(xx), "\n")
if(!is.na(xx) && x==xx){
cat("x=", xx, ", ret=", ret, " (memory)", fill=TRUE, sep="")
return(ret)
}
xx <<- x; ret <<- sum(x^2)
cat("x=", xx, ", ret=", ret, " (calculate)", fill=TRUE, sep="")
ret
}
fn
}
fn <- makeFn()
# Run the optimization process
optim(par=0.1, fn=fn, method="L-BFGS-B")
whose partial output (assuming no optimization run was done prior to running this code snippet) would be similar to the following:
Address of x and xx at start of fn:
address(x): 0000000013C89DA8
address(xx): 00000000192182D0
x=0.1, ret=0.010201 (calculate)
Address of x and xx at start of fn:
address(x): 0000000013C8A160
address(xx): 00000000192182D0
x=0.101, ret=0.010201 (calculate)
Address of x and xx at start of fn:
address(x): 0000000013C8A160
address(xx): 0000000013C8A160
x=0.099, ret=0.010201 (memory)
This problem does not happen with other optimization methods available in optim(), such as the default one.
Note: As mentioned, the data.table package can also be used to retrieve the memory address of objects, but here I am taking the opportunity to promote my recently released package envnames (which, other than retrieving an object's memory address, it also retrieves user-defined environment names from their memory address --among other things)

Related

R: instrument function to capture all assignments

Given a regular R function f, I'd like to be able to create a new function f_debug that acts just like f, but lets me keep track of all the assignments to function-local variables that happened inside it.
For example:
f <- function(x, y) {
z <- x + y
df <- data.frame(z=z)
df
}
# This function doesn't work as intended - would like it to (in the case of `f` above)
# write out a list containing `z` and `df` to an RDS file
capturing <- function(func) {
e <- new.env()
altered <- function(...) {
parent <- parent.frame()
e <- something...(func, environment(), parent, etc., etc.)
result <- func(...)
saveRDS(as.list(e), 'foo.rds')
result
}
environment(func) <- e
altered
}
f_debug <- capturing(f)
I'm not sure whether my knowledge gap to do this is large or small, anyone have a solution?
Solution 1: Steal the function's code
Here's a solution which doesn't return a new function which captures intermediate calculations, but rather calls the given function's code internally. There's some limitations, such as it probably only works with named arguments. Instead of storing the intermediate calculations as an RDS, it attaches them as an attribute.
capturing <- function(fun, ...) {
fun <- match.fun(fun)
code <- body(fun)
parent <- environment(fun)
env <- new.env(parent = parent)
for (val in names(list(...))) {
env[[val]] <- list(...)[[val]]
}
result <- eval(code, envir = env, enclos = parent.frame())
attr(result, "intermediate") <- env
result
}
my_add <- function(x, y) {
z <- x+y
u <- x-y
w <- x*y
x + y
}
intermediates <- function(x) {
attr(x, "intermediate", exact = TRUE)
}
value <- capturing(my_add, x = 1, y = 7)
ls(envir = intermediates(value))
#> [1] "u" "w" "x" "y" "z"
intermediates(value)$x
#> [1] 1
# Created on 2022-02-08 by the reprex package (v2.0.1)
Solution 2: Modify the function's code
One weakness of this solution is that if the chosen function features a call to on.exit(add=FALSE), some additional work needs to be done to modify the function so the internal environment is captured. However, it does work when the function accepts ... arguments.
my_add <- function(x, y) {
z <- x+y
u <- x-y
w <- x*y
x + y
}
insert_capture <- function(code) {
# `<<-` assigns into the global environment if no variable of the given name is found
# while traveling up to the global environment. If you need this assignment to go elsewhere,
# I'd recommend passing in `assign()`. Of course, you could also modify the `on.exit()`
# to use saveRDS.
parse(text=append(deparse(code),
"on.exit(._last_capture <<- environment(), add = TRUE)",
after = 1L))
}
capturing2 <- function(fun) {
fun <- match.fun(fun)
code <- insert_capture(body(fun))
body(fun) <- code
fun
}
my_add2 <- capturing2(my_add)
my_add2(1, 7)
#> [1] 8
ls(envir = ._last_capture)
#> [1] "u" "w" "x" "y" "z"
._last_capture$u
#> [1] -6
Created on 2022-02-08 by the reprex package (v2.0.1)
What you are describing is already implemented in base R with utils::dump.frames, in an even more sophisticated way. It saves the frame (environment) associated with each call in the call stack to an object of class "dump.frames", which you can explore retroactively with utils::debugger as if you had actually run your code under a debugger.
capturing <- function(func, ...) {
cc <- as.call(c(quote(utils::dump.frames), list(...)))
cc <- call("on.exit", cc, add = TRUE)
body(func) <- call("{", cc, body(func))
func
}
capturing injects the call on.exit(utils::dump.frames(...), add = TRUE) into the body of func and returns the modified function.
Here, ... is a list of arguments to dump.frames:
dumpto, a character string giving the name to be used for the "dump.frames" object
to.file, a logical flag indicating whether the "dump.frames" object should be assigned in the global environment or save-ed to paste0(dumpto, ".rda") in the current working directory
include.GlobalEnv, a logical flag indicating whether the global environment should be saved as well
A quick example, which you should try yourself:
tmp <- tempfile()
dir.create(tmp)
cwd <- setwd(tmp)
f <- function(x, y) {
z <- x + y
z + 1
}
g <- capturing(f, dumpto = "zzz", to.file = TRUE)
h <- function(a, b) {
d <- g(a, b)
d + 1
}
h12 <- h(1, 2)
load("zzz.rda")
zzz
## $`h(1, 2)`
## <environment: 0x14c16cb58>
##
## $`#2: g(a, b)`
## <environment: 0x14c16ca40>
##
## attr(,"error.message")
## [1] ""
## attr(,"class")
## [1] "dump.frames"
ls(zzz[[1L]])
## [1] "a" "b"
ls(zzz[[2L]])
## [1] "z" "x" "y"
utils::debugger(zzz)
## Message: Available environments had calls:
## 1: h(1, 2)
## 2: #2: g(a, b)
##
## Enter an environment number, or 0 to exit
## Selection: 2
## Browsing in the environment with call:
## #2: g(a, b)
## Called from: debugger.look(ind)
## Browse[1]> ls()
## [1] "x" "y" "z"
## Browse[1]> x == 1 && y == 2 && z == x + y
## [1] TRUE
## Browse[1]> Q
setwd(cwd)
unlink(tmp, recursive = TRUE)
See ?browser if you are unfamiliar with R's environment browser.
My capturing function has the limitation that on.exit calls in the body of func must also use add = TRUE. If you have written func yourself, then it is not much of a limitation at all, and passing add = TRUE is a good habit anyway.
Ultimately, there is no completely safe way to inject code into functions, but, in an interactive setting, I would say that this level of "unsafety" is fine.

Difference between applying a function() to list() and to new.env()?

Why do I get two different results if I print x$val? I get that the first one is a list and the second is an environment, but I do not understand what makes the result of x$val from the second chunk = NA
x <- list()
x$val <- 1
myfun <- function(x) {x$val <- x$val + NA}
myfun(x)
x$val
##[1] 1
x <- new.env()
x$val <- 18
myfun <- function(x) {x$val <- x$val + NA}
myfun(x)
x$val
##[1] NA
There are several issues here:
Return value A function returns the value of the last statement executed and in this case both instance of myfun return x$val which is NA (adding NA to any number gives NA) so they do return the same value.
Copy on modify If an object such as x is modified in a function the function creates a copy of the object and then modifies the copy. The original object outside the function is not changed.
Object identity Environments have an identity independently of their contents so changing the contents of an environment does not change the identity of the environment itself -- it only changes the contents. Thus changing the contents of an environment does not cause the environment to be copied within the function. (This is similar to pointers in C where a program can modify the pointed to data without modifying the pointer itself.) On the other hand lists do not have an identity distinct from their contents. Within a function modifying the contents of a list causes the list to be copied to a new list and then the new list is modified.
Example
Below, we use address from pryr to track the address of the list. For environments simply printing the environment will show its address so we don't need it for that. The trace statements below cause R to show the address upon entry and upon exit.
The address of the list is ...968 before entering the function and upon entry but after modifying it within the function it has become a new list at a new address ...200 which is local to the function and distinct from the list outside the function which is still at address ...968 .
library(pryr)
x <- list()
x$val <- 1
myfun_env <- function(x) {x$val <- x$val + NA}
trace(myfun_list, tracer = quote(print(address(x))), exit = quote(print(address(x))))
## [1] "myfun_list"
address(x)
## [1] "000000000bbbb968"
myfun_list(x)
## Tracing myfun_list(x) on entry
## [1] "000000000bbbb968"
## Tracing myfun_list(x) on exit
## [1] "000000000b368200"
## [1] NA
address(x)
## [1] "000000000bbbb968"
On the other hand in the case of an environment it has an identity distinct from its contents so changing the contents does not cause the environment to be copied to a new environment. The environment starts out at ...238 and never changes throughout the code.
x <- new.env()
x$val <- 18
myfun_env <- function(x) {x$val <- x$val + NA}
trace(myfun_env, tracer = quote(print(x)), exit = quote(print(x)))
## [1] "myfun_env"
x
## <environment: 0x000000000cac4238>
myfun_env(x)
## Tracing myfun_env(x) on entry
## <environment: 0x000000000cac4238>
## Tracing myfun_env(x) on exit
## <environment: 0x000000000cac4238>
x
## <environment: 0x000000000cac4238>
Environments are "reference objects" in R. That means that if you assign one to a new variable, changes to either copy will affect both copies. Lists are like most other objects and get copied on assignment.
So in your first example, myfun(x) makes a separate copy of the list x, and works on that in the function. It has no effect on the global variable x.
In your second example, myfun(x) makes a new reference to the environment x, and works on that in the function. That affects the original variable as well.
Ok, based on the legit comment of the risk of using <<- some examples.
risky solution
x <- list("val" = 1L)
myfun <- function(x) {x$val <<- x$val + NA}
myfun(x)
x
# $val
# [1] NA
risky solution going terribly wrong
x <- list("val" = "Please do not alter me at all")
y <- list("val" = 1L)
myfun <- function(x) {x$val <<- x$val + NA}
myfun(y)
x
# $val
# [1] NA
y
# $val
# [1] 1
let your function output your data
x <- list("val" = 1L)
myfun <- function(x) list("val" = x$val + NA)
x <- myfun(x)
x
# $val
# [1] NA
use the environment
y <- new.env()
y$val <- 18L
myfun <- function(x) {x$val <- x$val + NA}
myfun(y)
y$val
# [1] NA

R: preventing copies when passing a variable into a function

Hadley's new pryr package that shows the address of a variable is really great for profiling. I have found that whenever a variable is passed into a function, no matter what that function does, a copy of that variable is created. Furthermore, if the body of the function passes the variable into another function, another copy is generated. Here is a clear example
n = 100000
p = 100
bar = function(X) {
print(pryr::address(X))
}
foo = function(X) {
print(pryr::address(X))
bar(X)
}
X = matrix(rnorm(n*p), n, p)
print(pryr::address(X))
foo(X)
Which generates
> X = matrix(rnorm(n*p), n, p)
> print(pryr::address(X))
[1] "0x7f5f6ce0f010"
> foo(X)
[1] "0x92f6d70"
[1] "0x92f3650"
The address changes each time, despite the functions not doing anything. I'm confused by this behavior because I've heard R described as copy on write - so variables can be passed around but copies are only generated when a function wants to write into that variable. What is going on in these function calls?
For best R development is it better to not write multiple small functions, rather keep the content all in one function? I have also found some discussion on Reference Classes, but I see very little R developers using this. Is there another efficient way to pass the variable that I am missing?
I'm not entirely certain, but address may point to the memory address of the pointer to the object. Take the following example.
library(pryr)
n <- 100000
p <- 500
X <- matrix(rep(1,n*p), n, p)
l <- list()
for(i in 1:10000) l[[i]] <- X
At this point, if each element of l was a copy of X, the size of l would be ~3.5Tb. Obviously this is not the case as your computer would have started smoking. And yet the addresses are different.
sapply(l[1:10], function(x) address(x))
# [1] "0x1062c14e0" "0x1062c0f10" "0x1062bebc8" "0x10641e790" "0x10641dc28" "0x10641c640" "0x10641a800" "0x1064199c0"
# [9] "0x106417380" "0x106411d40"
pryr::address passes an unevaluated symbol to an internal function that returns its address in the parent.frame():
pryr::address
#function (x)
#{
# address2(check_name(substitute(x)), parent.frame())
#}
#<environment: namespace:pryr>
Wrapping of the above function can lead to returning address of a "promise". To illustrate we can simulate pryr::address's functionality as:
ff = inline::cfunction(sig = c(x = "symbol", env = "environment"), body = '
SEXP xx = findVar(x, env);
Rprintf("%s at %p\\n", type2char(TYPEOF(xx)), xx);
if(TYPEOF(xx) == PROMSXP) {
SEXP pr = eval(PRCODE(xx), PRENV(xx));
Rprintf("\tvalue: %s at %p\\n", type2char(TYPEOF(pr)), pr);
}
return(R_NilValue);
')
wrap1 = function(x) ff(substitute(x), parent.frame())
where wrap1 is an equivalent of pryr::address.
Now:
x = 1:5
.Internal(inspect(x))
##256ba60 13 INTSXP g0c3 [NAM(1)] (len=5, tl=0) 1,2,3,4,5
pryr::address(x)
#[1] "0x256ba60"
wrap1(x)
#integer at 0x0256ba60
#NULL
with further wrapping, we can see that a "promise" object is being constructed while the value is not copied:
wrap2 = function(x) wrap1(x)
wrap2(x)
#promise at 0x0793f1d4
# value: integer at 0x0256ba60
#NULL
wrap2(x)
#promise at 0x0793edc8
# value: integer at 0x0256ba60
#NULL
# wrap 'pryr::address' like your 'bar'
( function(x) pryr::address(x) )(x)
#[1] "0x7978a64"
( function(x) pryr::address(x) )(x)
#[1] "0x79797b8"
You can use the profmem package (I'm the author), to see what memory allocations take place. It requires that your R session was build with "profmem" capabilities:
capabilities()["profmem"]
## profmem
## TRUE
Then, you can do something like this:
n <- 100000
p <- 100
X <- matrix(rnorm(n*p), nrow = n, ncol = p)
object.size(X)
## 80000200 bytes
## No copies / no new objects
bar <- function(X) X
foo <- function(X) bar(X)
## One new object
bar2 <- function(X) 2*X
foo2 <- function(X) bar2(X)
profmem::profmem(foo(X))
## Rprofmem memory profiling of:
## foo(X)
##
## Memory allocations:
## bytes calls
## total 0
profmem::profmem(foo2(X))
## Rprofmem memory profiling of:
## foo2(X)
##
## Memory allocations:
## bytes calls
## 1 80000040 foo2() -> bar2()
## total 80000040

R type conversion expression() function()

I've been trying to write a program in R that implements Newton's method. I've been mostly successful, but there are two little snags that have been bothering me. Here's my code:
Newton<-function(f,f.,guess){
#f <- readline(prompt="Function? ")
#f. <- readline(prompt="Derivative? ")
#guess <- as.numeric(readline(prompt="Guess? "))
a <- rep(NA, length=1000)
a[1] <- guess
a[2] <- a[1] - f(a[1]) / f.(a[1])
for(i in 2:length(a)){
if(a[i] == a[i-1]){
break
}
else{
a[i+1] <- a[i] - f(a[i]) / f.(a[i])
}
}
a <- a[complete.cases(a)]
return(a)
}
I can't get R to recognize the functions f and f. if I try using readline() to prompt for user input. I get the error "Error in Newton() : could not find function "f."" However, if I comment out the readlines (as above), define f and f. beforehand, then everything works fine.
I've been trying to make R calculate the derivative of a function. The problem is that the class object with which R can take symbolic derivatives is expression(), but I want to take the derivative of a function() and have it give me a function(). In short, I'm having trouble with type conversion between expression() and function().
I have an ugly but effective solution for going from function() to expression(). Given a function f, D(body(f)[[2]],"x") will give the derivative of f. However, this output is an expression(), and I haven't been able to turn it back into a function(). Do I need to use eval() or something? I've tried subsetting, but to no avail. For instance:
g <- expression(sin(x))
g[[1]]
sin(x)
f <- function(x){g[[1]]}
f(0)
sin(x)
when what I want is f(0) = 0 since sin(0) = 0.
EDIT: Thanks all! Here's my new code:
Newton<-function(f,f.,guess){
g<-readline(prompt="Function? ")
g<-parse(text=g)
g.<-D(g,"x")
f<-function(x){eval(g[[1]])}
f.<-function(x){eval(g.)}
guess<-as.numeric(readline(prompt="Guess? "))
a<-rep(NA, length=1000)
a[1]<-guess
a[2]<-a[1]-f(a[1])/f.(a[1])
for(i in 2:length(a)){
if(a[i]==a[i-1]){break
}else{
a[i+1]<-a[i]-f(a[i])/f.(a[i])
}
}
a<-a[complete.cases(a)]
#a<-a[1:(length(a)-1)]
return(a)
}
This first problem arises because readline reads in a text string, whereas what you need is an expression. You can use parse() to convert the text string to an expression:
f <-readline(prompt="Function? ")
sin(x)
f
# [1] "sin(x)"
f <- parse(text = f)
f
# expression(sin(x))
g <- D(f, "x")
g
# cos(x)
To pass in values for the arguments in the function call in the expression (whew!), you can eval() it in an environment containing the supplied values. Nicely, R will allow you to supply those values in a list supplied to the envir= argument of eval():
> eval(f, envir=list(x=0))
# [1] 0
Josh has answered your question
For part 2 you could have used
g <- expression( sin(x) )
g[[1]]
# sin(x)
f <- function(x){ eval( g[[1]] ) }
f(0)
# [1] 0
f(pi/6)
# [1] 0.5
BTW, having recently written a toy which calculates fractal patterns based on root convergence of Newton's method in the complex plane, I can recommend you toss in some code like the following (where the main function's argument list includes "func" and "varname" ).
func<- gsub(varname, 'zvar', func)
funcderiv<- try( D(parse(text=func), 'zvar') )
if(class(funcderiv) == 'try-error') stop("Can't calculate derivative")
If you're more cautious, you could include a an argument "funcderiv" , and wrap my code in
if(missing(funcderiv)){blah blah}
Ahh, why not: here's my complete function for all to use and enjoy:-)
# build Newton-Raphson fractal
#define: f(z) the convergence per Newton's method is
# zn+1 = zn - f(zn)/f'(zn)
#record which root each starting z0 converges to,
# and to get even nicer coloring, record the number of iterations to get there.
# Inputs:
# func: character string, including the variable. E.g., 'x+ 2*x^2' or 'sin(x)'
# varname: character string indicating the variable name
# zreal: vector(preferably) of Re(z)
# zim: vector of Im(z)
# rootprec: convergence precision for the NewtonRaphson algorithm
# maxiter: safety switch, maximum iterations, after which throw an error
#
nrfrac<-function(func='z^5 - 1 ', varname = 'z', zreal= seq(-5,5,by=.1), zim, rootprec=1.0e-5, maxiter=1e4, drawplot=T, drawiterplot=F, ...) {
zreal<-as.vector(zreal)
if(missing(zim)) zim <- as.vector(zreal)
# precalculate F/F'
# check for differentiability (in R's capability)
# and make sure to get the correct variable name into the function
func<- gsub(varname, 'zvar', func)
funcderiv<- try( D(parse(text=func), 'zvar') )
if(class(funcderiv) == 'try-error') stop("Can't calculate derivative")
# Interesting "feature" of deparse : default is to limit each string to 60 or64
# chars. Need to avoid that here. Doubt I'd ever see a derivative w/ more
# than 500 chars, the max allowed by deparse. To do it right,
# need sum(nchar(funcderiv)) as width, and even then need to do some sort of
# paste(deparse(...),collapse='') to get a single string
nrfunc <- paste(text='(',func,')/(',deparse(funcderiv, width=500),')', collapse='')
# first arg to outer() will give rows
# Stupid Bug: I need to REVERSE zim to get proper axis orientation
zstart<- outer(rev(zim*1i), zreal, "+")
zindex <- 1:(length(zreal)*length(zim))
zvec <- data.frame(zdata=as.vector(zstart), zindex=zindex, itermap=rep(0,length(zindex)), badroot=rep(0,length(zindex)), rooterr=rep(0,length(zindex)) )
#initialize data.frame for zout.
zout=data.frame(zdata=rep(NA,length(zstart)), zindex=rep(NA,length(zindex)), itermap=rep(0,length(zindex)), badroot=rep(0,length(zindex)), rooterr=rep(0,length(zindex)))
# a value for rounding purposes later on; yes it works for rootprec >1
logprec <- -floor(log10(rootprec))
newtparam <- function(zvar) {}
body(newtparam)[2] <- parse(text=paste('newz<-', nrfunc, collapse=''))
body(newtparam)[3] <- parse(text=paste('return(invisible(newz))'))
iter <- 1
zold <- zvec # save zvec so I can return original values
zoutind <- 1 #initialize location to write solved values
while (iter <= maxiter & length(zold$zdata)>0 ) {
zold$rooterr <- newtparam(zold$zdata)
zold$zdata <- zold$zdata - zold$rooterr
rooterr <- abs(zold$rooterr)
zold$badroot[!is.finite(rooterr)] <- 1
zold$zdata[!is.finite(rooterr)] <- NA
# what if solvind = FFFFFFF? -- can't write 'nothing' to zout
solvind <- (zold$badroot >0 | rooterr<rootprec)
if( sum(solvind)>0 ) zout[zoutind:(zoutind-1+sum(solvind)),] <- zold[solvind,]
#update zout index to next 'empty' row
zoutind<-zoutind + sum(solvind)
# update the iter count for remaining elements:
zold$itermap <- iter
# and reduce the size of the matrix being fed back to loop
zold<-zold[!solvind,]
iter <- iter +1
# just wonder if a gc() call here would make any difference
# wow -- it sure does
gc()
} # end of while
# Now, there may be some nonconverged values, so:
# badroot[] is set to 2 to distinguish from Inf/NaN locations
if( zoutind < length(zindex) ) { # there are nonconverged values
# fill the remaining rows, i.e. zout.index:length(zindex)
zout[(zoutind:length(zindex)),] <- zold # all of it
zold$badroot[] <- 2 # yes this is safe for length(badroot)==0
zold$zdata[]<-NA #keeps nonconverged values from messing up results
}
# be sure to properly re-order everything...
zout<-zout[order(zout$zindex),]
zout$zdata <- complex(re=round(Re(zout$zdata),logprec), im=round(Im(zout$zdata),logprec) )
rootvec <- factor(as.vector(zout$zdata), labels=c(1:length(unique(na.omit(as.vector(zout$zdata))))))
#convert from character, too!
rootIDmap<-matrix(as.numeric(rootvec), nr=length(zim))
# to colorize very simply:
if(drawplot) {
colorvec<-rainbow(length(unique(as.vector(rootIDmap))))
imagemat<-rootIDmap
imagemat[,]<-colorvec[imagemat] #now has color strings
dev.new()
# all '...' arguments used to set up plot
plot(range((zreal)),range((zim)), t='n',xlab='real',ylab='imaginary',... )
rasterImage(imagemat, range(zreal)[1], range(zim)[1], range(zreal)[2], range(zim)[2], interp=F)
}
outs <- list(rootIDmap=rootIDmap, zvec=zvec, zout=zout, nrfunc=nrfunc)
return(invisible(outs))
}

Weird mapply behaviour: what have I missed?

The following code does not work as I expected:
a <- list(0, 1)
b <- list(0, 1)
# return a linear function with slope `a` and intercept `b`.
f <- function(a, b) function(x) a*x + b
# create a list of functions with different parameters.
fs <- mapply(f, a, b)
# test
fs[[1]](3)
# [1] 4 # expected zero!
fs[[2]](3)
# [1] 4
Can anyone tell me why?
NB: I've found a workaround, so I'm not looking for a different way to achieve the desired result. But I'm curious as to why this particular approach didn't work.
Update:
As of R 3.2.0, this now works as expected:
a <- list(0, 1)
b <- list(0, 1)
f <- function(a, b) function(x) a*x + b
fs <- mapply(f, a, b)
# test
fs[[1]](3)
# [1] 0
fs[[2]](3)
# [1] 4
This is the result of lazy evaluation -- all arguments are passed down the call tree as promises to avoid unnecessary execution and remain in this suspended state till R is convinced that they are used.
In your code you just populate functions with a same promise to a and same promise to b; then they all got committed to a last pair of vales. As #Tommy already showed, the solution is to force commitment by "using" the value before the function gets defined.
[Update] My initial analysis was correct but the conclusions were wrong :) Let's get to the conclusions after the analysis.
Here's some code demonstrating the effects:
x <- lapply(1:3, function(x) sys.frame(sys.nframe()))
x[[1]] # An environment
x[[2]] # Another environment
x[[3]] # Yet nother environment
x[[1]]$x # 3!!! (should be 1)
x[[2]]$x # 3!! (should be 2)
x[[3]]$x # 3 as expected
# Accessing the variable within the function will "fix" the weird behavior:
x <- lapply(1:3, function(x) {x; sys.frame(sys.nframe())})
x[[1]]$x # 1
x[[2]]$x # 2
x[[3]]$x # 3
So the work-around in your case:
f <- function(a, b) { a;b; function(x) a*x + b }
Btw, as #James notes there is a force function that makes accessing a variable more explicit:
f <- function(a, b) { force(a);force(b); function(x) a*x + b }
Conclusions
Well, as #mbq and #hadley noted, this is due to lazy evaluation. It' easier to show with a simple for-loop:
fs <- list(); for(i in 1:2) fs[[i]] <- f(a[[i]], b[[i]])
The function f's x argument will not get the value of a[[i]] (which is 0), but the whole expression and the environment where a and i exist. When you access x, it gets evaluated and therefore uses the i at the time of evaluation. If the for-loop has moved on since the call to f, you get the "wrong" result...
Initially I said that this was due to a bug in *apply, which it isn't. ...but since I hate to be wrong, I can point out that *apply DOES have a bug (or perhaps more of an inconsistency) in these cases:
lapply(11:12, function(x) sys.call())
#[[1]]
#FUN(11:12[[1L]], ...)
#
#[[2]]
#FUN(11:12[[2L]], ...)
lapply(11:12, function(x) function() x)[[1]]() # 12
lapply(11:12, function(x) function() x)[[2]]() # 12
As you see above, the lapply code says it calls the function with 11:12[[1L]]. If you evaluate that "later" you should still get the value 11 - but you actually get 12!
This is probably due to the fact that lapply is implemented in C code for performance reasons and cheat a bit, so the expression that it shows is not the expression that gets evaluated - ergo, a bug...
QED

Resources