Alvin
Alvin

Reputation: 351

How to initialize NumericVector to a specific size after it has been declared?

I wrote the following Rcpp code which compiles but does not give the expected result.

    // [[Rcpp::export]]
RObject test_1 (Rcpp::NumericVector& x)
{
  NumericVector x1;
  if (x.size() < 5)
  {
    NumericVector x1(x.size()*3);
    for (int ii = 0; ii < x.size(); ii++)
    {
      x1[ii] = sqrt(x[ii]);
    }
  }
  else
  {
    NumericVector x1(x.size()*2);
    for (int ii = 0; ii < x.size(); ii++)
    {
      x1[ii] = sqrt(x[ii]);
    }
  }
  return x1;
}

x1, the return vector is declared outside the if statement where the size of the returned vector x1 can vary. But if I declare the NumericVector with the size it created a new object, but the x1 that is returned in an empty vector.

The following code works:

// [[Rcpp::export]]
RObject test (const Rcpp::NumericVector& x)
{
  NumericVector x1;
  if (x.size() < 5)
  {
    NumericVector tmp(x.size()*3);
    for (int ii = 0; ii < x.size(); ii++)
    {
      tmp[ii] = sqrt(x[ii]);
    }
    x1 = tmp;
  }
  else
  {
    NumericVector tmp(x.size()*2);
    for (int ii = 0; ii < x.size(); ii++)
    {
      tmp[ii] = sqrt(x[ii]);
    }
    x1 = tmp;
  }
  return x1;
}

Here I declare temp numericvector and then set x1 to that vector.

I did this a while ago and know that there is a way to set a NumericVector to a specific size after it has been declared, I just cannot remember how.

Edit: Edited the code and question to show how the return vector size is not known before the if statement.

Upvotes: 2

Views: 6637

Answers (3)

Kevin Ushey
Kevin Ushey

Reputation: 21285

You could use this style:

NumericVector x;
if (foo)
    x = NumericVector(1);
else
    x = NumericVector(2);

Or even (if you want to avoid zero initialization of the generated vector)

x = static_cast<NumericVector>(no_init(size));

Note that when you write

NumericVector x;
if (foo)
    NumericVector x(1);

you are actually creating two NumericVector objects called x -- one living at the top-level scope, and one living within the scope of the if statement.

That is, it's worth understanding that when you write

NumericVector x;

you are actually creating an object, not just declaring an object (that is only true for built-in types, e.g. int). So, when you write NumericVector x, x is actually default-constructed with the zero-argument NumericVector constructor (which creates a numeric vector of length 0)

Upvotes: 5

nrussell
nrussell

Reputation: 18602

You could conditionally resize a std::vector<double> and Rcpp::wrap() it, like this:

#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::RObject test_1(const Rcpp::NumericVector& x) {

  std::vector<double> x1;
  if (x.size() < 5) {
    x1.resize(3*x.size());
  } else {
    x1.resize(2*x.size());
  }
  for (int ii = 0; ii < x.size(); ii++) {
    x1[ii] = sqrt(x[ii]);
  }
  return Rcpp::wrap(x1);
}

/*** R

xa <- 1:3
xb <- 1:10

test_1(xa)
# [1] 1.000000 1.414214 1.732051 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

test_1(xb)
#[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 2.828427 3.000000 3.162278 0.000000 0.000000
[13] 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

length(test_1(xa))/length(xa)
#[1] 3
length(test_1(xb))/length(xb) 
#[1] 2

*/

Upvotes: 2

dlavila
dlavila

Reputation: 1212

you can do something like this

RObject test_1 (Rcpp::NumericVector x)
{
    for (int ii = 0; ii < x.size(); ii++)
    {
       x[ii] = sqrt(x[ii]);
    }
    return x;
}

By passing by value you create a new NumericVector of the same size of the original one

if you don't want to change your API, the second code is OK but the temp var is not necessary

 RObject test_1 (const Rcpp::NumericVector& x)
{
    NumericVector x1(x.size());
    for (int ii = 0; ii < x.size(); ii++)
    {
       x1[ii] = sqrt(x[ii]);
    }
    return x1;
}

Upvotes: 1

Related Questions