From b.rowlingson at lancaster.ac.uk  Fri May 22 18:28:42 2009
From: b.rowlingson at lancaster.ac.uk (Barry Rowlingson)
Date: Fri, 22 May 2009 17:28:42 +0100
Subject: [Rd] Scope problem?
Message-ID: <d8ad40b50905220928p5235b38fi69361e4c7f7cd91d@mail.gmail.com>

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



[Rd] Scope problem?
Barry Rowlingson