[R] Environmental problems.

Duncan Murdoch murdoch.duncan at gmail.com
Tue Feb 25 23:29:14 CET 2014


On 14-02-25 4:43 PM, Rolf Turner wrote:
>
> Please see at end.
>
> On 26/02/14 01:13, Duncan Murdoch wrote:
>> On 14-02-25 3:08 AM, Rolf Turner wrote:
>>>
>>> I have a function that makes use of the ode() function from the
>>> "deSolve" package.  I am trying to find a way of getting it to put out a
>>> "progress report" every "t.int" time units (by "progress report" I just
>>> mean reporting what time it's got up to).
>>>
>>> I thought to put code something like the following in my "func" function
>>> that gets called by (is an argument to) ode():
>>>
>>>> cat("Before: time =",tt,"tdone =",tdone,"diff =",tt-tdone,"\n")
>>>> if(tt - tdone >= 0.1-sqrt(.Machine$double.eps)) {
>>>>       cat("Prog. Rep.: time =",tt,"tdone =",tdone,"diff =",tt-tdone,"\n")
>>>>       assign("tdone",tt,envir=parent.env(environment()))
>>>> }
>>>> cat("After: time =",tt,"tdone =",tdone,"diff =",tt-tdone,"\n")
>>>
>>> The object "tdone" gets initialized (to 0) outside of func(), so there
>>> is not a problem with "tdone" not being found the first time that func()
>>> gets called by ode().  (I'm hardwiring "t.int=0.1" in the forgoing just
>>> for test/illustration purposes.)  The "Before" and "After" cat()-s are
>>> there to demonstrate what goes wrong.
>>>
>>> What goes wrong is that I get no progress report and tdone remains equal
>>> to 0 until tt reaches 0.1. As desired. I then get a progress report and
>>> tdone gets set equal to the first value of tt which is greater than 0.1.
>>> As desired.
>>>
>>> Then I get no further progress reports and tdone gets set equal to tt at
>>> every call to func() --- even though tt - tdone = 0 which is less than
>>> 0.1 so the assignment of tdone cannot occur.  And yet it does, keeping
>>> the difference equal to 0.  (*Not* as desired!)
>>>
>>> So the function is recognizing that the difference is less than 0.1 in
>>> that it does not execute the cat() statement.  Yet it executes the
>>> assign() statement.  This is clearly impossible! :-)  But it happens.
>>>
>>> The output from the cat()-ing, around time = 0.1, looks like:
>>>
>>>> Before: time = 0.09364548 tdone = 0 diff = 0.09364548
>>>> After: time = 0.09364548 tdone = 0 diff = 0.09364548
>>>> Before: time = 0.0975779 tdone = 0 diff = 0.0975779
>>>> After: time = 0.0975779 tdone = 0 diff = 0.0975779
>>>> Before: time = 0.0975779 tdone = 0 diff = 0.0975779
>>>> After: time = 0.0975779 tdone = 0 diff = 0.0975779
>>>> Before: time = 0.09698997 tdone = 0 diff = 0.09698997
>>>> After: time = 0.09698997 tdone = 0 diff = 0.09698997
>>>> Before: time = 0.1009224 tdone = 0 diff = 0.1009224
>>>> Prog. Rep.: time = 0.1009224 tdone = 0 diff = 0.1009224
>>>> After: time = 0.1009224 tdone = 0.1009224 diff = 0
>>>> Before: time = 0.1009224 tdone = 0.1009224 diff = 0
>>>> After: time = 0.1009224 tdone = 0.1009224 diff = 0
>>>> Before: time = 0.1003344 tdone = 0.1003344 diff = 0  <--------------|
>>>> After: time = 0.1003344 tdone = 0.1003344 diff = 0
>>>> Before: time = 0.1042669 tdone = 0.1042669 diff = 0
>>>> After: time = 0.1042669 tdone = 0.1042669 diff = 0
>>>
>>> It's at that line indicated by "<----|", 4 lines from the bottom of the
>>> forgoing display, where things go to hell in a handcart.  Why (how on
>>> earth can) tdone change from 0.1009224 to 0.1003344, given that the
>>> difference is 0 whence no assignment of tdone should take place?
>>>
>>> What am I not seeing?  Can anyone help me out?  I'm going mad!
>>> ***MAD*** I tell you! :-)
>>>
>>> Suggestions as to a better way of accomplishing my desired goal of
>>> producing progress reports would also be welcome.
>>>
>>> I am not at all sure that assigning "tdone" in parent.env(environment())
>>> is the right thing to do.  I need to assign it in such a way and in such
>>> a location that its value will persist
>>> from call to call of "func".  Words of wisdom about this would be
>>> gratefully received.  (I don't really grok environments.  I just try
>>> things until *something* works!)
>>
>> You don't show us everything (we want the full Monty!), so it's hard to
>> say what's going wrong.  My guess would be that you have both a local
>> copy as well as a parent copy of some variable, and your test is
>> consulting the wrong one.
>>
>> A better approach to what you are doing (better since yours modifies
>> things in the global environment, and that can be dangerous; other code
>> might modify it too) is the following.  Create your func in a local
>> block, and it will have it's own mini-environment associated with it.
>> Put tdone there.  For example,
>>
>> func <- local({
>>     # local() creates an environment; that's where tdone will live
>>     tdone <- 0
>>
>>     # this function's environment will be the one created by
>>     # local(), so it will see tdone, but other code won't see it.
>>
>>     function(x) {
>>       # the timing stuff
>>       tt <- Sys.time()
>>       if (tt - tdone > 0.1) {
>>         cat("Prog rep...")
>>         tdone <<- tt
>>       }
>>       # now the real stuff
>>       ...
>>     })
>
> (a) The only relevant variable is "tdone"; I have grepped on "tdone" in
> the code.  There are no other assignments than the one enclosed in the
> if statement and the initialization.  I swear they are out to get me.
>
> (b) Your "local" idea looks like it should solve the problem.  I tried
> it in a toy example and it worked like a charm.  In the real example I
> get "Error: object 'tdone' not found".
>
> (c) In respect of giving you the full Monty: I have attached a tar-ball
> and a zip archive --- hope that they get through the listserv
> machinations --- both containing all the necessary code to demonstrate
> the phenomenon.  Unpack, source all the *.R files, then source
> "demoScript".  You'll get a browser() call inside the "func" (which is
> actually called "scrF"). Type "tdone" and you'll get the error message.
>    Press "return" again, and it all falls over (due to the same error).
>
> Somehow, due to the fact that "scrF" gets called by ode() which gets
> called by xsolve.disc(), the local environment gets hidden.  I swear
> that they are still out to get me.

One problem in your scrF:  you have

tdone <- tt

rather than

tdone <<- tt

That creates a new local copy, it doesn't update the one in the parent 
environment.  That doesn't explain a "not found" error, but it would 
cause problems...

The thing that causes the "not found" error is in xsolve.disc.R, where 
you replace the environment of scrF with new.env().  That wipes out the 
tiny one that local() created, and replaces it with an empty one whose 
parent is the evaluation frame of xsolve.disc.

Generally speaking it's a bad idea to replace the environment of 
functions, it's hard to get it right.  It's better to create the right 
one in the first place.

Duncan Murdoch




More information about the R-help mailing list