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? R 2.9.0 on Ubuntu. Barry
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
Martin Maechler
2009-May-25 15:08 UTC
[Rd] "interpolator constructor"; was "Scope problem?"
Hi Barry, this is just a side-remark, probably not at all something you were interested in, but to be put along this thread in the list archives, in case some readers are side-tracked there ...>>>>> "BaRow" == Barry Rowlingson <b.rowlingson at lancaster.ac.uk> >>>>> on Fri, 22 May 2009 17:28:42 +0100 writes:BaRow> I've just spent today trying to fix a Heisenbug... BaRow> this function returns a linear interpolator function: BaRow> interpOne <- function(xl,yl){ BaRow> f = function(data){ BaRow> t = (data-min(xl))/(max(xl)-min(xl)) BaRow> return(min(yl)+t*(max(yl)-min(yl))) BaRow> } BaRow> return(f) BaRow> } >> k=interpOne(c(0,1),c(4,5)) >> k(0.5) BaRow> [1] 4.5 Note that "base R" has already two such functions, namely splinefun() and approxfun(), both returning a *function* as in your examples. Best, Martin Maechler, ETH Zurich BaRow> and this function uses the above to return a function that returns a BaRow> piece-wise linear interpolator function: BaRow> mr <- function(){ BaRow> parts = list() BaRow> ranges = rbind(c(0,1),c(1,2),c(2,3)) BaRow> domains = rbind(c(3,4),c(5,6),c(2,8)) BaRow> for(i in 1:length(ranges[,1])){ BaRow> parts[[i]] = interpOne(ranges[i,],domains[i,]) BaRow> } BaRow> f = function(d){ BaRow> pos = sum(d>ranges[,1]) BaRow> cat("using pos = ",pos,"\n") BaRow> return(parts[[pos]](d)) BaRow> } BaRow> return(f) BaRow> } BaRow> m = mr() BaRow> The 'ranges' and 'domains' vectors describe the pieces. But this doesn't work: >> m(0.5) BaRow> using pos = 1 BaRow> [1] -7 BaRow> - but it should be 3.5 (since 0.5 is in the first piece, and that BaRow> then interpolates between 3 and 4). What about the other pieces: >> m(1.5) BaRow> using pos = 2 BaRow> [1] -1 >> m(2.5) BaRow> using pos = 3 BaRow> [1] 5 BaRow> - which looks like it's using the last set of range/domain pairs each BaRow> time. Curious, I thought. BaRow> So I thought I'd evaluate the functions as they are created in the BaRow> list to see what's going on. Change the loop to print out: BaRow> for(i in 1:length(ranges[,1])){ BaRow> parts[[i]] = interpOne(ranges[i,],domains[i,]) BaRow> cat("part ",i," at zero = ",parts[[i]](0),"\n") BaRow> } BaRow> and try: >> m=mr() BaRow> part 1 at zero = 3 BaRow> part 2 at zero = 4 BaRow> part 3 at zero = -10 BaRow> looks good, those are the intercepts of my pieces... but now: >> m(0.5) BaRow> using pos = 1 BaRow> [1] 3.5 >> m(1.5) BaRow> using pos = 2 BaRow> [1] 5.5 >> m(2.5) BaRow> using pos = 3 BaRow> [1] 5 BaRow> Woah! It's now working! Trying to observe the thing changes it? A Heisenbug! BaRow> I can only think it's my misunderstanding of some aspect of R's BaRow> scoping and evaluation rules. Does evaluating the functions within BaRow> that loop cause a copy of some environment to be made, or a 'lazy BaRow> evaluation' to be evaluated? Or a 'promise' to be fulfilled? I don't BaRow> really understand those terms, I'd just hoped functions ran in the BaRow> environment they were created in. Seems sometimes they do, sometimes BaRow> they dont... What's going on? BaRow> R 2.9.0 on Ubuntu. BaRow> Barry BaRow> ______________________________________________ BaRow> R-devel at r-project.org mailing list BaRow> https://stat.ethz.ch/mailman/listinfo/r-devel