#include <iostream>    //头文件
using namespace std;   //命名空间
int main(void)        //主函数
{
    cout << "Hellow world" << endl;
    system("pause");
    return 0;
}
1、头文件:可以认为头文件是你在调用函数时的一个桥梁。c++的程序是由一个又一个的函数组成的,比如说刚刚这个程序里就可以说包含了两个函数(其中一个函数包含在另一个函数里头),一个是主函数(main函数),还有一个是输出函数cout。在c++中,除了主函数及自定义的函数,其他的函数都是包含于某些头文件里的,它们被称为库函数,想要调用这些函数,只要在程序的开头写上包含该函数的头文件就可以了。比如说包含cout函数的头文件是
2、命名空间:在程序中必须要写上这么一句话即可,是为防止不同人写的函数出现重名,然后将这些函数整合到一起会出现问题的情况。
3、主函数:这是整个程序最为核心,也是最重要的部分。因为它是系统默认的你这个程序运行的入口,换句话说,当你的源代码通过编译,成为一个程序,在计算机上运行时,它是从int main()(其中int 代表这个函数的返回值类型)这里开始的,一句一句往下走。如果没有这个主函数,程序就运行不了。
4、函数体:主函数声明后,下面有一对花括号({ }),花括号里面 所表达的就是你这个函数想要干什么。比如说这一个程序,他的main函数想要做的是输出hello world,因为它调用了一个输出函数 (cout)。有一个非常重要的点就是每一个函数体都必须在一对花括号内,只有这样,系统才能真正明白你想要表达什么(编译才能通过)。
5、返回值:本程序主函数的最后有一句话:return 0,它的意思是 主函数结束后向操作系统返回一个0值,也就是说,如果你的程序顺利地结束了,你的操作系统会得到一个0值,如果运行出错,那么得到的就也许是另外一个值了。
方便自己和他人阅读,不会被程序执行。
//单行
/*多行注释*/
作用:给一段指定的内存空间起名,方便操作这段内存。 语法:数据类型 变量名 = 初始值;
int a = 10;
cout << "a = "<< a << endl;
#define 常量名 常量值
通常在文件上方定义,表示一个常量。
const 数据类型 常量名 = 常量值
通常在变量定义之前加关键字const,修饰该变量为常量,不可修改。
#define day 7//是不可修改的值,一旦修改就会报错
const int month = 30;
C++规定在创建一个变量或者常量的时候,必须要指定出相应的数据类型,否则无法给该变量分配内存空间。
数据类型:整型、sizeof关键字、实型(浮点型)、字符型、转义字符、字符串、布尔类型
为了提高R程序的运行效率,可以尽量使用向量化编程,减少循环,尽量使用内建函数。对于效率的瓶颈,尤其是设计迭代算法时,可以采用编译代码,而Rcpp扩展包可以很容易地将C++代码连接到R程序中,并且支持在C++中使用类似于R的数据类型。
Rcpp可以很容易地把C++代码与R程序连接在一起,可以从R中直接调用C++代码而不需要用户关心那些繁琐的编译、链接、接口问题。可以在R数据类型和C++数据类型之间容易地转换。
在用Rcpp开发之前,要先安装c++编译器
Windows用户需要安装Rtools包;Mac系统的用户可以从应用商店安装Xcode软件,Linux操作系统可以在操作系统命令行用如下命令安装编译软件:
sudo apt-get install r-base-dev
通过执行以下代码
install.packages("Rcpp")
考虑用C++程序计算Fibonacci数的问题。可以使用如下R代码,其中有一部分C++代码,用cppFunction转换成了R可以调用的同名R函数。
library(Rcpp)
cppFunction(code='
  int fibonacci(const int x){
    if(x < 2) return x;
    if(x > 40) return -1; 
    // 太大的x值会占用过多计算资源
    return ( fibonacci(x-1) + fibonacci(x-2) );
  }
')
print(fibonacci(5))
## [1] 5
编译、链接、导入是在后台由Rcpp控制自动进行的,不需要用户去设置编译环境,也不需要用户执行编译、链接、导入R的工作。在没有修改C++程序时,同一R会话期间重新运行不必重新编译。
Rcpp属性(attributes)用来简化把C++函数变成R函数的过程。做法是在C++源程序中加入一些特殊注释,利用其指示自动生成C++与R的接口程序。属性是C++11标准的内容,现在的编译器支持还不多,所以在Rcpp支持的C++程序中写成了特殊格式的注释。
Rcpp属性有如下优点: 降低了同时使用R与C++的学习难度; 取消了很多繁复的接口代码; 可以在R会话中很简单地调用C++代码,不需要用户自己考虑编译、连接、接口问题; 可以先交互地调用C++,成熟后改编为R扩展包而不需要修改界面代码。
Rcpp属性的主要组成部分如下: 在C++中,提供Rcpp::export标注要输出到R中的C++函数。 在R中,提供sourceCpp(),用来自动编译连接保存在文件或R字符串中的C++代码,并自动生成界面程序把C++函数转换为R函数。
sourceCpp()函数可以用code=指定一个R字符串,字符串的内容是C++源程序, 其中还是用特殊注释//[[Rcpp::export]]标识要导出的C++函数。 如
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
  NumericVector iters(NumericVector x){
    int n = x.size();
    NumericVector y(n);
    y[0] = x[0];
    double sign=-1;
    for(int t=1; t<n; t++){
      sign *= -1;
      y[t] = sign*y[t-1] + x[t];
    }
    return y;
  }
')
print(iters(1:5))
## [1] 1 3 0 4 1
这个例子说明C++程序可以直接写在R程序文件内(保存为R多行字符串),用sourceCpp()函数编译。Rcpp包为C++提供了一个NumericVector数据类型,用来存储数值型向量。用成员函数size()访问其大小,用方括号下标访问其元素。C程序中定义的函数可以返回NumericVector数据类型,将自动转换为R的数值型向量。特殊的注释//[[Rcpp::export]]用来指定哪些C++函数是要转换为R函数的。这叫做Rcpp属性(attributes)功能。
直接把C++代码写在R源程序内部的好处是不用管理多个源文件,缺点是当C++代码较长时,不能利用专用C++编辑环境和调试环境,出错时显示的错误行号不好定位,而且把代码保存在R字符串内,C++代码中用到字符时需要特殊语法。所以,稍复杂的C++代码应该放在单独的C++源文件内。
假设上面的iters函数的C++代码单独存入了一个iters.cpp源文件中,内容如下:
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector iters(NumericVector x){
  int n = x.size();
  NumericVector y(n);
  
  y[0] = x[0];
  double sign=-1;
  for(int t=1; t<n; t++){
    sign *= -1;
    y[t] = sign*y[t-1] + x[t];
  }
  
  return y;
}
用如下的sourceCpp()函数把C++源文件中代码编译并转换为R可访问的同名函数, 测试调用:
sourceCpp(file='iters.cpp')
print(iters(1:5))
Rcpp包为C++定义了NumericVector,IntegerVector,CharacterVector, Matrix等新数据类型,可以直接与R的numeric,charactor,matrix对应。 Rcpp最基础的R数据类型是RObject,这是NumericVector,IntegerVector等的基类,通常不直接使用。RObject包裹了原来R的C API的SEXP数据 结构,并且提供了自动的内存管理,不再需要用户自己处理建立内存和消除内存的工作。RObject存储数据完全利用R C API的SEXP数据 结构,不进行额外的复制。
RObject有如下导出类: IntegerVector: 整数向量; NumercicVector: 数值向量; LogicalVector: 逻辑向量; CharacterVector: 字符型向量; GenericVector: 列表; ExpressionVector: 表达式向量; RawVector: 元素为raw类型的向量。 IntergerMatrix, NumericMatrix: 整数值或数值矩阵。
在R向量中,如果其元素都是同一类型(如整数、双精度数、逻辑、字符型),则称为原子向量。Rcpp提供了IntegerVector,NumericVector,LogicalVector,CharacterVector等数据类型与R的原子向量类型对应。在C++中可以用[]运算符存取向量元素,也可以用STL的迭代器。用.begin(),.end()等界定范围,用循环或者accumulate等STL算法处理整个向量。
在R中通常不严格区分整数与浮点实数,但是在与C++交互时,C++对整数与实数严格区分,所以RCpp中整数向量与数值向量是区分的。
在R中,如果定义了一个仅有整数的向量,其类型是整数(integer)的,否则是数值型(numeric)的,如:
x <- 1:5
class(x)
## [1] "integer"
y <- c(0, 0.5, 1)
class(y)
## [1] "numeric"
Rcpp可以把R的整数向量传递到C++的IntegerVector中,也可以把C++的IntegerVector函数结果传递回R中变成一个整数向量。也可以在C++中生成IntegerVector向量,填入整数值。
如果一个正整数等于它所有的除本身以外的因子的和,称这个数为完全数。如 6=1+2+3 28=1+2+4+7+14 是完全数。
任务:用C++程序输入前4个完全数偶数,返回到R中。这4个数为6, 28, 496, 8182。 程序:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
IntegerVector wholeNumberA(){
    IntegerVector epn(4);
    epn[0] = 6;    epn[1] = 28;
    epn[2] = 496;  epn[3] = 8182;
    return epn;
}
')
print(wholeNumberA())
## [1]    6   28  496 8182
对IntegerVector类型的x,访问单个下标i格式为x[i],下标从0开始计算,与R向量下标从1开始的规则不同。
从例子看出,可以在C++中建立一个IntegerVector,需指定大小,可以逐个填入数值,直接返回IntegerVector到R即可。
任务:用C++编写函数,从R中输入整数向量,计算其元素乘积(与R的prod()函数功能类似)。程序如下:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
IntegerVector prod1(IntegerVector x){
    int prod = 1;
    for(int i=0; i < x.size(); i++){
        prod *= x[i];
    }
    return wrap(prod);
}
')
print(prod1(1:5))
## [1] 120
为了获得向量的元素个数, 用x.size()这种方法。 从程序看出, 用IntegerVector从R中接受一个整数值向量时, 不需要显式地转换。 把一个C++整数值返回给R时, 可以用IntegerVector返回,因为返回值原来是一个C++的int类型, 所以需要用Rcpp::wrap()转换一下。在sourceCpp中可以省略Rcpp::部分。
NumericVector类在C++中保存双精度型一维数组,可以与R的实数型向量(class为numeric)相互转换。这是用C++程序与R交互时最常用到的数据类型。x.size()返回元素个数。
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
double ssp(
    NumericVector vec, double p){
  double sum = 0.0;
  for(int i=0; i < vec.size(); i++){
    sum += pow(vec[i], p);
  }
  return sum;
}
')
ssp(1:4, 2)
## [1] 30
从程序看出,用Rcpp属性编译时,C++函数的输入与返回值类型转换有如下规则:R中数值型向量在C++中可以用NumericVector接收; R中单个的实数在C++中可以用double来接收;为了返回单个的实数, 可以直接返回double,也可以返回NumericVector类型, 或者从一个double型用wrap()转换。C++中如果返回NumericVector, 在R中转换为数值型向量。
在自定义R函数时,输入的自变量的值不会被改变,相当于自变量都是局部变量。如果在自定义函数中修改了自变量的值,实际上只能修改自变量的一个副本的值。如
x <- 100
f <- function(x){
  print(x); x <- 99; print(x)
}
c(f(x), x)
## [1] 100
## [1] 99
## [1]  99 100
但是,在用Rcpp编写R函数时,因为RObject传递的是指针,并不自动复制自变量值,所以修改自变量值会真的修改原始的自变量变量值。如:
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f2(NumericVector x){
  x[0] = 99.0;
  return(x);
}
')
x <- 100
c(f2(x), x)
## [1] 99 99
可见自变量的值被修改了。当然,对这个问题而言,因为输入的是一个标量,只要函数自变量不是NumericVector类型而是用double类型, 则自变量值会被复制,达到值传递的效果,自变量值也就不会被真的修改。如
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f3(double x){
  x = 99.0;
  return(wrap(x));
}
')
x <- 100
c(f3(x), x)
## [1]  99 100
C++中Rcpp支持的向量和列表,仍可以用正整数向量作为下标。 例如, 下面的C++函数求指定的元素子集的和:
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
double sumsub(
  NumericVector x, IntegerVector i){
  NumericVector y = x[i-1];
  double s = 0.0;
  int n = y.size();
  for(int j=0; j<n; j++)
    s += y[j];
  
  return s;
}
')
x <- 1:10
sumsub(x, c(1, 3, 5))
## [1] 9
注意在程序中将输入的下标向量减了1,以适应从R规则到C++规则的变化。 Rcpp在输入和输出时会自动在整型和双精度型之间转换, 比如上面例子中输入的x是整型的,要求输入NumericVector是双精度型的, Rcpp会自动转换。 Rcpp也支持逻辑下标, 比如,下面在C++定义的函数可以取出向量的正值的子集:
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector get_positive(NumericVector x){
  NumericVector y = x[x > 0];
  return y;
}
')
x <- c(1, -1, 2, -2)
get_positive(x)
## [1] 1 2
Rcpp也支持按元素名访问向量元素或向量子集。
NumericMatrix x(n,m)产生一个未初始化的矩阵,元素为double类型。 x.nrow()返回行数,x.ncol()返回列数,x.size()返回元素个数。 下标从0开始计算。为了访问Xij,用x(i,j)的格式:注意,不是x[i,j]也不是x[i][j]。 为了访问x的第i行,用x.row(i)或x(i,); 为了访问x的第j列,用x.column(j)或x(,j)。 transpose(x)返回x的转置。 NumericMatrix::zeros(n)返回n阶元素为0的方阵。 NumericMatrix::eye(n)返回n阶单位阵。
输入一个矩阵,计算各列的向量模,并返回模的最大值。
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
double testfun(NumericMatrix x){
  int nr = x.nrow();
  int nc = x.ncol();
  double y, y1;
  y = 0.0;
  for(int j=0; j<nc; j++){
    y1 = 0.0;
    for(int i=0; i<nr; i++){
      y1 += pow(x(i,j), 2);
    }
    if(y1 > y) y = y1;
  }
  y = sqrt(y);
  return y;
}
')
x <- matrix(c(1, 2, 4, 9), 2, 2)
print(x)
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    9
print(testfun(x))
## [1] 9.848858
可以用x(,j)这样的格式将矩阵x的第j列取出为普通向量,并可以赋值修改。也可以用x.column(j),用法相同。x(i,)取出第i行为一个普通向量。 比如,下面的C++函数将输入的矩阵的每一行计算累加和:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericMatrix testfun(NumericMatrix x){
  NumericMatrix y = clone(x);
  for(int j=1; j<x.ncol(); j++){
    y(_,j) = y(_,j-1) + x(_,j);
  }
  return y;
}
')
x <- matrix(c(1, 2, 4, 9), 2, 2)
print(x)
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    9
print(testfun(x))
##      [,1] [,2]
## [1,]    1    5
## [2,]    2   11
CharacterVector类型可以与R的字符型向量相互交换信息,在C++中其元素为字符串。 字符型缺失值在C++中为R_NAString。R的字符型向量也可以转换为std::vector。 如:
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
CharacterVector f5(){
  CharacterVector x(3);
  x[0] = "This is a string";
  x[1] = "Test";
  x[2] = R_NaString; 
  return(x);
}
')
f5()
## [1] "This is a string" "Test"             NA
R中的向量、矩阵、数据框可以有元素名、列名、行名。 这些名字可以借助Named类添加。
例如:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f6(){
  NumericVector x = NumericVector::create(
    Named("math") = 82,
    Named("chinese") = 95,
    Named("English") = 60);
  return(x);
}
')
f6()
##    math chinese English 
##      82      95      60
“Named(元素名)”可以简写成“_元素名”。 如:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f6b(){
  NumericVector x = NumericVector::create(
    _["math"] = 82,
    _["chinese"] = 95,
    _["English"] = 60);
  return(x);
}
')
Rcpp提供的List类型对应于R的list(列表)类型, 在C++中也可以写成GenericVector类型。 其元素可以不是同一类型, 在C++中可以用方括号和字符串下标的格式访问其元素。
例如,下面的函数输入一个列表, 列表元素vec是数值型向量, 列表元素multiplier是数值型标量, 返回一个列表, 列表元素sum为vec元素和, 列表元素dsum为vec元素和乘以multiplier的结果:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
List f7(List x){
  NumericVector vec = as<NumericVector>(x["vec"]);
  double multiplier = as<double>(x["multiplier"]);
  double y = 0.0, y2;
  for(int i=0; i<vec.length(); i++){
    y += vec[i];
  }
  y2 = y*multiplier;
  return(List::create(Named("sum")=y,
    Named("dsum")=y2));
}
')
f7(list(vec=1:5, multiplier=10))
## $sum
## [1] 15
## 
## $dsum
## [1] 150
上面的程序用了Rcpp::List::create()当场生成List类型, 因为用Rcpp属性功能编译所以可以略写Rcpp::。
Rcpp的DataFrame类用来与R的data.frame交换信息。
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
DataFrame f8(){
  IntegerVector vec = 
    IntegerVector::create(7,8,9);
  std::vector<std::string> s(3);
  s[0] = "abc"; s[1] = "ABC"; s[2] = "123";
  return(DataFrame::create(
    Named("x") = vec,
    Named("s") = s));
}
')
f8()
##   x   s
## 1 7 abc
## 2 8 ABC
## 3 9 123
Rcpp的Function类用来接收一个R函数, 并且可以在C++中调用这样的函数。 示例如:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
SEXP dsort(Function sortFun, SEXP x){
  return sortFun(x, Named("decreasing", true));
}
')
dsort(sort, c('bb', 'ab', 'ca'))
## [1] "ca" "bb" "ab"
程序用Function对象sortFun接收从R中传递过来的排序函数, 实际调用时传递过来的是R的sort函数。
在C++中调用R函数时, 有名的自变量用“Named(自变量字符串, 自变量值)”的格式给出。
程序中的待排序的向量与排序后的向量都用了SEXP来说明, 即直接传送原始R API指针, 这样可以不管原来类型是什么, 在C++中完全不进行类型转换或复制。
从运行例子看出数值和字符串都正确地按照降序排序后输出了。
R函数可以不作为C++的函数自变量传递进来, 而是直接调用R的函数。 R的许多函数都已经在Rcpp名字空间中输出了, 所以可以直接调用R的各个向量化的函数, 包括随机数函数。 例如, 下面的C++函数返回一个各列为随机数的数值型矩阵:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix rngCpp(const int N) {
  NumericMatrix X(N, 4);
  X(_, 0) = runif(N);
  X(_, 1) = rnorm(N);
  X(_, 2) = rt(N, 5);
  X(_, 3) = rbeta(N, 1, 1);
  return X;
}')
R的环境是分层的,可以逐层查找变量名对应的内容。 Rcpp的Environment类用来与R环境对应。 可以利用Environment来定位R的扩展包中的函数或数据, 例如下面的程序片段在C++中定位了stats扩展包中的 rnorm函数并进行了调用:
Environment stats("package:stats");
Function rnorm = stats["rnorm"];
return rnorm(3, Named("sd", 100.0));
as<CPP>(RCPP):将RCPP数据类型(RCPP)转换成C++数据类型(CPP)
wrap(CPP):将C++数据类型(CPP)转换成RCPP数据类型(RCPP)
| RCPP | C++ | as | wrap | 
|---|---|---|---|
Vector | 
vector,list,deque | 
+ | + | 
List,DataFrame | 
vector<vector>,list<vector>,etc | 
+ | + | 
NamedVector | 
map,unodered_map | 
- | + | 
Vector | 
set,unodered_set | 
- | + | 
RCPP::Vector与C++序列容器(如:std::vector,std::list,std::deque,etc)的互相转换NumericVector   rcpp_vector = {1,2,3,4,5};
// Conversion from Rcpp::Vector to std::vector  
std::vector<double>  cpp_vector = as< std::vector<double> >(rcpp_vector);
// Conversion from std::vector to Rcpp::Vector  
NumericVector v1 = wrap(cpp_vector);
DataFrame或List类型的互相转换
using namespace std;
// [[Rcpp::export]]
// A two-dimensional vector with all element vectors of equal length
// can be converted to a DataFrame
vector<vector<double>> cpp_vector_2d_01 = {{1,2},{3,4}};
DataFrame df = wrap(cpp_vector_2d_01);
// A two-dimensional vector with different length of element vectors
// can be converted to a list
vector<vector<double>> cpp_vector_2d_02 = {{1,2},{3,4,5}};
List li = wrap(cpp_vector_2d_02);
std::map<key,value>和std::unordered_map<key,value>数据类型转换成命名的Rcpp::Vector数据类型(key是元素的名字,value是元素的值)library(Rcpp)
sourceCpp(code='
#include<Rcpp.h>
#include<map>
#include<unordered_map>
using namespace Rcpp;
// [[Rcpp::export]]
List std_map(){
  std::map<std::string, double> map_str_dbl;
  
  map_str_dbl["E"] = 5;    
  map_str_dbl["A"] = 1;
  map_str_dbl["C"] = 3;    
  map_str_dbl["D"] = 4;
  map_str_dbl["B"] = 2;
  
  std::unordered_map<std::string, double> umap_str_dbl;
  umap_str_dbl["E"] = 5;    
  umap_str_dbl["A"] = 1;
  umap_str_dbl["C"] = 3;    
  umap_str_dbl["D"] = 4;
  umap_str_dbl["B"] = 2;
  
  List li = List::create(
              Named("std::map",map_str_dbl),
              Named("std::unordered_map", umap_str_dbl)
                        );
  
  return(li);
}
')
std_map()
## $`std::map`
## A B C D E 
## 1 2 3 4 5 
## 
## $`std::unordered_map`
## B D C A E 
## 2 4 3 1 5
当C++中赋值运算的右侧表达式是一个R对象或R对象的部分内容时,可以隐含地调用as()将其转换成左侧的C++类型。
当C++中赋值运算的左侧表达式是一个R对象或其部分内容时, 可以隐含地调用wrap()将右侧的C++类型转换成R类型。
详见Rcpp属性。 # Rcpp糖
在C++中,向量和矩阵的运算通常需要逐个元素进行,或者调用相应的函数。
Rcpp通过C++的表达式模板(expression template)功能,可以在C++中写出像R中对向量和矩阵运算那样的表达式。
这称为Rcpp糖(sugar)。
R中的很多函数如sin等是向量化的,Rcpp糖也提供了这样的功能。
Rcpp糖提供了一些向量化的函数如ifelse,
sapply等。比如,两个向量相加可以直接写成x + y而不是用循环或迭代器(iterator)逐元素计算;
若x是一个NumericVector,用sin(x)可以返回由x每个元素的正弦值组成的NumericVector。
比如,函数
\[f(x,y)=\begin{cases} x^2 & x<y \\ -y^2 & x>=y \end{cases}\] 如下的程序可以在C++中定义一个向量化的版本:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f11(NumericVector x, NumericVector y){
  return ifelse(x < y, x*x, -(y*y));
}
')
f11(c(1, 3), c(4,2))
## [1]  1 -4
上面简单例子中,x < y是向量化的,x * x,
y * y, -(y * y)都是向量化的。
ifelse也是向量化的。
Rcpp糖向量化了+, -, *, /。
设x, y都是NumericVector,
则x + y,x -     y,x * y,x * y将返回对应元素进行四则运算后的NumericVector变量。
向量与标量运算,如x + 2.0, 2.0 - x,
x * 2.0,
2.0 /  x将返回标量与向量每个元素进行四则运算后的NumericVector变量。
NumericVector res = x * y + y / 2.0;
NumericVector res = x * (y - 2.0);
NumericVector res = x / (y * y);
Rcpp糖扩展了两个元素的比较到两个等长向量的比较,以及一个向量与一个同类型标量的比较,结果是一个同长度的LogicalVector,比较运算符包括<, >, <=, >=, ==, !=。
对数值型向量或返回数值型向量的表达式前面加负号,可以返回每个元素取相反数的结果。
对逻辑型向量或返回逻辑型向量的表达式前面加叹号,可以返回每个元素取反的结果。
在C和C++代码中可以调用R中的函数,但是格式比较复杂,而且原来不支持向量化。 Rcpp糖则使得从C++中调用R函数变得和在R调用函数格式类似。
R源程序中提供了许多数学和统计相关的函数, 这些函数可以编译成一个独立的函数库, 供其它程序链接使用,函数内容在R的Rmath.h头文件中有详细列表。
Rcpp糖把R中的数学函数在C++中向量化了,输入一个向量, 结果是对应元素为函数值的向量。自变量类型可以是数值型或整数型。 这些数学函数包括abs, exp, floor, ceil, pow等。 在Rcpp中支持的C++源程序中调用这些函数, 最简单的一种方法是直接在Rcpp 名字空间中使用和R中相同的函数名和调用方法, 类似sqrt这样的函数允许输入一个向量, 对向量的每个元素计算。
比如,下面的程序输入一个向量,计算相应的ceil函数值:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f10(NumericVector x){
  NumericVector y(x.size());
  y = ceil(x);
  return y;
}
')
f10(c(-2.3, 1.9, 3.2))
## [1] -2  2  4
R中的rxxxx类的函数可以产生各种分布的随机数向量,随机数向量与当前种子有关。 Rcpp属性会自动地维护随机数发生器的状态使其与R同步。 如:
library(Rcpp)
sourceCpp(code='
#include <Rcpp.h>
using namespace Rcpp;
//[[Rcpp::export]]
NumericVector f15(){
  NumericVector x = rnorm(10, 0.0, 1.0);
  return x;
}
')
round(f15(), 2)
##  [1]  0.23 -0.88  1.44  1.49 -0.12  0.92  0.95  0.34 -0.73  0.01
Armadillo C++ Library是一种C++的线性代数库(矩阵数学)以取得良好的平衡速度与易用性。有整数,浮点,和复杂的数字支持,以及获取子集,进行三角和统计的功能。
RcppArmadillo包利用rcpp将R与C++结合了起来。
首先,安装RcppArmadillo包
install.packages("RcppArmadillo")
| Data Type | Mathematics | 
|---|---|
| mat | Matrix矩阵 | 
| vec/colvec | Column Vector列向量 | 
| rowvec | Row Vector行向量 | 
| cube | Cube 3维矩阵 | 
| field | 域 | 
mat X(n_rows,n_cols);
mat X(n_rows,n_cols,fill_type);
vec X(n_lem);
vec X(n_lem,fill_type);
其中fill_type是可选的,可以是以下选择
| fill_type | 描述 | 
|---|---|
| fill::zeros | 设置所有元素为0 | 
| fill::ones | 设置所有元素为1 | 
| fill::eye | 将矩阵对角线元素设置为1,其他元素为0 | 
| fill::randu | 随机生成服从0-1平均分布的元素 | 
| fill::randn | 随机生成服从[0,1]正态分布的元素 | 
也可以通过函数创建,如:
vec v = randu<vec>(5);
mat x = randu<mat>(5,5);
元素的初始化:
vec v = {1,2,3};
mat A = {{1,3,5},
         {2,4,6}}
元素访问:
| 元素访问 | 描述 | 
|---|---|
(n) | 
对于vec和rowvec,访问第n个元素。对于mat和field,首先把矩阵的下一列接到上一列之下,从而构成一个长列向量,并访问第n个元素。 | 
(i,j) | 
对于mat和二维field,访问第(i,j)个元素。 | 
子集函数访问:
| 函数 | 描述 | 
|---|---|
X.diag(k) | 
访问矩阵X的第k个对角线(k是可选的,主对角线为k=0,上对角线为k>0,下对角线为k<0) | 
X.row(i) | 
访问矩阵X的第i行 | 
X.col(i) | 
访问矩阵X的第i列 | 
X.rows(a,b) | 
访问矩阵X从第a行到第b行的子矩阵 | 
X.cols(c,d) | 
访问举证X从第c列到第d列的子矩阵 | 
其他详细内容可参考Armadillo操作手册
下面是一个用RcppArmadillo编写的线性回归程序:
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
Rcpp::List fastLm(const arma::mat& X, 
                  const arma::colvec& y) {
    int n = X.n_rows, k = X.n_cols;
        
    arma::colvec coef = arma::solve(X, y);    // fit model y ~ X
    arma::colvec res  = y - X*coef;           // residuals
    // std.errors of coefficients
    double s2 = std::inner_product(res.begin(), res.end(), res.begin(), 0.0)/(n - k);
                                                        
    arma::colvec std_err = arma::sqrt(s2 * arma::diagvec(arma::pinv(arma::trans(X)*X)));  
    return Rcpp::List::create(Rcpp::Named("coefficients") = coef,
                              Rcpp::Named("stderr")       = std_err,
                              Rcpp::Named("df.residual")  = n - k);
}