介绍
CONNECT/C++是一个使用S语言和C++交互的接口工具。它为使用C++的程序员提供了把SPLUS引擎集成在其中的便利,同时,它也可以整合C++代码到SPLUS环境中去。
为了容许在GUI和SPLUS间通讯,在SPLUS7中,CONNECT/C++被开发用来提供了这样一个框架(基于S语句版本4)。事实上,SPLUS7 GUI提供了使用CONNECT/C++集成SPLUS引擎和C++应用更全面的例子。同样的,C++开发者也可以使用同样的技术来和SPLUS交互
CONNECT/C++是一个C++类库,使用这些类和它们的成员可以创建和处理大多数的本地S对象
为在C++程序和模块中计算S表达式,CONNECT/C++提供了一个灵活多变的机制,在SPLUS7.X中提供了许多使用这些类库的例子,其中有些例子提供了执行相同任务的等价的S和C++函数,其中C++函数大多数运行时间都要快于S代码,当然这也依赖于代码的复杂度和数据的尺寸。这些例子你可以在SHOME/sconnect目录中找到,其中SHOME为你的SPLUS的安装目录。
资源
更多关于CONNECT/C++的信息,请参见SPLUS7.0 CONNECT/C++库帮助,如果你再UNIX上,请查看SHOME/connect/help/ConnectC++ .Class.library.htm文件。这个HTML文件是一个CONNECT/C++类库的C++开发者指南。它仔细讨论了如何连接到SPLUS引擎,如何建立数据对象、调用SPLUS函数和执行SPLUS语法
简单的例子:一个简单应用的执行流程
CONNECT/C++主要用于实现这2个目标:建立一个能访问SPLUS功能的C++应用;建立一个能被SPLUS调用的C++函数,下面我们开始一个简单的应用
建立一个简单的应用
这个例子是一个控制台程序,用来在应用中建立2个SPLUS向量,然后使用SPLUS来建立关于这2个向量的线性模型。
这个代码开始行包含了一个sconnect.h文件,这是必须的,所有CONECT/C++代码必须引用这个文件。然后定义一个全局的SPLUS连接对象,这个类的名字为CSPengineConnect, 它必须在主调函数之前定义,这个CSPengineConnect类会在客户应用和SPLUS间生成一个连接,这将允许你建立SPLUS对象,并且当数据库被附加或者被卸载到客户的时候发出通知,而且还可以执行S语言表达式等。代码看起来像下面所示:
#include "sconnect.h"
// A global connection object
CSPengineConnect g_engineConnect;
int main(int argc, char* argv[])
{
主函数的第一步是建立一个实际的连接对象(连接到SPLUS)
// Create the connection to S-PLUS
g_engineConnect.Create( argc, argv);
然后我们建立变量X,Y,这个CSPnumeric用于存储数字型向量。这个类是CONNECT/C++中的一个类,被使用在C++中用来表示SPLUS中的对象,同样,也还有很多C++类对应了SPLUS中的原子对象(见表7。1);接下来通过使用create方法建立这个类的一个实例,通过Assign方法赋值这个类到SPLUS数据库,如下
// Create S object with name "x" in the current database.
// Same as x<-1:10 at the command line. CSPnumeric sx;
sx.Create("1:10","x");
// Squaring sx, which is the same as S expression
// sy <- x*x in a local frame, but here we set it to local
// C++ variable sy. CSPnumeric sy = sx * sx;
// Assign the result as S object with name "y" in the
// current database. sy.Assign("y");
最后,我们准备合适的线性模型,通过CONNECT/C++方法在适当的时候调用SPLUS执行,如下
// Evaluate z<-lm(y~x)
g_engineConnect.SyncParseEval("z<-lm(y~x)");
return 1;
}
这个例子的完整代码在SHOME/samples/spllm(WIN下),UNIX下这个目录在SHOM E/sconnect/samples/splm.
要运行这个应用,打开一个命令提示或者MS-DOS窗口(WIN)或者编译(UNIX)
1. 改变当前目录到包含代码的目录
cd SHOME/samples/spllm
(WIN)
cd /sconnect/samples/splm
(UNIX,假设当前路径为SHOME)
2. 编译程序
msdev spllm.dsp /make
(WIN)
Splus7 CHAPTER -sconnectapp *.cxx
Splus7 make
(UNIX)
3. 如果你在WIN下,检查环境变量PATH确认它包含了%SHOM E%/cmd,如果没有,你应该在运行下面的步骤前把它加上去。
4. 运行程序
spllm.exe S_PROJ=.
(WIN)
Splus7 EXEC S.app
(UNIX)
要验证运行结果,在相同目录下启动SPLUS控制台(WIN)或者启动SPLUS(UNIX)
sqpe.exe S_PROJ=.
在WIN平台上,SPLUS将返回下面信息
S-PLUS : Copyright (c) 1988, 2005 Insightful Corp. Version 7.0 Release 1 for Microsoft Windows : 2005
Working data will be in E:/programs/splus7.0/users/rich
或者输入
Splus7
在UNXI上,将返回下面信息:
S-PLUS : Copyright (c) 1988, 2005 Insightful Corp. Version 7.0 Release 1 for Sun SPARC, SunOS 5.8 : 2005
Working data will be in .Data
然后查看对象X,Y和Z:
|
[1] | 1 | 2 | 3 | 4 |
> y |
|
|
|
|
[1] | 1 | 4 | 9 | 16 |
>
x
5 6 7 8 9 10
25 36 49 64 81 100
> z
Call:
lm(formula = y ~ x)
Coefficients:
(Intercept) x
-22 11
Degrees of freedom: 10 total; 8 residual
Residual standard error: 8.124038
通过CALL调用C函数的例子
Gauss-Seidel是一个熟悉的技术,主用于线性回归系统的解决。这个算法在SPLUS中是很容易简单明了的实现,如下
gaussSeidel<-
# gaussSeidel solves a linear system using Gauss-Seidel
# iterative method.
# REQUIRED ARGUMENTS:
# A and b are numeric matrix and vector respectively.
# VALUE:
# a vector x, solution of A x = b
#
# Usage:
# A<-matrix(rnorm(100),nrow=10)
# diag(A)<-seq(ncol(A),ncol(A)) #Make it diagonally
# # dominant
# b<-rnorm(ncol(A))
# sys.time({x1<-gaussSeidel(A,b)})
function(A,b)
{
# Hard-coded relative tolerance and max iterations tol<-1.0e-4
maxItr<-1e4
# Validating
A <- as.matrix(A)
b <- as.numeric(b)
if(nrow(A)!=ncol(A) || ncol(A)!=length(b))
stop("nrow(A)!=ncol(A) || ncol(A)!=length(b)")
# Begin Gauss-Seidel step
x<-b
for(k in 1:maxItr)
{
xOld<-x
for(i in 1:nrow(A))
{
s<- A[i,i]*x[i]
for(j in 1:ncol(A))
s <- s - A[i,j]*x[j]
x[i] <- (b[i]+s)/A[i,i]
}
# Check convergence; continue if necessary if(max(abs((x-xOld)/x)) < tol)
return(x);
}
warning("Solution does not converge/n")
return(x)
}
这个代码中,包含了一个嵌套的循环,也许可以写成更加有效率的代码,但我们这只是一个演示。通过使用CONNECT/C++的类和方法,下面的这些代码实现了上面类似的功能
这些代码开始处包含了sconnect.h头文件,这样我们就可以访问SCONNECT/C++库,下一步,它包含了一个实现Gauss-Seidel的头文件
# include "sconnect.h"
# include "gausssdl.h"
当我们定义gaussSeidel对象作为s_object类的一个对象时,它请求通过CALL交互
s_object* gaussSeidel(s_object* ps_A, s_object* ps_b)
作为一个典型的代码,我们定义了S_EVALUATOR.然后嵌入了我们的实现代码在try-catch块中,在try 块里,可能发生的错误是可以捕抓的。如下代码
{
S_EVALUATOR
try
{
// Hard-coded relative tolerance and max iterations double tol =1e-4;
long maxItr = 1000;
// Constructing and validating C++ objects
CSPnumericMatrix A(ps_A); CSPnumeric b(ps_b);
if(A.nrow()!=A.ncol() || A.ncol()!=b.length())
PROBLEM "A.nrow()!=A.ncol() || A.ncol()!=b.length()" ERROR;
实际的Gauss-Seidel步骤如下:
// Begin Gauss-Seidel step
CSPnumeric x=b;
for(long k =1; k<= maxItr; k++)
{
CSPnumeric xOld = x;
for(long i= 1; i <= A.nrow(); i++)
{
double s = A(i,i) * x(i);
for(long j = 1; j <= A.ncol(); j++)
s = s - A(i,j) * x(j);
x(i) = (b(i)+s)/A(i,i);
}
// Check convergence; continue if necessary if(Max(abs((x-xOld)/x)) < tol)
return(x);
}
PROBLEM "Solution does not converge" WARN;
return(x);
}
catch(...)
{
}
return(blt_in_NULL); // return the built-in NULL object
}
在UNXI上编译和执行C++
这个例子代码在SHOM E/ sconnect/samples/gausssdl下,C++代码在gausssdl.cxx中
编译和执行C++代码
1. 改变当前目录到包含代码的目录
cd SHOME/sconnect/samples/gausssdl
2. 编译共享库
Splus7 CHAPTER -sconnectlib *.cxx
Splus7 make
3. 运行SPLUS
Splus7
通过CHAPTER生成makefile,编译的代码只是需要在SPLUS后加上MAKE命令,如步骤2
在MAKE前可以适当的设置环境变量
make工具执行了必要的命令编译和连接C++代码到共享对象S.so
.注意:-sconnectlib是被请求包含CONNECT/C++库
通过CALL调用CONNECT/C++要比SPLUS代码快,下面是一个比较,运行在奔腾3 512内存,WIN平台,矩阵A为100行100列
> A<-matrix(rnorm(10000),nrow=100); diag(A)<-seq(ncol(A),
+ ncol(A)) # Make it diagonally dominant
> b<-rnorm(100);
> sys.time({x1<-gaussSeidel(A,b)})
[1] 19.328 19.354
下面是一个运行在Solaris机器上的一个比较,矩阵也是100列100行
[1] 37.00 39.35
> sys.time({x2<-.Call('gaussSeidel',A,b)})
我们比较(通过sys.time) 在不同平台的输出的时间:
[1] 0.07 0.07
在WIN上
[1] 0.04 0.04
在UNIX上t.
可以看出,相同的CONNECT/C++版本比WIN上快250次,比纯SPLUS版本快大约1000次