将SEXP从R转换为C ++中的字符串向量

时间:2015-12-03 01:24:43

标签: c++ r string

我试图将字符向量(即字符串向量)从R传递到C / C ++以进行排序和其他目的。使用Rcpp时,可以使用以下代码轻松完成:

#include <Rcpp.h>
#include <vector>
#include <string>
using namespace Rcpp;

// [[Rcpp::export]]
CharacterVector sort(CharacterVector x) {

  std::sort(x.begin(), x.end());
  return x;
}

然而,由于这是我在这个软件包中唯一计划使用的C ++,因此引入对Rcpp的依赖似乎不值得。没有它就做同样的事情并不容易。整数很容易:

#include <R.h>
#include <Rdefines.h>
#include <algorithm>
#include <string.h>
using namespace std;
SEXP sort(SEXP x) {
  int* xx = INTEGER(x);
  std::sort(xx, xx+LENGTH(x));
  return(x);
}

std::vector<string>char**INTEGER()不等。

如何在不将依赖项引入Rcpp的情况下模拟相同的代码?

这里有一些问题讨论如何使用CHAR(STRING_ELT())转换单个字符串,但不清楚如何转换为数组/字符串向量。

1 个答案:

答案 0 :(得分:6)

STRING_PTR()类似于INTEGER()。它返回SEXP *。取消引用该值是R字符向量的第一个元素的SEXP; STRING_PTR(x)[0]STRING_ELT(x, 0)相同。 SEXP本身是指向数据结构的指针,该数据结构包含与字符向量的第一个元素的实际字符的const char *; CHAR(STRING_PTR(x)[i])可能会访问此指针。各种宏在file.path(R.home("include"), "Rinternals.h")

中定义

默认std::sort(STRING_PTR(x), STRING_PTR(x) + LENGTH(x))比较参数的取消引用值,即比较元素的指针地址 - STRING_PTR(x)[i] < STRING_PTR(x)[j]。你想要的是比较实际的以null结尾的C字符串strcmp(CHAR(STRING_PTR(x)[i]), CHAR(STRING_PTR(x)[j]))。您的原始std::sort(x.begin(), x.end())实际上并不返回已排序的字符串(我认为Rcpp执行排序的方式是x.sort())。

您需要一个自定义比较器,它采用SEXP,提取const char *(通过CHAR()宏)并比较这些比较器。这是比较器

struct CMP_CHAR {
    bool operator()(SEXP x, SEXP y) {
        return strcmp(CHAR(x), CHAR(y)) < 0;
    }
} cmp_char;

和实施

// [[Rcpp::export]]
SEXP sortcpp0(SEXP x) {
    std::sort(STRING_PTR(x), STRING_PTR(x) + LENGTH(x), cmp_char);
    return x;
}

(我在上面的示例中使用Rcpp以便继续使用sourceCpp(),但这可以删除,代码使用R CMD SHLIB编译或在包中使用而不依赖于Rcpp)

请注意,这是一个非常糟糕的想法,因为直接操作从R传递而没有复制的对象会破坏复制时的复制错觉并在远处引入操作 - 在下面,{{1}即使只对x进行了排序, y也会被更改。

x

我相信天真的Rcpp实现

> n = 10; set.seed(123); x = y = sample(as.character(1:n))
> sortcpp0(x); y
 [1] "1"  "10" "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9" 
 [1] "1"  "10" "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"

也会受此影响 - 在排序之前需要复制输入参数。解决方案很简单 - 复制(和PROTECT!即使在本例中技术上不需要)传入的参数。

// [[Rcpp::export]]
CharacterVector sortRcpp2(CharacterVector x) {
    return x.sort();
}

具有预期的行为

// [[Rcpp::export]]
SEXP sortcpp(SEXP x) {
    x = PROTECT(Rf_duplicate(x));
    std::sort(STRING_PTR(x), STRING_PTR(x) + LENGTH(x), cmp_char);
    UNPROTECT(x);
    return x;
}

我认为有一些微不足道的Rcpp咒语(> n = 10; set.seed(123); x = y = sample(as.character(1:n)) > sortcpp(x); y [1] "1" "10" "2" "3" "4" "5" "6" "7" "8" "9" [1] "3" "8" "4" "7" "6" "1" "10" "9" "2" "5" ,感谢@DirkEddelbuettel)也可以复制参数,这应该被认为是C ++代码改变的任何参数的必要实践。

@Spacedman指出R的排序可能没问题 - 它快速下降到C,有一个相当快的(虽然不是最快的)排序实现,并且内置了很多很好的功能(例如,处理NA& #39; s和不同的系统区域设置;后者以非常微妙的方式影响排序顺序)。但是仍然有很大的收获(我对此感到惊讶......)

Rcpp::clone()

最后,尽管对帖子和封闭的原始帖子发表评论,但搜索“{3}}的StackOverflow只会返回一个命中 - 这个答案。”