我试图将字符向量(即字符串向量)从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())
转换单个字符串,但不清楚如何转换为数组/字符串向量。
答案 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 ++代码改变的任何参数的必要实践。
Rcpp::clone()
最后,尽管对帖子和封闭的原始帖子发表评论,但搜索“{3}}的StackOverflow只会返回一个命中 - 这个答案。”