2017-04-03 47 views

回答

6

可能有幾種不同的方式來處理這一點,但這裏有std::transform一個選項:

#include <Rcpp.h> 
using namespace Rcpp; 

struct Functor { 
    std::string 
    operator()(const std::string& lhs, const internal::string_proxy<STRSXP>& rhs) const 
    { 
     return lhs + rhs; 
    } 
}; 

// [[Rcpp::export]] 
CharacterVector paste2(CharacterVector lhs, CharacterVector rhs) 
{ 
    std::vector<std::string> res(lhs.begin(), lhs.end()); 
    std::transform(
     res.begin(), res.end(), 
     rhs.begin(), res.begin(), 
     Functor() 
    ); 
    return wrap(res); 
} 

/*** R 

lhs <- letters[1:2]; rhs <- letters[3:4] 

paste(lhs, rhs, sep = "") 
# [1] "ac" "bd" 

paste2(lhs, rhs) 
# [1] "ac" "bd" 

*/ 

其原因第一複印左手錶達成std::vector<std::string>在於internal::string_proxy<>provides operator+與簽名

std::string operator+(const std::string& x, const internal::string_proxy<STRSXP>& y) 

而不是,例如

operator+(const internal::string_proxy<STRSXP>& x, const internal::string_proxy<STRSXP>& y) 

如果你的編譯器支持C++ 11,這是可以做到稍微乾淨:

// [[Rcpp::plugins(cpp11)]] 
#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 
CharacterVector paste3(CharacterVector lhs, CharacterVector rhs) 
{ 
    using proxy_t = internal::string_proxy<STRSXP>; 

    std::vector<std::string> res(lhs.begin(), lhs.end()); 
    std::transform(res.begin(), res.end(), rhs.begin(), res.begin(), 
     [&](const std::string& x, const proxy_t& y) { 
      return x + y; 
     } 
    ); 

    return wrap(res); 
} 

/*** R 

lhs <- letters[1:2]; rhs <- letters[3:4] 

paste(lhs, rhs, sep = "") 
# [1] "ac" "bd" 

paste3(lhs, rhs) 
# [1] "ac" "bd" 

*/ 
+0

太好了,謝謝你 – user3507085

+0

爲了更好地理解,你能就如何'內部發表簡短評論:: string_proxy &'與'String'有關,爲什麼不能使用'String'? – NoBackingDown

+0

@Dominik總之,他們並不真正相關; 'string_proxy'基本上是一個輕量級的包裝類(即,[* proxy * class](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Temporary_Proxy)),當單個元素[在一個'Vector'](https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/vector/Vector.h#L324-L346)。這種方法可以在沒有實際存儲(「擁有」)的情況下修改'CHARSXP'或'const char *'(假定)與附加功能(例如多重構造函數,操作符重載等) '本身。 – nrussell

3

我要離開這個答案了,但要注意關於由@nrussell提供的警告使用push_back()


我仍然得到認真處理Rcpp自己,所以我在一個循環

library(Rcpp) 

cppFunction('StringVector concatenate(StringVector a, StringVector b) 
{ 
    StringVector c; 
    std::ostringstream x; 
    std::ostringstream y; 

// concatenate inputs 
    for (int i = 0; i < a.size(); i++) 
    x << a[i]; 

    for (int i = 0; i < b.size(); i++) 
    y << b[i]; 

    c.push_back(x.str()); 
    c.push_back(y.str()); 

    return c; 

}') 

a=c("a","b"); b=c("c","d"); 
concatenate(a,b) 
# [1] "ab" "cd" 

(I)的重複調用的性能比較走了一個字符串生成器針對(ii)預分配和填充策略,我們可以看到後者是優選的:

#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 
CharacterVector pbpaste(CharacterVector lhs, CharacterVector rhs) 
{ 
    R_xlen_t i = 0, sz = lhs.size(); 
    CharacterVector res; 

    for (std::ostringstream oss; i < sz; i++, oss.str("")) { 
     oss << lhs[i] << rhs[i]; 
     res.push_back(oss.str()); 
    } 

    return res; 
} 

// [[Rcpp::export]] 
CharacterVector sspaste(CharacterVector lhs, CharacterVector rhs) 
{ 
    R_xlen_t i = 0, sz = lhs.size(); 
    CharacterVector res(sz); 

    for (std::ostringstream oss; i < sz; i++, oss.str("")) { 
     oss << lhs[i] << rhs[i]; 
     res[i] = oss.str(); 
    } 

    return res; 
} 

/*** R 

lhs <- as.character(1:5000); rhs <- as.character(5001:10000) 

all.equal(pbpaste(lhs, rhs), sspaste(lhs, rhs)) 
# [1] TRUE 

microbenchmark::microbenchmark(
    "push_back" = pbpaste(lhs, rhs), 
    "preallocate" = sspaste(lhs, rhs), 
    times = 200L 
) 
# Unit: milliseconds 
#   expr  min   lq  mean  median   uq  max neval cld 
# push_back 101.521579 105.334649 115.156544 107.275678 110.957420 256.722239 200 b 
# preallocate 1.364213 1.585818 1.789564 1.778153 1.934758 2.955352 200 a 

*/ 
+2

正如你所說你剛剛接觸Rcpp,請注意儘可能避免Rcpp'* Vector'類型的'push_back'。 Vector類沒有使用內存分配器,因此這個函數的效率遠低於'std :: vector'對應。當然,這對於長度爲2的物體來說可以忽略不計,但即使是中等大小的物體,其差異也可能是顯着的。 – nrussell

+0

@nrussell有趣的 - 有用的知識。我也剛剛注意到我的輸出與OP所要求的不同... – SymbolixAU

+0

@nrussell - 我要刪除這個答案;但我認爲你的評論應該包含在你的回答中作爲對其他人的提示/警告? – SymbolixAU

3

一個工作解決方案是使用:

#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 
CharacterVector concatenate(std::string x, std::string y) 
{ 
       return wrap(x + y); 
} 

然後:

Vconcatenate=Vectorize(concatenate) 
Vconcatenate(letters[1:2],letters[3:4]) 

或者:

// [[Rcpp::export]] 
CharacterVector concatenate(std::vector<std::string> x,std::vector<std::string> y) 
{ 
    std::vector<std::string> res(x.size()); 
    for (int i=0; i < x.size(); i++) 
    { 
    res[i]=x[i]+y[i]; 
    } 
    return wrap(res); 
} 
+0

爲什麼你說不完全Rcpp?當然,它是或者如何得到膠水和'Rcpp :: CharacterVector'類型?但是你忘了所需的'#include'和'Rcpp :: export'標籤。 –

+0

我指的是使用Vectorize函數 – user3507085

+1

膠水也可以用於'std :: vector ';你可以在內循環。 –