[Rd] all.equal applied to function closures

Duncan Murdoch murdoch@dunc@n @end|ng |rom gm@||@com
Tue Dec 1 17:08:25 CET 2020


One more comment:  it might be worthwhile reporting on a case where 
identical(e1, e2) fails when those are the environments associated with 
two functions (though I think not by default in all.equal.environment).

Functions can modify variables in their environment, so examples like 
the open.account example in the R-intro manual would distinguish between 
ross and robert even if the balances matched.

Duncan Murdoch

On 01/12/2020 10:37 a.m., Bill Dunlap wrote:
> Probably all.equal.environment's do1() could be enhanced to do the 
> recursion (and look at the environments' attributes).  I wrote a 
> separate function because it was easier to experiment that way (e.g., 
> when to stop recursing - it stops when one environment is a top-level 
> environment or the empty environment).  I've been thinking about similar 
> recursion options for ls.str() - it would make it easier to debug 
> refClass and R6 code where the data is somewhere in a stack of environments.
> 
>  > E1 <- list2env(list(X=1.1, Y=1.2), parent=list2env(list(p=1.3), 
> parent=baseenv()))
>  > E2 <- list2env(list(X=1.1, Y=1.2), parent=list2env(list(p=1.4), 
> parent=baseenv()))
>  > base::all.equal.environment(E1,E2)
> [1] TRUE
>  > globalenv()$all.equal.environment(E1,E2)
> [1] "<parent.env> Component “p”: Mean relative difference: 0.07692308"
>  >
>  > E3 <- list2env(list(X=1.1, Y=1.2), parent=list2env(list(p=1.5), 
> parent=new.env(parent=baseenv())))
>  > base::all.equal.environment(E1,E3)
> [1] TRUE
>  > globalenv()$all.equal.environment(E1,E3)
> [1] "<parent.env> Component “p”: Mean relative difference: 0.1538462"
> [2] "<parent.env> <parent.env> target is <environment: base> and current 
> is <environment: 0x564e806705c8>"
> 
> On Tue, Dec 1, 2020 at 1:31 AM Martin Maechler 
> <maechler using stat.math.ethz.ch <mailto:maechler using stat.math.ethz.ch>> wrote:
> 
>      >>>>> Bill Dunlap
>      >>>>>     on Mon, 30 Nov 2020 13:41:54 -0800 writes:
> 
>          > To make the comparison more complete, all.equal.environment
>     could compare
>          > the parents of the target and current environments.  That
>     would have to be
>          > recursive but could stop at the first 'top level environment'
>     (the global,
>          > empty, or a package-related environment generally) and use
>     identical
>          > there.  E.g.,
> 
>          > > f1 <- function(x) (function(){ expx <- exp(x) ; function(y)
>     y + expx})()
>          > > all.equal(f1(2), f1(3))
>          > [1] "Environments: Component “expx”: Mean relative
>     difference: 1.718282"
>          >
>          > [2] "Environments: <parent.env> Component “x”: Mean relative
>     difference:
>          > 0.5"
> 
>          > This is from the following, where I avoided putting the existing
>          > non-recursive all.equal.environment into the body of this one.
> 
>          > all.equal.environment <-
>          > function(target, current, ...)
>          > {
>          >     .all.equal.environment <- base::all.equal.environment #
>     temporary hack
>          >     stopifnot(is.environment(target), is.environment(current))
>          >     if (identical(target, current)) {
>          >         TRUE
>          >     } else {
>          >         msg <- NULL # TODO: check attributes
>          >         # deal with emptyenv now since parent.env(emptyenv())
>     gives error
>          >         # and topenv(emptyenv()) gives GlobalEnv
>          >         eTarget <- identical(target, emptyenv()) ||
>          > identical(target,topenv(target))
>          >         eCurrent <- identical(current, emptyenv()) ||
>          > identical(current,topenv(current))
>          >         if (eTarget || eCurrent) {
>          >             msg <- c(msg, paste("target is", format(target),
>     "and current
>          > is", format(current)))
>          >         } else {
>          >             thisComparison <- .all.equal.environment(target,
>     current, ...)
>          >             if (!isTRUE(thisComparison)) {
>          >                 msg <- c(msg, thisComparison)
>          >             }
>          >             parentComparison <- Recall(parent.env(target),
>          > parent.env(current), ...)
>          >             if (!isTRUE(parentComparison)) {
>          >                 msg <- c(msg, paste("<parent.env>",
>     parentComparison))
>          >             }
>          >         }
>          >         if (is.null(msg) || isTRUE(msg)) TRUE else msg
>          >     }
>          > }
> 
>     Thank you, Duncan and Bill (and Kevin for bringing up the
>     topic).
> 
>     I agree  all.equal() should work better with functions,
> 
>     and I think probably it would make sense to define  all.equal.function()
>     rather than put this into all.equal.default()
> 
>     However, it's not quite clear if it is always desirable to check the
>     environments as well notably as that *is* done recursively.
> 
>     Bill, I'm sure you've noticed that we did write  all.equal.environment()
>     to work recursively... Actually, I had worked quite a bit at
>     that, too long ago to remember details, but the relevant svn log
>     entry is
>     ------------------------------------------------------------------------
>     r66640 | maechler | 2014-09-18 22:10:20 +0200 (Thu, 18 Sep 2014) | 1
>     line
> 
>     more sophisticated all.equal.environment(): no longer "simple"
>     infinite recursions
>     ------------------------------------------------------------------------
> 
>     Are you sure that code with the internal recursive do1()
>     function should/could not be amended where needed?
> 
>     Martin
> 
>          > On Mon, Nov 30, 2020 at 10:42 AM Duncan Murdoch
>     <murdoch.duncan using gmail.com <mailto:murdoch.duncan using gmail.com>>
>          > wrote:
>          >
>          > > On 30/11/2020 1:05 p.m., Kevin Van Horn via R-devel wrote:
>          > > > Consider the following code:
>          > > >
>          > > >      f <- function(x)function(y){x+y}
>          > > >      all.equal(f(5), f(0))
>          > > >
>          > > > This returns TRUE, when it should return FALSE; I think
>     it’s hard to
>          > > make the case that f(5) and f(0) are “approximately equal”
>     in any
>          > > meaningful sense. Digging into the code for all.equal(), I
>     see that
>          > > all.equal(f(5), f(0)) results in a call to
>     all.equal.language(f(5), f(0)),
>          > > which only compares the function texts for equality.
>          > > >
>          > > > If it is decided to leave this behavior as-is, then at
>     least it should
>          > > be documented. Currently I cannot find any documentation
>     for all.equal
>          > > applied to functions.
>          > >
>          > > Clearly it should also compare the environments of the two
>     functions,
>          > > then it would see a difference:
>          > >
>          > >  > all.equal(environment(f(5)), environment(f(0)))
>          > > [1] "Component “x”: Mean relative difference: 1"
>          > >
>          > > Changing the first few lines from
>          > >
>          > >      if (is.language(target) || is.function(target))
>          > >          return(all.equal.language(target, current, ...))
>          > >
>          > > to
>          > >
>          > >      if (is.function(target)) {
>          > >          msg <- all.equal.language(target, current, ...)
>          > >          if (isTRUE(msg)) {
>          > >              msg <- all.equal.environment(environment(target),
>          > > environment(current), ...)
>          > >              if (is.character(msg))
>          > >                msg <- paste("Environments:", msg)
>          > >          }
>          > >          return(msg)
>          > >      }
>          > >      if (is.language(target))
>          > >          return(all.equal.language(target, current, ...))
>          > >
>          > > would fix it.
>          > >
>          > > Duncan Murdoch
>          > >
>          > > ______________________________________________
>          > > R-devel using r-project.org <mailto:R-devel using r-project.org>
>     mailing list
>          > > https://stat.ethz.ch/mailman/listinfo/r-devel
>     <https://stat.ethz.ch/mailman/listinfo/r-devel>
>



More information about the R-devel mailing list