C++ 将R中的SEXP转换为C+中的字符串向量+;
我试图将字符向量(即字符串向量)从R传递到C/C++以进行排序和其他用途。使用Rcpp时,可以使用以下代码轻松完成此操作:C++ 将R中的SEXP转换为C+中的字符串向量+;,c++,r,string,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()
#include <Rcpp.h>
#include <vector>
#include <string>
using namespace Rcpp;
// [[Rcpp::export]]
CharacterVector sort(CharacterVector x) {
std::sort(x.begin(), x.end());
return x;
}
#包括
#包括
#包括
使用名称空间Rcpp;
//[[Rcpp::导出]]
CharacterVector排序(CharacterVector x){
std::sort(x.begin(),x.end());
返回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;
SEXP排序(sexpx){
int*xx=整数(x);
排序(xx,xx+长度(x));
返回(x);
}
但是没有std::vector
或char**
等价于INTEGER()
如何在不向Rcpp引入依赖项的情况下模拟相同的代码
这里有一些问题讨论了如何使用CHAR(STRING_ELT())
转换单个字符串,但不清楚如何转换为字符串数组/向量。STRING_PTR()
类似于INTEGER()
。它返回一个SEXP*
。解引用该值是R字符向量的第一个元素的SEXPSTRING_PTR(x)[0]
与STRING_ELT(x,0)
相同。SEXP本身是一个指向数据结构的指针,该数据结构包含一个常量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]
。您想要的是比较实际的以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是为了使继续使用sourceCpp()
变得容易,但是这可以被删除,并且代码可以使用R CMD SHLIB
编译,或者在不依赖Rcpp的情况下在包中使用)
请注意,这是一个非常糟糕的想法,因为直接操纵从R传递的对象而不进行复制打破了更改时复制的假象,并引入了一定距离的操作——在下面,x
和y
被更改,即使只有x
被排序
> 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"
我相信天真的Rcpp实现
// [[Rcpp::export]]
SEXP sortcpp0(SEXP x) {
std::sort(STRING_PTR(x), STRING_PTR(x) + LENGTH(x), cmp_char);
return x;
}
// [[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;
}
具有预期的行为
> 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"
我认为还有一些Rcpp咒语(
最后,尽管有帖子上的评论和关闭的原始帖子,但在StackOverflow中搜索只返回了一次点击——这是答案。确切地说,为什么你认为引入对Rcpp的依赖似乎不值得,即使你认为它已经提供了一个可行的答案?询问朋友…另外,请允许我删除Rcpp的标签,因为您询问的是非Rcpp答案。我的软件包是22kB。Rcpp为3MB。另外,有些人(比如我自己的公司)在导入依赖项时有困难。对于一个函数来说,这都是相当大的开销。i)您的包是一个二进制的、预构建的、具有动态库依赖关系的包。ii)您可能已经通过其他依赖项拥有了Rcpp:iii)Rcpp可能是3mb,但仅R一项就可以达到这一数字的几十倍。2015年,以mb为单位的文件大小很少重要。不管怎样,你自己强加的困难,谢谢你的分享。它甚至值得引入对C编译器的依赖吗?C++排序在R排序上有多快?这对您的应用程序是否至关重要?您是否在pure R中分析了代码以了解瓶颈在哪里?当然,这取决于你神秘的“其他目的”是什么,但是你应该先在R中编码它,然后你就有了测试的基础。我怀疑速度的差异可能是R正在使用区域的排序序列,而C++只是使用ASCII。我认为你需要<代码> RCPP::C克隆()/<代码>来强制一个不同的副本。这一点在很多地方都有讨论。非常感谢你提出的这个非常透彻的问题/答案,这将花费我痛苦的几个小时来解决。另外,感谢您帮助解决问题,无论您对问题的质量/性质有何个人意见。
> library(microbenchmark)
> n = 1e4; set.seed(123); x = sample(as.character(1:n))
> identical(sort(x), sortcpp(x))
[1] TRUE
> microbenchmark(sort(x), sortcpp(x))
Unit: milliseconds
expr min lq mean median uq max neval
sort(x) 56.061580 56.563541 57.034674 56.997618 57.667031 59.003068 100
sortcpp(x) 3.542409 3.556655 3.610071 3.582562 3.662196 3.800319 100