[Rd] R (development) changes in arith, logic, relop with (0-extent) arrays

Martin Maechler maechler at stat.math.ethz.ch
Tue Sep 6 22:26:31 CEST 2016


Yesterday, changes to R's development version were committed, relating
to arithmetic, logic ('&' and '|') and
comparison/relational ('<', '==') binary operators
which in NEWS are described as

  SIGNIFICANT USER-VISIBLE CHANGES:

         [.............]

         • Arithmetic, logic (‘&’, ‘|’) and comparison (aka
           ‘relational’, e.g., ‘<’, ‘==’) operations with arrays now
           behave consistently, notably for arrays of length zero.

           Arithmetic between length-1 arrays and longer non-arrays had
           silently dropped the array attributes and recycled.  This
           now gives a warning and will signal an error in the future,
           as it has always for logic and comparison operations in
           these cases (e.g., compare ‘matrix(1,1) + 2:3’ and
           ‘matrix(1,1) < 2:3’).

As the above "visually suggests" one could think of the changes
falling mainly two groups,
  1) <0-extent array>  (op)     <non-array>
  2) <1-extent array>  (arith)  <non-array of length != 1>

These changes are partly non-back compatible and may break
existing code.  We believe that the internal consistency gained
from the changes is worth the few places with problems.

We expect some package maintainers (10-20, or even more?) need
to adapt their code.

Case '2)' above mainly results in a new warning, e.g.,

   > matrix(1,1) + 1:2
   [1] 2 3
   Warning message:
   In matrix(1, 1) + 1:2 :
     dropping dim() of array of length one.  Will become ERROR
   > 

whereas '1)' gives errors in cases the result silently was a
vector of length zero, or also keeps array (dim & dimnames) in
cases these were silently dropped.

The following is a "heavily" commented  R script showing (all ?)
the important cases with changes :

----------------------------------------------------------------------------

(m <- cbind(a=1[0], b=2[0]))
Lm <- m; storage.mode(Lm) <- "logical"
Im <- m; storage.mode(Im) <- "integer"

## 1. -------------------------
try( m & NULL ) # in R <= 3.3.x :
## Error in m & NULL :
##  operations are possible only for numeric, logical or complex types
##
## gives 'Lm' in R >= 3.4.0

## 2. -------------------------
 m + 2:3 ## gave numeric(0), now remains matrix identical to  m
Im + 2:3 ## gave integer(0), now remains matrix identical to Im (integer)

 m > 1      ## gave logical(0), now remains matrix identical to Lm (logical)
 m > 0.1[0] ##  ditto
 m > NULL   ##  ditto

## 3. -------------------------
mm <- m[,c(1:2,2:1,2)]
try( m == mm ) ## now gives error   "non-conformable arrays",
## but gave logical(0) in R <= 3.3.x

## 4. -------------------------
str( Im + NULL)  ## gave "num", now gives "int"

## 5. -------------------------
## special case for arithmetic w/ length-1 array
(m1 <- matrix(1,1,1, dimnames=list("Ro","col")))
(m2 <- matrix(1,2,1, dimnames=list(c("A","B"),"col")))

m1 + 1:2  # ->  2:3  but now with warning to  "become ERROR"
tools::assertError(m1 & 1:2)# ERR: dims [product 1] do not match the length of object [2]
tools::assertError(m1 < 1:2)# ERR:                  (ditto)
##
## non-0-length arrays combined with {NULL or double() or ...} *fail*

### Length-1 arrays:  Arithmetic with |vectors| > 1  treated array as scalar
m1 + NULL # gave  numeric(0) in R <= 3.3.x --- still, *but* w/ warning to "be ERROR"
try(m1 > NULL)    # gave  logical(0) in R <= 3.3.x --- an *error* now in R >= 3.4.0
tools::assertError(m1 & NULL)    # gave and gives error
tools::assertError(m1 | double())# ditto
## m2 was slightly different:
tools::assertError(m2 + NULL)
tools::assertError(m2 & NULL)
try(m2 == NULL) ## was logical(0) in R <= 3.3.x; now error as above!

----------------------------------------------------------------------------


Note that in R's own  'nls'  sources, there was one case of
situation '2)' above, i.e. a  1x1-matrix was used as a "scalar".

In such cases, you should explicitly coerce it to a vector,
either ("self-explainingly") by  as.vector(.), or as I did in
the nls case  by  c(.) :  The latter is much less
self-explaining, but nicer to read in mathematical formulae, and
currently also more efficient because it is a .Primitive.

Please use R-devel with your code, and let us know if you see
effects that seem adverse.

In some case where R-devel now gives an error but did not
previously, we could contemplate giving another  "warning
.... 'to become ERROR'" if there was too much breakage,  though
I don't expect that.


For the R Core Team,

Martin Maechler,
ETH Zurich



More information about the R-devel mailing list