0

Consider these examples using Rcpp to check various data types. Why does my check for is_list return FALSE?

library(Rcpp)

cppFunction('bool is_list(SEXP data) {
  return Rf_isList(data) ? true : false;
}')

cppFunction('bool is_data_frame(SEXP x) {
    return Rf_inherits(x, "data.frame") ? true : false;
}')

cppFunction('bool is_integer(SEXP data) {
  return Rf_isInteger(data) ? true : false;
}')

is_data_frame(list(a = 1))
# [1] FALSE
is_data_frame(data.frame())
# [1] TRUE
is_integer(1)
# [1] FALSE
is_integer(1L)
# [1] TRUE

## However, this is unexpected
is_list(list(a = 1))
[1] FALSE

References

SymbolixAU
  • 25,502
  • 4
  • 67
  • 139
  • Interesting. Nothing here is really Rcpp apart from using Rcpp to marshal the build via `cppFunction`. The `Rf_*` functions are R's, as are the types. – Dirk Eddelbuettel Nov 29 '17 at 22:05
  • @DirkEddelbuettel - yes I debated whether Rcpp was relevant - I may have come to the wrong conclusion. – SymbolixAU Nov 29 '17 at 22:10
  • As an aside `cppFunction('int what_is_it(SEXP data) { return(TYPEOF(data)); }') ; what_is_it(list(a=1))` returns `19` which is `19 VECSXP list (generic vector) ` which is how `isNewList()` is defined: `INLINE_FUN Rboolean isNewList(SEXP s) { return (s == R_NilValue || TYPEOF(s) == VECSXP); }` – hrbrmstr Nov 30 '17 at 04:33

2 Answers2

2

This may be R. Consider these two plain C functions against the R API which replicate your result, but do not involve any Rcpp:

R> library(inline)
R> f <- cfunction(signature(s="SEXP"), body="SEXP b = PROTECT(allocVector(INTSXP, 1)); INTEGER(b)[0] = Rf_isList(s); UNPROTECT(1); return b;")
R> f(list(a=1))
[1] 0
R> g <- cfunction(signature(s="SEXP"), body="SEXP b = PROTECT(allocVector(INTSXP, 1)); INTEGER(b)[0] = Rf_inherits(s, \"data.frame\"); UNPROTECT(1); return b;")
R> g(data.frame(a=1))
[1] 1
R> 

So in essence Rf_isList() seems to miss list types. There may be a good reason behind it but I would not be able to name it right now ...

And digging a little we see in src/include/Rinlinedfuns.h

INLINE_FUN Rboolean isList(SEXP s)
{
    return (s == R_NilValue || TYPEOF(s) == LISTSXP);
}

which makes it a little hard to see where this could go wrong. But maybe you want to poke there...

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • This helps - it's lead me to `is_list(pairlist(a = 1))` == `TRUE`. It appears a `list` in R is a `VECSXP`, whereas a `pairlist` is a `LISTSXP` – SymbolixAU Nov 29 '17 at 22:49
1

Use Rf_isNewList instead. (Apparently the representation of lists changed at some point.)

This works:

library(Rcpp)

cppFunction('bool is_list(SEXP data) {
    return Rf_isNewList(data) ? true : false;
}')

is_list(list(a = 1))
#> TRUE

is_list(1)
#> FALSE

I think it's more idiomatic to do the following instead:

cppFunction('bool is_list(SEXP data) {
    return (TYPEOF(data) == VECSXP);
}')

This has the side-benefit that NULL is not a list.

Patrick Perry
  • 1,422
  • 8
  • 17