Search code examples
c++rcpp

c++ count algorithm template called by Rcpp


C++ count algorithm called by Rcpp which can count the occurrences of a particular element in a vector. The template should be used. My attempt:

countRcpp <-'
#include<iostream>
#include<vector>
using namespace std;

  int val = as<int>(y);
  NumericVector xx(x);
  NumericVector::iterator iter;
  int m = 0;

  for(iter = xx.begin(); iter < xx.end(); iter++){
    if(*iter == val) ++m;}

  return(wrap(m));
'
countf <- cxxfunction(signature(x = "numeric", y = "numeric"),
                      body = countRcpp,
                      plugin = "Rcpp")

set.seed (1005) # set seed for sampling
x <- sample(1:5L, size = 10, replace = T) #L is Long integer. Keep the nunmber as integer. 
x # [1] 1 3 1 3 3 4 1 3 1 2
y <- 3L
y
countf(x,y) 


incl <- '
#include<iostream>
#include <algorithm>  
#include <vector> 
using namespace std;

  template <typename S_Type, typename T>
  typename iterator_traits<S_Type>::difference_type 

  S_Type countR(S_Type first, S_Type last, const T & val){
    typename iterator_traits<S_Type>::difference_type ret=0;
    while (first !=last){
    if(*first == val) ++ret;
    ++first;
    }
  return ret;
  }
'
body_count <- '
#include<iostream>
#include<vector>
#include <algorithm>    

using namespace std;

  NumericVector xx(x);
  int n = xx.size();
  NumericVector yy = xx + n;
  int val = as<int>(y);
  int pos = countR(xx, yy, val);
  return wrap(pos);
'
countRcpp3 <- cxxfunction(signature(x = "numeric", y = "numeric"),
              body = body_count,
              includes = incl,
              plugin = "Rcpp")

Could you please give me any suggestions? Or would you recommend other logical on this task? Thanks in advance.


Solution

  • As a first step you can extract a function with signature

    int foo(Rcpp::IntegerVector x, int val)
    

    from your working code. You can then generalize this to act on any iterable type. Signature:

    template <typename T>
    int foo(T x, typename std::iterator_traits<typename T::iterator>::value_type val) 
    

    One cannot call this from R, though. If the function in R should act on different types, it has to take SEXP as argument. TYPEOF() can then be used to determine the R data type. Putting this together for integer vectors:

    #include <Rcpp.h>
    
    template <typename T>
    int iter_count(T x, typename std::iterator_traits<typename T::iterator>::value_type val) { 
      int m = 0;
    
      for(typename T::iterator iter = x.begin(); iter < x.end(); ++iter) {
        if(*iter == val) ++m;
     }
    
      return m;
    }
    
    
    // [[Rcpp::export]]
    int count(SEXP x, SEXP val) {
      switch( TYPEOF(x) ) {
      case INTSXP: {
        return iter_count(Rcpp::as<Rcpp::IntegerVector>(x),
                  Rcpp::as<Rcpp::IntegerVector>(val)(0));
      }
      default: {
        Rcpp::stop("incompatible SEXP encountered");
      }
      }  
    }
    
    
    /*** R
    set.seed (1005)
    x <- sample(1:5L, size = 10, replace = T)
    y <- 3L
    count(x,y) 
    */
    

    I am using Rcpp attributes here: Save this as a .cpp file and use Rcpp::sourceCpp("...") on it.

    BTW, in your code this looks fishy:

      NumericVector xx(x);
      int n = xx.size();
      NumericVector yy = xx + n;
    

    Do you want the end-point of xx? Then use xx.end() or end(xx), which is not a NumericVector. Your code creates a new NumericVector where the content of xx has been incremented by the size of xx. Here the equivalent code using Rcpp attributes:

    Rcpp::cppFunction('NumericVector foo(NumericVector xx) {
      int n = xx.size();
      NumericVector yy = xx + n;
      return yy;
    }
    ')
    
    set.seed(42)
    foo(runif(3))
    # [1] 3.914806 3.937075 3.286140