[R-pkg-devel] Trying to work around missing functionality

Georgi Boshnakov georgi@bo@hn@kov @ending from m@nche@ter@@c@uk
Tue Aug 28 18:27:50 CEST 2018


If you don't insist on putting the variable in the global environment, 
variations of the following give a cleaner solution:

TraceSetup_1 <- local({
    ifn = 0
    igr = 0
    ftrace = FALSE
    fn = NA
    gr = NA

    function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
        ifn    <<- ifn
        igr    <<- igr
        ftrace <<- ftrace
        fn     <<- fn
        gr     <<- gr
        parent.env(environment())
    }
})

For example,

TraceSetup_1 <- local({
+     ifn = 0
+     igr = 0
+     ftrace = FALSE
+     fn = NA
+     gr = NA
+     function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
+         ifn    <<- ifn
+         igr    <<- igr
+         ftrace <<- ftrace
+         fn     <<- fn
+         gr     <<- gr
+         parent.env(environment())
+     }
+ })
> 
> e <- TraceSetup_1(fn = function(x) x^2)
> ls(e)
[1] "fn"     "ftrace" "gr"     "ifn"    "igr"   
> e$fn
function(x) x^2

## let's change 'fn':
> e$fn <- function(x) x^4
> e$fn
function(x) x^4


Note that the environment is always the same, so can be accessed from anywhere in your code:

> e2 <- environment(TraceSetup_1)
> e2
<environment: 0x000000000d1af620>
> identical(e2, e)
[1] TRUE
> 

If you need a new environment every time, a basic setup might be:

TraceSetup_2 <- local({
    staticVar1 <- NULL
    ## other variables here
    
    function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
        ## force evaluation of the arguments
        ifn    
        igr    
        ftrace 
        fn     
        gr     
        environment()
    }
})

There is no need for local() here but usually one needs also some static variables.
Now every call gives a different environment  (but all have the same parent):

ea <- TraceSetup_2(fn = function(x) x^2 - 2*x + 1)
> ls(ea)
[1] "fn"     "ftrace" "gr"     "ifn"    "igr"   
> ea$fn
function(x) x^2 - 2*x + 1
> 
> eb <- TraceSetup_2(fn = function(x) x^2 + 1)
> eb$fn
function(x) x^2 + 1
> 
> ## ea$fn is still the same:
> ea$fn
function(x) x^2 - 2*x + 1
>

Obviously, in this case some further arrangements are  needed for the environments to be made available to the external world.

Hope this helps,
Georgi Boshnakov


-----Original Message-----
From: R-package-devel [mailto:r-package-devel-bounces using r-project.org] On Behalf Of J C Nash
Sent: 28 August 2018 14:18
To: Fox, John; Richard M. Heiberger
Cc: List r-package-devel
Subject: Re: [R-pkg-devel] Trying to work around missing functionality

Indeed, it appears that globalVariables must be outside the function. However, I had quite a bit of
fiddle to get things to work without errors or warnings or notes. While I now have a package that
does not complain with R CMD check, I am far from satisfied that I can give a prescription. I had
to remove lines in the rootfinder like
   envroot$fn <- fn
that were used to set the function to be used inside my instrumented function, and instead
call TraceSetup(fn=fn, ...) where a similar statement was given. Why that worked while the direct
assignment (note, not a <<- one) did not, I do not understand. However, I will work with this for
a while and try to get a better handle on it.

Thanks for the pointer. As an old-time programmer from days when you even set the add table, I'm
still uncomfortable just putting code in a directory and assuming it will be executed, i.e., the
globals.R file. However, I now have this set to establish the global structure as follows

> ## Put in R directory. 
> if(getRversion() >= "2.15.1") { utils::globalVariables(c('envroot')) } # Try declaring here 
> groot<-list(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA, label="none")
> envroot <- list2env(groot) # Note globals in FnTrace

Then TraceSetup() is

> TraceSetup <- function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){
>    envroot$ifn <- ifn
>    envroot$igr <- igr
>    envroot$ftrace <- ftrace
>    envroot$fn <- fn
>    envroot$gr <- gr
>    return()
> }

and it is called at the start of the rootfinder routine.

Thus I am establishing a global, then (re-)setting values in TraceSetup(), then
incrementing counters etc. in the instrumented FnTrace() that is the function for which I find
the root, which calls fn() given by the "user". Messy, but I can now track progress and measure
effort.

I'm sure there are cleaner solutions. I suggest offline discussion would be better until such
options are clearer.

Thanks again.

JN



On 2018-08-28 12:01 AM, Fox, John wrote:
> Hi John,
> 
> It's possible that I didn’t follow what you did, but it appears as if you call globalVariables() *inside* the function. Instead try to do as Richard Heiberger suggested and place the call outside of the function, e.g., in a source file in the package R directory named globals.R. (Of course, the name of the source file containing the command isn’t important.)
> 
> I hope this helps,
>  John
> 
> -----------------------------------------------------------------
> John Fox
> Professor Emeritus
> McMaster University
> Hamilton, Ontario, Canada
> Web: https://socialsciences.mcmaster.ca/jfox/
> 
> 
> 
>> -----Original Message-----
>> From: R-package-devel [mailto:r-package-devel-bounces using r-project.org] On
>> Behalf Of J C Nash
>> Sent: Monday, August 27, 2018 8:44 PM
>> To: Richard M. Heiberger <rmh using temple.edu>
>> Cc: List r-package-devel <r-package-devel using r-project.org>
>> Subject: Re: [R-pkg-devel] Trying to work around missing functionality
>>
>> Unfortunately, makes things much worse. I'd tried something like this already.
>>
>>> * checking examples ... ERROR
>>> Running examples in ‘rootoned-Ex.R’ failed The error most likely
>>> occurred in:
>>>
>>>> ### Name: rootwrap
>>>> ### Title: zeroin: Find a single root of a function of one variable within
>>>> ###   a specified interval.
>>>> ### Aliases: rootwrap
>>>> ### Keywords: root-finding
>>>>
>>>> ### ** Examples
>>>>
>>>> # Dekker example
>>>> # require(rootoned)
>>>> dek <- function(x){ 1/(x-3) - 6 }
>>>> r1 <- rootwrap(dek, ri=c(3.0000001, 6), ftrace=TRUE,
>>>> method="uniroot")
>>> Error in registerNames(names, package, ".__global__", add) :
>>>   The namespace for package "rootoned" is locked; no changes in the global
>> variables list may be made.
>>> Calls: rootwrap -> TraceSetup -> <Anonymous> -> registerNames
>>> Execution halted
>>
>> Also had to use utils::globalVariables( ...
>>
>> JN
>>
>>
>> On 2018-08-27 08:40 PM, Richard M. Heiberger wrote:
>>> Does this solve the problem?
>>>
>>> if (getRversion() >= '2.15.1')
>>>   globalVariables(c('envroot'))
>>>
>>> I keep this in file R/globals.R
>>>
>>> I learned of this from John Fox's use in Rcmdr.
>>>
>>> On Mon, Aug 27, 2018 at 8:28 PM, J C Nash <profjcnash using gmail.com>
>> wrote:
>>>> In order to track progress of a variety of rootfinding or
>>>> optimization routines that don't report some information I want, I'm
>>>> using the following setup (this one for rootfinding).
>>>>
>>>> TraceSetup <- function(ifn=0, igr=0, ftrace=FALSE, fn=NA, gr=NA){ #
>>>> JN: Define globals here
>>>>    groot<-list(ifn=ifn, igr=igr, ftrace=ftrace, fn=fn, gr=gr, label="none")
>>>>    envroot <<- list2env(groot) # Note globals in FnTrace
>>>>    ## This generates a NOTE that
>>>>    ## TraceSetup: no visible binding for '<<-' assignment to ‘envroot’
>>>> ##   envroot<-list2env(groot, parent=.GlobalEnv) # Note globals in FnTrace -
>> - this does NOT work
>>>>    ## utils::globalVariables("envroot") # Try declaring here --
>>>> causes errors # end globals
>>>>    envroot
>>>> }
>>>>
>>>> FnTrace <- function(x,...) {
>>>>   # Substitute function to call when rootfinding
>>>>   # Evaluate fn(x, ...)
>>>>     val <- envroot$fn(x, ...)
>>>>     envroot$ifn <- envroot$ifn + 1 # probably more efficient ways
>>>>     if (envroot$ftrace) {
>>>>        cat("f(",x,")=",val," after ",envroot$ifn," ",envroot$label,"\n")
>>>>     }
>>>>     val
>>>> }
>>>>
>>>>
>>>> Perhaps there are better ways to do this, but this does seem to work quite
>> well.
>>>> It lets me call a rootfinder with FnTrace and get information on evaluations
>> of fn().
>>>> (There's another gr() routine, suppressed here.)
>>>>
>>>> However, R CMD check gives a NOTE for
>>>>
>>>>   TraceSetup: no visible binding for global variable ‘envroot’
>>>>   Undefined global functions or variables:
>>>>     envroot
>>>>
>>>> The commented lines in TraceSetup suggest some of the things I've
>>>> tried. Clearly I don't fully comprehend how R is grinding up the
>>>> code, but searches on the net seem to indicate I am far from alone. Does
>> anyone have any suggestion of a clean way to avoid the NOTE?
>>>>
>>>> JN
>>>>
>>>> ______________________________________________
>>>> R-package-devel using r-project.org mailing list
>>>> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>>
>> ______________________________________________
>> R-package-devel using r-project.org mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-package-devel

______________________________________________
R-package-devel using r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel


More information about the R-package-devel mailing list