在C++中将SEXP从R转换为字符串向量
Converting an SEXP from R into a vector of strings in C++
我正试图将一个字符向量(即字符串向量)从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);
}
但不存在与CCD_ 3等价的CCD_ 1或CCD_。
如何在不引入对Rcpp的依赖的情况下模拟相同的代码?
这里有一些问题讨论了如何使用CHAR(STRING_ELT())
转换单个字符串,但尚不清楚如何转换为字符串的数组/向量。
STRING_PTR()
类似于INTEGER()
。它返回一个SEXP *
。对该值进行去引用是R字符向量的第一个元素的SEXP;CCD_ 8与CCD_。SEXP本身是指向数据结构的指针,该数据结构包含指向字符向量的第一元素的实际字符的CCD_;该指针可能被CCD_ 11访问。std::vector<string>
2 中定义了各种宏
默认的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是为了方便继续使用char**
0,但这可以删除,代码可以用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]]
CharacterVector sortRcpp2(CharacterVector x) {
return x.sort();
}
还有一个问题——在排序之前,需要复制输入参数。解决方案很简单——复制(和PROTECT!,尽管在当前示例中技术上不需要)传入的参数。
// [[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咒语(Rcpp::clone()
,感谢@DirkEddelbuettel)用于复制参数,这应该被视为C++代码更改的任何参数的必要练习。
@Spacedman指出,R的排序可能还可以——它很快就降到了C,有一个相当快(尽管不是最快)的排序实现,并且内置了很多不错的功能(例如,处理NA和不同的系统位置;后者以非常微妙的方式影响排序顺序)。但仍有巨大的收益(我对此感到惊讶…)
> 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
最后,尽管帖子上有评论,也有关闭的原始帖子,但在StackOverflow中搜索[r]STRING_PTR只会返回一个点击——这个答案。
- wcstombs_s(),转换字符串的长度
- 使用 CStringW/CStringA 和 CT2W/CT2A 转换字符串有什么区别?
- 无法转换字符串插入函数的参数
- 转换字符串向量:基于先前的值跳过元素
- 从中间器转换字符串不起作用,出了什么问题?
- 失败的证明转换字符串到枚举类
- 如何使用正则表达式和提升转换迭代器标记和转换 c 字符串
- 使用java jni时转换字符串类型
- 如何避免在转换字符串令牌流时重复istringstream构造
- 阿托伊未正确转换字符串
- C++,转换字符串,使连续下划线序列变为单个下划线
- 使用长算术转换字符串
- 转换字符串十进制数时的精度
- 将词法转换字符串提升为双精度
- 使用marshal_as函数转换字符串的性能
- C++不会从数据转换字符串
- 无论如何,在c++ /Qt中转换字符串到结构中的字段
- C++ 隐式类型转换字符串 -> int?
- 转换字符串到浮点数,c++实现
- 用于转换字符串大小写的函数