当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《将SEXP从R转换为C++中的字符串向量》经验,为你挑选了1个好方法。

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

#include 
#include 
#include 
using namespace Rcpp;

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

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

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

#include 
#include 
#include 
#include 
using namespace std;
SEXP sort(SEXP x) {
  int* xx = INTEGER(x);
  std::sort(xx, xx+LENGTH(x));
  return(x);
}

但没有std::vectorchar**相当于INTEGER().

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

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



1> Martin Morga..:

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传递,而不复制对象的直接操纵打破副本上变化的错觉,并在距离引入动作-在下面,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

最后,尽管对帖子和封闭的原始帖子发表了评论,但搜索[r] STRING_PTR的 StackOverflow 只返回一个命中 - 这个答案.

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有