[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