On 5/22/2009 12:28 PM, Barry Rowlingson wrote:
I've just spent today trying to fix a Heisenbug...
this function returns a linear interpolator function:
interpOne <- function(xl,yl){
f = function(data){
t = (data-min(xl))/(max(xl)-min(xl))
return(min(yl)+t*(max(yl)-min(yl)))
}
return(f)
}
k=interpOne(c(0,1),c(4,5))
k(0.5)
[1] 4.5
and this function uses the above to return a function that returns a
piece-wise linear interpolator function:
mr <- function(){
parts = list()
ranges = rbind(c(0,1),c(1,2),c(2,3))
domains = rbind(c(3,4),c(5,6),c(2,8))
for(i in 1:length(ranges[,1])){
parts[[i]] = interpOne(ranges[i,],domains[i,])
}
f = function(d){
pos = sum(d>ranges[,1])
cat("using pos = ",pos,"\n")
return(parts[[pos]](d))
}
return(f)
}
m = mr()
The 'ranges' and 'domains' vectors describe the pieces. But this doesn't work:
m(0.5)
using pos = 1
[1] -7
- but it should be 3.5 (since 0.5 is in the first piece, and that
then interpolates between 3 and 4). What about the other pieces:
m(1.5)
using pos = 2
[1] -1
m(2.5)
using pos = 3
[1] 5
- which looks like it's using the last set of range/domain pairs each
time. Curious, I thought.
So I thought I'd evaluate the functions as they are created in the
list to see what's going on. Change the loop to print out:
for(i in 1:length(ranges[,1])){
parts[[i]] = interpOne(ranges[i,],domains[i,])
cat("part ",i," at zero = ",parts[[i]](0),"\n")
}
and try:
> m=mr()
part 1 at zero = 3
part 2 at zero = 4
part 3 at zero = -10
looks good, those are the intercepts of my pieces... but now:
> m(0.5)
using pos = 1
[1] 3.5
m(1.5)
using pos = 2
[1] 5.5
m(2.5)
using pos = 3
[1] 5
Woah! It's now working! Trying to observe the thing changes it? A Heisenbug!
I can only think it's my misunderstanding of some aspect of R's
scoping and evaluation rules. Does evaluating the functions within
that loop cause a copy of some environment to be made, or a 'lazy
evaluation' to be evaluated? Or a 'promise' to be fulfilled? I don't
really understand those terms, I'd just hoped functions ran in the
environment they were created in. Seems sometimes they do, sometimes
they dont... What's going on?
I think it's lazy evaluation that gets you. I haven't stepped through
your code, but have done a similar one recently, and this is my guess
about what happens:
- interpOne creates the function, but never evaluates xl or yl.
- You call it several times, to create a number of functions, but still
never evaluate xl or yl. They are left as promises to evaluate
ranges[i,] and domains[i,] in the environment of that loop.
- Finally, you start evaluating those created functions, and it's at
that point that xl and yl get forced. Since i is now on the last value,
they get the wrong values set.
Putting force(xl); force(yl) into your interpOne definition (so they get
executed when interpOne is called, not just when the returned function
is called) should work.
Duncan Murdoch
______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel