[Rcpp-devel] Help with accessing and manipulating List objects

Romain Francois romain at r-enthusiasts.com
Tue Jul 23 14:23:01 CEST 2013


Hello Tal,

I'm trying to express this with the tools available in Rcpp 0.10.3. (And 
adding some tools for later versions too, like is<>).

Here is how I would do the first problem:

First break down into functions. We need :
- something that tells me if a given R object is a list. With Rcpp 
devel, we could use is<List> but for now we can write it as:

bool is_list(RObject x){
     return TYPEOF(x) == VECSXP ;
}

I've added the is<> so that you don't need to know about TYPEOF or 
VECSXP. But here we are essentially mimicking typeof( x ) == "list"

- We need something to tell me if something is a single string:

bool is_string(RObject x){
     return TYPEOF(x) == STRSXP && Rf_length(x) == 1 ;
}

agai, with Rcpp devel, we could express this like this: is<String>( x ) 
instead.

- We need something that tells if an object has a type attribute that is 
equal to fun. R lets us do that easily, but we have to pay that 
flexibility in C++. What I came up with is this:

bool is_fun(RObject x){
     // is this a numeric vector
     if( TYPEOF(x) != REALSXP ) return false ;

     // is the type attribute a string ?
     if( !is_string( x.attr("type") ) ) return false ;

     // test the string
     String type = x.attr( "type" ) ;
     return type == "fun" ;
}

- Then, we need a recursive function that goes through elements of a 
list. For collecting results, I'm choosing to pass a std::vector by 
reference. So there are two things to learn here:
    - pass by reference
    - std::vector
you'll find many resources online to teach you these things.

void process( List data, std::vector<double>& results ){
     for( int i=0; i<data.size(); i++){
         if( is_list( data[i] ) ){
             // recurse
             process( data[i], results ) ;
         } else if( is_fun( data[i] ) ){
             // we want to collect them. we can use the NumericVector class
             // wince we know thius is a numeric vector.
             // (it is tested in is_fun)
             NumericVector x = data[i] ;

             // loop through the values and add them to the results vector
             for( int j=0; j<x.size(); j++){
                 results.push_back( x[j] ) ;
             }
         } // else do nothing
     }
}


- Finally, we need something that you can call from R. It takes a list 
as input, and returns a numeric vector.

// [[Rcpp::export]]
NumericVector extract_fun(List x){
     std::vector<double> results ;
     process(x, results) ;
     return wrap(results) ;
}


Here is the full script:

#include <Rcpp.h>
using namespace Rcpp ;

bool is_list(RObject x){
     return TYPEOF(x) == VECSXP ;
}

bool is_string(RObject x){
     return TYPEOF(x) == STRSXP && Rf_length(x) == 1 ;
}

bool is_fun(RObject x){
     if( TYPEOF(x) != REALSXP ) return false ;
     if( !is_string( x.attr("type") ) ) return false ;
     String type = x.attr( "type" ) ;
     return type == "fun" ;
}

void process( List data, std::vector<double>& results ){
     for( int i=0; i<data.size(); i++){
         if( is_list( data[i] ) ){
             // recurse
             process( data[i], results ) ;
         } else if( is_fun( data[i] ) ){
             // we want to collect them. we can use the NumericVector class
             // wince we know thius is a numeric vector.
             // (it is tested in is_fun)
             NumericVector x = data[i] ;

             // loop through the values and add them to the results vector
             for( int j=0; j<x.size(); j++){
                 results.push_back( x[j] ) ;
             }
         } // else do nothing
     }
}

// [[Rcpp::export]]
NumericVector extract_fun(List x){
     std::vector<double> results ;
     process(x, results) ;
     return wrap(results) ;
}

/*** R

x <- list(a = 1, b = 2, c = list(ca = 3, cb = 4, 5), 6)
attr(x[[1]], "type") = "fun"
attr(x[[3]][[1]], "type") = "fun"
x

extract_fun(x)

*/

You should be able to sourceCpp it and get the expected result.



I let you do 2 and 3 as an exercize with what you learn from this.
Hints:
- For 2). you don't need to collect results, so your process can look 
like this:

void process( List data ){
     // ...
}

I let you update the body to do what you wanted

- For 3). There are several ways to do this in C++. you could use a 
reference to an int, a static variable within the function. You could 
even use some data that lives in an R environment, using the Environment 
class.

Whatever you come up with, feel free to share. Those examples are nice 
examples of something that is relatively easy to do when you start to be 
Rcpp fluent, but not so easy otherwise.

Romain


Le 20/07/13 18:44, Tal Galili a écrit :
> Hello dear Rcpp users,
>
> First - I'd like to say that I took Hadley and Romain's workshop at the
> useR conference this year, and I am very excited about trying out Rcpp -
> this project looks AMAZING.
>
> Second - this is my first post, and I apologize if my question is too
> basic. To my defense, I tried looking through this mailing list /
> googled before asking, and read through Hadley's github chapter on Rcpp.
>
> The questions:
>
> I am looking to understand better how List objects can be navigated,
> their elements accessed, and manipulated - using Rcpp.
>
> For example, here is an R list object:
>
> x <- list(a = 1, b = 2, c = list(ca = 3, cb = 4, 5), 6)
> attr(x[[1]], "type") = "fun"
> attr(x[[3]][[1]], "type") = "fun"
> x
>
> I would like to create two types of functions:
> 1) A function that will go through "x" and will *_RETURN_* all of the
> elements within it that are of type "fun".
> In R I would do it like this:
>
> return_fun <- function(x) {
>     fun_nubmers <- numeric()
>     for(i in seq_along(x)) {
>        if(class(x[[i]]) == "list") {
>           fun_nubmers <- c(fun_nubmers, return_fun(x[[i]]))
>        } else {
>           if(!is.null(attr(x[[i]], "type"))) {
>              if(attr(x[[i]], "type") == "fun") fun_nubmers <-
> c(fun_nubmers, x[[i]])
>           }
>        }
>     }
>     return fun_nubmers
> }
> return_fun(x) # output: 1 3
>
> But in Rcpp there are many parts to this R function that I don't know
> how to do. I don't know how to access the attributes of a sub-element
> within a List, I don't know how to check if that attribute is null or
> not, etc. So any suggestions on either reading material (or better yet -
> an example of how this function might be created in Rcpp would be great.
>
>
> 2) A function that will go through "x" and will *_CHANGE_* all of the
> elements within it that are of type "fun". For example, adding 1 to them.
> In R I would do it like this:
>
> add1_fun <- function(x) {
>     for(i in seq_along(x)) {
>        if(class(x[[i]]) == "list") {
>           x[[i]] <- add1_fun(x[[i]])
>        } else {
>           if(!is.null(attr(x[[i]], "type"))) {
>              if(attr(x[[i]], "type") == "fun") x[[i]] <- x[[i]] + 1
>           }
>        }
>     }
>     x
> }
> add1_fun(x)
> return_fun(x) # output: 1 3
> return_fun(add1_fun(x) ) # output: 2 4
>
>
> 3) Is it possible to work with some "global" variable from within a
> recursive Rcpp function?
> For example:
>
>
> count_till_5 <- function() {
>     if(!exists("global_var")) global_var = 0
>
>     if(global_var <5) {
>        global_var <<- global_var+1
>        count_till_5()
>     }
> }
> count_till_5()
> global_var
>
> Is there a way to create something like this with Rcpp?
>
>
> Thanks in advance for any help.
>
> With regards,
> Tal

-- 
Romain Francois
Professional R Enthusiast
+33(0) 6 28 91 30 30

R Graph Gallery: http://gallery.r-enthusiasts.com

blog:            http://blog.r-enthusiasts.com
|- http://bit.ly/13SrjxO : highlight 0.4.2
`- http://bit.ly/10X94UM : Mobile version of the graph gallery



More information about the Rcpp-devel mailing list