介绍

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次