MathLink同高级语言混合编程

来源:互联网 发布:java获取浏览器cookie 编辑:程序博客网 时间:2024/06/06 05:49

 

 

 

利用MathLink同高级语言混合编程(一)

概述 同外部程序进行交互是Mathematica中令人激动的一项功能。您可能学会了如何在Mathematica中通过输入语句的方法进行各种数值和符号运算。然而,您可能曾用一些高级计算机语言编写过一些针对具体应用的计算程序或者函数,现在想在Mathematica中调用这 些函数;或者您想在自己的程序中集成Mathematica的强大计算功能。利用Mathematica提供的MathLink系统,这些都能够轻松实现,您甚至可以通过网络调用运行在远程主机上的程序。

Mathematica采用结构化和非结构化两种方式与外部程序进行通讯。结构化通讯是指把Mathematica的表达式交给事先建立起来的处理这些表达式的外部程序,其基础就是MathLink系统。非结构化通讯是指从外部程序中接收文本数据,进行读写操作。我们将要介 绍的是结构化的通讯方式,也可以看作是Mathematica同高级语言的混合编程。使用MathLink可以把Mathematica建立的表达式发送给外部程序,或者将外部程序的结果读进来 。我们将重点介绍MathematicaC/C++语言的混合编程,并通过一个实例介绍如何利用 Visuall C++编写MathLink程序以及如何在利用Visual C++创建的应用程序中集成Mathematica的计算功能。

 

x.1.初识MathLink

在深入讨论混合编程的细节之前,我们有必要首先搞清楚Mathematica进行结构化通讯的基础MathLink的相关问题。

 

x.1.1什么是MathLink

MathLink是一种在程序之间交换Mathematica表达式的机制,它提供了一个外部程序同Mathematics通讯的通用接口。通过内置或者插件技术,现有的许多软件都是现了对MathLink的兼容性。这些程序都可以透明地建立同Mathematica间的连接。这种连接可以是在 同一台计算机上的本地连接,也可以是跨越计算机网络的远程连接,并且连接双方主机 可能是不同类型的计算机,如Microsoft WindowsMacintosh系统间可以建立连接。同时,Mathematica提供了MahLink开发工具进行MathLink程序的设计。利用这个工具,你可以方便地设计自己的兼容MathLink的应用程序。 通过MathLink实现的典型应用有:

?   Mathematica内部调用外部程序的函数;

?   在外部程序中调用Mathematica的功能;

?   构建Mathematica可选择的前端应用程序;

?   Mathematica和外部程序间交换数据;

?   在当前的Mathematica进程间交换数据。 MathLink库提包含一些能够实现在外部程序中发送以及接收Mathematica 表达式的程序集合。MathLink的一个重要特征是完全平台无关的。也就是说用户的外部程序完全可以不考虑MathLink运行在什么平台上,MathLink能够透明地使用您计算机系统中的内部程序通讯机制。

 

x.1.2安装MathLink

在缺省的安装情况下,安装Mathematica时就会安装MathLink开发工具(MathLinkDeveloper's Kit)。一般情况下MathLink Developer's Kit的安装目录是:Mathematica/4.0/AddOns/MathLink/DevelopersKits/Windows。安装MathLink主要包括两部分:系统要求的部分和编译程序要求的部分。

第一部分,系统要求的部分包括一些完成MathLink功能的动态连结库。大多数的MathLink程序运行所需的共享库是:"ML32I2.DLL", "ML32I1.DLL","MLMAP32.MLP", "MLTCP32.MLP"。您可以在插件子目录的SystemAdditions目录中找到他们。它们通常被安装Mathematica时被安装在系统可以找到的默认位置:MathLink是作为一个共享的动态连接库实现的。当运行一个MathLink程序时,操作系统 将在磁盘上找到主动态连结库ML32I2.DLL并装载进系统中,并且建立同用户程序的关联。这样,调用的MathLink函数被映射到动态连接库中的相应代码。在Windows操作系统中,ML32I2.DLL一般被装载在Windows的系统目录下。除了ML32I2.DLL,还有一些辅助动态 连结库(如MLTCP32.MLP MLMAP32.MLP),这些文件通常放在Windows目录下。

第二部分,编译程序要求的部分主要包括支持特定高级语言(这里是C语言)编译的一些 文件:.LIB 文件、"mathlink.h"头文件、"MPREP32.EXE"代码自动生成程序等,分别在安装Mathematica时安装在目录中的结构是:

?   "MLDev32/LIB                  包含库文件

?   "MLDev32/INCLUDE             包含头文件

?   "MLDev32/BIN                  包含代码自动生成程序

当创建一个MathLink程序时,需要导入一个库文件(一个.LIB 文件)。这个导入库文件 被加入到工程中,它并不包含代码,只是为了方便用户输出同共享库中相同的函数名。MathLinkC语言接口在头文件"mathlink.h"被详细说明。在调用MathLink共享库的所有 CC++源文件中都应该包含这个头文件。 "MPREP"是一个32位的控制台应用程序,它可以根据模板自动地生成MathLink程序部分代码。将它拷贝到您的开发工具的BIN目录下会给你的开发带来方便。除了这两部分以外,安装MathLink时还会拷贝一些文档、例程等,这些都可以在对应的 文件夹中找到。

 

x.1.3 MathLink支持的编译器

MathLink是通过一个共享库实现的,因此它可以被任何遵从微软规定的动态连结库接口 标准的编译器。对于C/C++语言来说,常用编译器有:

?   Borland C/C++

?   Metrowerks CodeWarrior C/C++

?   Microsoft Visual C++ and Visual Basic

?   Symantec C/C++

?   Watcom C/C++

以上的每一个开发工具都遵从一种"工程文件"的机制,通过它在开发环境中统一地管理 程序源文件、编译器选项、联接器以及调试器等。除了这种集成开发环境(IDE),他们 都提供一些从命令行运行的命令来编译生成代码。

x.1.4怎样用MathLink同外部程序交互

1 Mathematica结构图

Mathematica的结构其实比较清晰:Mathematica Kernel + External Program,包括Standard Notebook front end都可以看作是一个完善的外部程序。显然,要使用Mathematica提供的各项计算分析功能,必须启动Mathematica Kernel并建立外部程序与其的一 个联系通道。 上图说明,MathLink实际上提供了一种访问Mathematica内核以及内核访问外部程序的途 径。

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....


MathLink混合编程(二)

x.2Mathematica中调用外部程序

我们首先来看一下如何在Mathematica中装载外部的MathLink程序,如何利用程序中的函 数、如何卸载程序;然后通过一个实例重点介绍如何利用Visual C++ 6.0 创建并生成一个MathLink程序;最后简单介绍如何处理各种类型的表达式。

x.2.1调用的步骤及相应的命令

MathLink最通常的用法是在Mathematica中调用外部MathLink程序。当外部程序建立完成 后,我们就可以装载这个程序,并且在当前的Mathematica进程中调用程序中的函数。装载及卸载外部MathLink程序的语句是:

?   Install["prog"]   装载一个外部MathLink程序

?   Uninstall[link]   卸载外部程序

例一:我们举一个例子,这个例子中使用的例子可以在MathLink开发工具文件夹的"MathLinkExamples"中找到源代码,已经编译好的程序"addtwo.exe""PrebuiltExamples" 中可以找到。外部函数的源代码如下:

inaddtwo(int i, int j)

{

   return(i+j); //返回两数之和

}

下面,我们就在Mathematica中调用这个函数实现两数相加的运算:

运行Mathematica,输入下面的命令:

In[1] :=link = Install[LinkLaunch[]]

确认后会出现一个文件打开对话框选择要加载联接的外部程序,选择编译好的程序"addtwo.exe"。这样,外部程序就被加载了,返回的信息是:

Out[1] =LinkObject[C:/MathLink/addtoe.exe,2,2]

其中,"C:/MathLink/addtoe.exe"是选择的外部程序。然后,就可以使用外部程序中的函数了。

2   显示该外部函数AddTwo的用法:

2   调用该函数:

2   如果传递的参数不符合原型函数的要求,将不会得到返回值:

2   卸载该外部程序:

常用的处理外部函数连结的命令主要有:

命令格式                      用法说明

Install["prog"]                  装载一个外部程序

Unstall[link]                    卸载一个外部程序

Links["prog"]                  显示同外部程序"prog"相关联的活动连结

Links[]                            显示所有的活动连结

LinksPatterns[link]            显示特定连结上的求值模式

注:其他处理MathLinkMathematica命令请参考帮助文档。

x.2.2创建MathLink外部程序的一般步骤

如果已经在外部文件中定义并且实现了函数原型,那您将要作的就是在源代码中加入一些MathLink相关的代码,从而实现从Mathematica中调用这个函数并得到函数的返回值。简单的情况下,可以使用建立MathLink临时文件,通过代码自动生成程序自动生成调用MathLink相关函数的C语言源代码。一般地说,建立一个MathLink外部程序主要要完成三步工作:

?   建立MathLink临时文件;

?   处理临时文件生成的源代码;

?   提取源代码,加入函数实现的主代码并编译生成外部程序。

MathLink临时文件由一系列指令组成,可以看作是一个代码生成模版。其中的指令规定了外部程序如何在Mathematica中被调用。临时文件建立了在Matheamtica中定义函数同外部程序相应函数的联系关系。临时文件用生成的源代码必须包含在外部文件中,以使外部程序具有MathLink兼容性。

下面是MathLink模板中包含的元素:

指令                                说明

Begin                         标志对应特定函数的模板的起始

Function                    外部程序中的函数名

Pattern                        调用此函数方式的定义

Arguments                 函数的参数

ArgumentTypes          函数参数值类型

ReturnType                 返回值类型

End                             标志对应特定函数的模板的结束

Evaluate                    在函数加载时Mathematica的输入计算

注:函数参数值类型及函数返回值类型类表请参看下表。

例一:接着我们给出一个MathLink模板的例子:

Begin

Function           addtwo

Pattern            AddTwo[x_Integer , y_Integer]

Arguments         {x , y}

ArgumentTypes      {Integer , Integer}

ReturnType         Integer

End

:Evaluate:AddTwo::usage = "AddTwo[x, y] gives the sum of two machine integers x andy."

这个例子定义了例一中AddTwo函数的MathLink模板,并保存在文件addtwo.tm中。编写好模板后将其保存在模板文件(*.tm)中,利用代码生成程序生成源代码。不同平台不同操作系统上对应不同版本的代码生成程序,一般是mprep处理程序。Windows系统下的用户在MathLink的安装目录中可以找到"MPREP16.EXE"或者"MPREP32.EXE"程序(分别对应16位和32位处理程序),利用这两个工具可以生成Windows平台下C/C++编译器可以识别编译的源代码。

利用命令:

mprepaddtwo.tm -o addtwom.c

将会在生成文件addtwom.c中包含生成的源代码。需要注意的是,如果文件mprep.exeaddtwo.tm不在当前目录下则要在命令中指定其路径。

接下来,我们将编写外部程序主源码,并将生成的代码加入到外部程序源文件中,最后编译生成一个MathLink兼容的外部程序。

2   在外部程序源文件中包含MathLink的标准头文件

#include"mathlink.h"

2   加入实际函数代码

inaddtwo(int i, int j)

{

   return(i+j); //返回两数之和

}

2   编写主函数以使外部程序接收来自Mathematica的调用请求

intmain(int argc, char* argv[])

{

return MLMain(argc, argv);

}

2   最后利用C/C++编译器编译这个外部程序,生成最终可执行程序。下面一节将专门讨论利用Visual C++编译器编译外部程序的步骤。

x.2.3使用Visual C++ 6.0创建MathLink程序

我们将利用VC6.0实现一个MathLink程序 ,通过这个程序说明创建外部程序的详细步骤。我们的程序中包含一个根据经验公式编写的查表计算程序。

我们简化一下这个查表函数,去掉同具体算法相关的细节,把重点放在实现同MathLink的接口实现上。该函数myfun根据调用时传递的两个参数值int lineint row在一个数据表查找对应的数据项,并将查到的数据返回。

下面,我们来看怎样分步实现这个程序:

1.  启动MicrosoftDeveloper Studio,进入Visual C++的集成开发环境;

2.  File菜单中选择 New 将会打开一个新建对话框;

3.  New对话框中单击Projects标签;

4.  Location文本框中选择工程将被创建的路径;

5.  ProjectName文本框中键入工程名MYFUN

图二 VC中新建一个工程MYFUN

6.  在左边的Project类型列表中选择 Win32 Application

7.  在右下的Platforms列表框中确保Win32被选中了;

完成后如图二所示

8.  单击OK确认,将出现Win32 Application对话框;

9.  选择emptyproject并单击Finish完成,将出现New Project Information对话框;

10. 单击OK确认;

接下来,我们在工程中添加我们的代码文件。我们将需要编辑两个文件,分别是myfun.cmyfun.tm,对应主程序源文件和MathLink的模板文件。

11. Project菜单的AddTo Project子菜单中选择New…,将出现新建对话框,选中Files标签;

12. 在文件名中键入以下文件名,以空格隔开:"myfun.c"   "myfun.tm"   "myfuntm.c"

13. Project菜单的AddTo Project子菜单中选择Files,将出现插入文件对话框。定位并插入文件 "ml32i2m.lib"。注意,这个文件是在安装MathLink时拷贝到硬盘上的,我们前面在介绍安装MathLink时提到过。我的Mathematica安装在D:/ProgramFiles/Wolfram Research,我在MathLink的安装目录中找到了这个文件:

D:/ProgramFiles/WolframResearch/Mathematica/4.0/AddOns/MathLink/DevelopersKits/Windows/CompilerAdditions/mldev32/lib

您可能觉得这个文件隐藏地太深了。没关系,您可以将MathLink拷贝到一个适当地位置,如Microsoft Developer Studio安装目录下。

下面,我们编写主程序以及模板文件:

14. Workspace中打开文件myfun.tm,加入下面的代码:

int myfunP(( int, int));

:Begin:

:Function:       myfun

:Pattern:        MyFun[line_Integer, row_Integer]

:Arguments:      { line, row }

:ArgumentTypes:  { Integer, Integer }

:ReturnType:     Real

:End:

:Evaluate:MyFun::usage = "MyFun[line,  row] retrunfloat value in the line/row position."

15. Workspace中打开文件myfun.c,加入下面的代码:

#include"mathlink.h"

externdouble myfun( int line, int row);

doublemyfun( int line, int row)

{

    double ret=0.8979*line-row;

 

    /* 这里省略了具体处理的代码*/

 

return ret;

}

#ifMACINTOSH_MATHLINK

int main(int argc, char* argv[])

{

    /* Due to a bug in some standard Clibraries that have shipped with

     * MPW, zero is passed to MLMainbelow.  (If you build this program

     * as an MPW tool, you can change the zeroto argc.)

     */

    argc = argc; /* suppress warning */

    return MLMain( 0, argv);

}

#elifWINDOWS_MATHLINK

#if__BORLANDC__

#pragmaargsused

#endif

intPASCAL WinMain( HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, LPSTRlpszCmdLine, int nCmdShow)

{

    char buff[512];

    char FAR * buff_start = buff;

    char FAR * argv[32];

    char FAR * FAR * argv_end = argv + 32;

    hinstPrevious = hinstPrevious; /* suppresswarning */

    if( !MLInitializeIcon( hinstCurrent,nCmdShow)) return 1;

    MLScanString( argv, &argv_end,&lpszCmdLine, &buff_start);

    return MLMain( argv_end - argv, argv);

}

#else

int main(argc,argv)

    int argc; char* argv[];

{

    return MLMain(argc, argv);

}

#endif

下面设置编译选项以及编译前的代码生成:

16. Project菜单中选择Settings,将出现Project Settings对话框;

17. 在左边的设置列表中展开工程MYFUN,并选择文件myfun.tm

18. 单击Custom Build标签显示CustomBuild页;

19. Description文本框中输入编译的描述,如:"mpreping ..."

20. Build Commands列表框的第一行中输入:

mprepmyfun.tm -o myfuntm.c

21. Output Files列表框的第一行中输入:

./myfuntm.c

图三 设置编译选项

22. 单击Ok确认

在编译之前,我们还要将相应版本的mathlink.h拷贝到编译器默认的包含文件目录下或者该工程文件夹中。选择Tools菜单下的Options…子菜单,在出现的对话框中的Directories标签栏内可以查看和设置编译器的缺省目录。另外,还要将相应的mprep.exe文件拷贝到工程文件夹中。

下面可以开始编译了:

23. 选择Build菜单中选择BuileMYFUN.EXE或者按快捷建F7,将会编译生成一个MathLink兼容程序MYFUN.EXE

最后,简单介绍一下编译的过程:

2   系统首先执行用户定制的步骤,即在图三所示界面上的设置,通过这一步系统调用mprep程序自动生成了MathLink相关源代码,存放在文件myfuntm.c中。我们可以打开该文 件看看到底生成了些什么代码。有兴趣的读者可以参考Mathematica的帮助文档理解这些代码。

2   接着是常规地编译,按照顺序分别编译了myfun.cmyfuntm.c两个文件。

2   最后是连接并生成目标代码。

可以看到,除了编译前的"执行用户定制的步骤",并没有特殊的地方。我们完全可以通过手工调用命令mprep myfun.tm -o myfuntm.c而取代"执行用户定制的步骤"。但是这样每次对模板文件myfun.tm改动后都要输入该命令重新生成一次,比较麻烦。所以我们建议在编译器参数设置中加入用户定制的步骤以自动生成myfuntm.c文件。

x.2.4处理各种交换数据类型

在上面的例子中,我们的从Mathematica传递给外部含数的数据是整型变量,函数返回的是符点实型变量。事实上,利用MathLink可以同外部程序交换任何类型的数据。可以在模板文件中的:ArgumentTypes:和:ReturnType:中指定函数参数以及返回值的数据类型。

下表给出了在模板文件中定以的基本数据类型以及C语言中相对应的类型:

Mathematica中的定义   说明                 C语言中的定义

Integer                         整型变量          int

Real                          浮点型变量         double

IntegerList                    整型变量列表      int*, long

RealList                     浮点型变量列表 double*, long

String                         字符串变量        char *

Symbol                       符号名变量        chat *

Manual                       直接调用             MathLink程序   void

基本数据类型对照表

类似我们上面介绍的例子,您可以在模板中的参数类型段使用上面任何一种变量类型。需要注意的是,如果您使用IntegerList或者RealList作为变量类型,则必须在外部的C函数中包含附加的参数来指定列表的长度。

例如,在模板中您这样定义参数类型:

ArgumentTypes{IntergerList, RealList, Interger}

则对应的C函数定义可能是:

voidf(int *a, long alen, double *b, long blen, int c);

下面将介绍在MathLinkC语言程序间更加灵活地传递数据的方法。先来看一个例子:

模板文件:

:Begin:

:Function:            otherfun

:Pattern:               OtherFun[line_Integer,row_Integer]

:Arguments:          { line, row }

:ArgumentTypes:   { Integer,Integer }

:ReturnType:         Manual

:End:

C语言函数:

Voidotherfun(int ling, int row)

{

    int a[32], k;

    for(k=0;k<32;k++){

  ……

//根据具体的算法给数组a赋值

}

  k++;

  MLPutIntegerList(stdlink, a, k);

  return;

}

我们看到,在模板中定义的函数返回值类型是Manual,即在外部函数中直接调用MathLink的函数向Mathematica返回数据。

这调用的函数是MLPutIntegerList,下面列出其他几个常用的类似的函数,他们在"mathlink.h"中都有定义。从函数的名字中可以猜测其意义,这里不再详细介绍了。

MLPutInteger(MLINKlink, int i)

MLPutReal(MLINKlink, double x)

intMLPutIntegerList(MLINK link, int *a, long n)

intMLPutRealList(MLINK link, double *a, long n)

intMLPutIntegerArray(MLINK link, int *a, long *dims, char **heads, long d)

intMLPutRealArray(MLINK link, double *a, long *dims, char **heads, long d)

 

intMLPutFunction(MLINK link, char *s, long n)

intMLPutString(MLINK link, char *s)

intMLPutSymbol(MLINK link, char *s)

上面介绍的是从C语言中通过调用MathLink函数返回数据给Mathematica,如果要从外部函数中直接通过调用MathLink函数得到传递过来的数据,则要用到另外一组函数:

intMLGetInteger(MLINK link, int *i)

intMLGetReal(MLINK link, double *x)

int MLGetIntegerList(MLINKlink, int **a, long *n)

intMLGetRealList(MLINK link, double **a, long *n)

intMLGetIntegerArray(MLINK link, int **a, long **dims, char ***heads, long *d)

intMLGetRealArray(MLINK link, double **a, long **dims, char ***heads, long *d)

下面看一个具体的例子:

模板文件:

:Begin:

:Function:       getfun

:Pattern:        GetFun[a,b,c]

:Arguments:      { a,b,c}

:ArgumentTypes:  { Integer, Manual }

:ReturnType:     Real

:End:

C语言函数:

Voidgetfun(int a)

{

    double b,c;

    MLGeReal(stdlink, &b);

MLGeReal(stdlink, &c);

return a+b*c;

 }

在这个函数中,我们利用函数参数传递整型变量int a,通过MathLink函数MLGetReal得到另外两个浮点型变量bc。通过调用这些MathLink外部函数,可以很灵活在Mathematica以及外部C语言函数间交换数据。

事实上,利用mprep程序生成的也正是一些调用MathLink库的外部函数的代码,系统利用这些自动生成的代码,建立了Mathmatica同外部程序之间的连接。我们可以通过阅读这些代码深刻理解MathLink是怎样工作的。

如果读者想了解有关MathLink外部函数更详细的资料,可以参考Mathematica的帮助文档。 

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....


MathLink混合编程(三)

x.3从外部程序中同Mathematica交互

这一节里,我们将向您介绍如何在外部程序中集成Mathematica的强大计算功能。这同上一节中介绍的内容是不同的,上一节中我们关注的是如何在Mathematica中调用外部程序中的函数以扩充Mathematica处理具体问题的能力。两者思路刚好相反,但都是混合编程方面的内容,都达到了结合C语言灵活地处理能力和Mathematica强大数值、符号处理能力的目的。 我们首先介绍在外部程序中调用Mathematica计算模块的原理,然后介绍编写此类外部程 序的一般步骤,最后给出一个"代数多项式展开"的例子。

x.3.1建立同Mathematica连接的原理

要在外部程序中利用Mathematica内部计算功能需要使用MathLink的许多一般的特征。我们前面提到过,通过编写MathLink模板并调用相关程序我们可以得到一些MathLink自动生成的代码。这些代码向外部程序提供与MathLink连结通讯的手段。也就是说,这些代码封装了实际调用MathLink库的细节,我们所需要作的就是在程序中调用MLMain(argc, argv)函数,这在我们前面的外部程序中的主函数中可以看出。

我们先来看一下,调用MLMain(argc, argv)函数是如何建立这个连接的,我们以x.2.3 提到程序的为例:

intMLMain( int argc, charpp_ct argv)

{

    return _MLMain( argv, argv + argc,(charp_ct)0);

}

该函数直接调用了自动生成的另一个函数

_MLMain static int _MLMain( charpp_ct argv,charpp_ct argv_end, charp_ct commandline)

{

    MLINK mlp;

    long err;

    if( !stdenv)

        stdenv = MLInitialize( (MLParametersPointer)0);

    if( stdenv == (MLEnvironment)0) goto R0;

    if( !stdyielder)

        stdyielder = MLCreateYieldFunction(stdenv,

            NewMLYielderProc(MLDefaultYielder), 0);

    if( !stdhandler)

        stdhandler = MLCreateMessageHandler(stdenv,

            NewMLHandlerProc(MLDefaultHandler), 0);

    mlp = commandline

        ? MLOpenString( stdenv, commandline,&err)

        : MLOpenArgv( stdenv, argv, argv_end,&err);

    if( mlp == (MLINK)0){

        MLAlert( stdenv, MLErrorString( stdenv,err));

        goto R1;

    }

    if( MLIconWindow){

        char textbuf[64];

        int len;

        len = GetWindowText(MLIconWindow,textbuf, sizeof(textbuf)-2);

        strcat( textbuf + len, "(");

        _fstrncpy( textbuf + len + 1,MLName(mlp), sizeof(textbuf) - len -3);

        textbuf[sizeof(textbuf) - 2] = '/0';

        strcat( textbuf, ")");

        SetWindowText( MLIconWindow, textbuf);

    }

    if( MLInstance){

        if( stdyielder) MLSetYieldFunction(mlp, stdyielder);

        if( stdhandler) MLSetMessageHandler(mlp, stdhandler);

    }

    if( MLInstall( mlp))

        while( MLAnswer( mlp) == RESUMEPKT){

            if( ! refuse_to_be_a_frontend(mlp)) break;

        }

    MLClose( mlp);

R1:MLDeinitialize( stdenv);

    stdenv = (MLEnvironment)0;

R0:return !MLDone;

} /*_MLMain */

我们简单分析一下这个函数:

2   MLInitialize函数执行初始化过程以初始化MathLink库函数,是整个连接过程的第一 步。调用任何MathLink库函数之前都要调用这个函数以完成连接环境的建立,系统将返回一个MLEnvironment变量表明库初始化成功。

2   MLCreateYieldFunction函数创建输出函数并返回MLYieldFunctionObject变量表明创建成功。这不是建立连接所必需调用的函数。

2 MLCreateMessageHandler函数创建了连接的消息处理句柄并返回一个MLMessageHandlerObject变量表明创建成功。这也不是建立连接所必需调用的函数。

2  MLOpenArgvMLOpenString是两个比较重要的函数,他们实际完成建立同Mathematica Kernel的连接。两者的区别在于:MLOpenArgv将与程序主函数相同的参数传递给MathLink以建立一个连接,而MLOpenString函数将所有参数集中放入一个字符串中传递给MathLink以建立连接。主函数_MLMain通过变量commandline来区分到底调用两者中的哪一个来建立连接。最后,函数返回一个MLINK变量标志创建连接的结果。

2   接下来是创建窗口,并利用函数MLSetYieldFunctionMLSetMessageHandler将创建的 输出函数和消息处理句柄同已经建立的连接联系起来。

2   主函数末尾的while语句是外部程序程序的主循环。上面,我们介绍了利用mprep生成的源代码是如何建立同Mathematica连接的。有时为了增加外部程序的灵活性或者出于其他的一些考虑,我们需要直接在程序中调用内部的这些MathLink库函数以建立连接。当然,更多的情况下,我们希望能在外部程序中调用MathLink的库函数以实现利用内核计算的目的。下面的一节里,我们着重介绍如何编写这类外部程序。

x.3.2怎样编写此类外部程序

编写此类MathLink外部程序的重点是如何处理同Mathematica的数据交互问题。外部程序在成功建立连接之后,通过调用MathLink库函数向MathematicaKernel发送调用其内部 计算模块的请求,Kernel接收请求调用相应的模块进行处理之后将计算结果发送给外部 程序,外部程序调用相应的函数接收返回的计算结果。 Mathematica表达式提供了一种处理各种数据的通用的方法。我们可能希望在外部程序中处理这些表达式。然而,类似C语言这样的高级计算机语言并没有提供直接存储、处理这些表达式的方法。幸运的是,MathLink提供了使用环回连接(loopback links)来处理Mathematica表达式的方法,进而达到与Mathematica交互的目的。 MLINK MLLoopbackOpen(stdenv, long *erreo) 函数打开一个环回连接;void MLClose(MLINK link) 函数关闭一个环回连接; MLTransferExpression(MLINK dst, MLINK src)函数将表达式从源连接src传送到目标连接dst 我们稍后会介绍这种使用环回使用Mathematica表达式的方法。 下面,我们先来看三个问题:

第一,       外部程序如何发出请求?

我们首先来看下面的这个程序段:

MLENV ep= (MLENV)0;

MLINK lp= (MLINK)0;

//初始化MathLink

ep =  MLInitialize( (MLParametersPointer)0);

……

//打开一个连接

lp =MLOpenArgv( ep, argv, argv + argc, &err);

……

//调用MLPut*函数向内核传递命令及数据

MLPutFunction(lp, "EvaluatePacket", 1L);

MLPutFunction(lp, "FactorInteger", 1L);

MLPutInteger(lp, n);  //n是参数

//同步函数表明包已经传送完毕

MLEndPacket(lp);

这一段函数完成了向内核传递数据包的工作,下面等待内核处理完毕后的相应。MLPutFunction函数向内核传递了一个调用函数名,MLPutInteger向内核传递了一个整型变量。有关这些函数的详细用法,请参考帮助文档。

第二, 外部程序同内核两者如何保持同步?也就是说,外部程序如何判断Kernel是否 理完毕,以作进一步的处理:例如,将结果打印出来。下面的代码段通过判断MLNextPacket的返回值是否为RETURNPKT来同步内核的处理过程。

while ((p= MLNextPacket(link)) && p != RETURNPKT)

          MLNewPacket(link);

下表列出了MLNextPacket可能的返回值及其意义:

Mathematica包类型                 返回的常量                          说明

ReturnPacket[expr]                   RETURNPKT                       计算的结果

ReturnTextPacket["string"]        RETURNTEXTPKT              文本形式结果

InputNamePacket["name"]          INPUTNAMEPKT                输入行名称

OutputNamePacket["name"]    OUTPUTNAMEPKT              输出行名称

TextPacket["string"]                TEXTPKT                            函数的文本形式输出

MessagePacket[symb,"tag","string"   MESSAGEPKT             Mathematica产生的消息

DisplayPacket["string"]              DISPLAYPKT                       图片后文的一部分

DisplayEndPacket["string"]        DISPLAYENDPKT               图片后文的结尾

InputPacket["prompt"]              INPUTPKT                         请求一个输入函数的相应

CallPacket[I,list]                       CALLPKT                            请求调用一个外部函数

表三 MLNextPacket的返回值及其意义

第三, 外部程序如何处理这些返回的数据?我们可以调用MLGetNext库函数依次得到kernel传递给外部程序的每一个对象。
根据MLGetNext的不同返回值,我们可以编写相应的C语言语句作出处理。

表列出了MLGetNext函数可能的返回值及其意义:

MLTKERR                 出错标志

MLRKINT                 整数变量

MLTKFUNC            复合函数

MLTKREAL             实数变量

MLTKSTR                 字符串

MLTKSYM                符号变量

表四 MLGetNext返回值及其意义

下面我们来看一段代码,着重说明MLGetNext函数的用法:

staticvoid read_and_print_expression( MLINK lp)

{

    kcharp_ct s;

    int n;

    long i, len;

    double r;

    static int indent;

    switch( MLGetNext( lp)) {

    case MLTKSYM:

        MLGetSymbol( lp, &s);

        printf( "%s ", s);

        MLDisownSymbol( lp, s);

        break;

    case MLTKSTR:

        MLGetString( lp, &s);

        printf( "/"%s/" ",s);

        MLDisownString( lp, s);

        break;

    case MLTKINT:

        MLGetInteger( lp, &n);

        printf( "%d ", n);

        break;

    case MLTKREAL:

        MLGetReal( lp, &r);

        printf( "%g ", r);

        break;

    case MLTKFUNC:

        indent += 3;

        printf( "/n %*.*s", indent,indent, "");

        if( MLGetArgCount( lp, &len) == 0){

            error( lp);

        }else{

            read_and_print_expression( lp);

            printf( "[");

            for( i = 1; i <= len; ++i){

                read_and_print_expression( lp);

                if( i != len) printf( ",");

            }

            printf( "]");

        }

        indent -= 3;

        break;

    case MLTKERROR:

    default:

        error( lp);

    }

}

上面的程序使用switch语句判断MLGetNext( lp)的返回值,以分别调用处理不同类型返回对象的代码。然后将返回的数据储存在预先定义的几个内部变量中,最后将结果打印出来。

2   MLGetSymbol得到返回的符号变量,MLDisownSymbol释放为其分配的内存;

2   MLGetString得到返回的字符串变量;MLDisownString释放为其分配的内存;

2   MLGetInteger得到返回的整型变量;

2   MLGetReal得到返回的实型变量;

2   对返回MLTKFUNC的处理相对复杂一些:输出程序先调用MLGetArgCount得到了该复合函数的参数个数,然后递归调用read_and_print_expression本身将每个参数输出。注意,indent是控制缩进格式的变量;

2   如果MLGetNext返回了MLTKERROR就调用错误处理函数error( lp)输出错误信息。 接下来,我们给出利用上面说明的三个步骤编写的外部程序的主程序。该程序计算一个代数方程式ax+b=0,其中x是方程的变元。

intmain(int argc, char* argv[])

{

    char* s="3+6";

    char* q="(x-1)^2==0";

   int pkt;

    double result;

init and openlink( argc, argv);

printf( "Computing... /n");

      MLPutFunction(lp,"Solve", 2);

         MLPutFunction(lp,"Equal",2);

           MLPutFunction(lp,"Plus", 2);

            MLPutFunction(lp,"Times",2);

                MLPutSymbol(lp,"a");

                MLPutSymbol(lp,"x");

             MLPutSymbol(lp,"b");

           MLPutInteger(lp,0);

     MLPutSymbol(lp,"x");

    MLEndPacket(lp);

    while( (pkt = MLNextPacket( lp), pkt)&& pkt != RETURNPKT) {

        MLNewPacket( lp);

        if (MLError( lp))

            error( lp);

    }

    read_and_print_expression(lp);

    MLPutFunction( lp, "Exit", 0);

    return 0;

}

程序的输出如下: List[

    List[

        Rule[x,

            Time[-1,

                Power[a,-1],b]]]]

我们可以将其转换成通常的形式:

要说明一点:外部程序同Mathematica之间的数据都是以表达式的形式传递的。例如,"5+6"应该转化为表达式Plus[5,6]才能为内核识别,同样内核向外部程序返回Mathematica表达式。用户的外部程序要能够处理这些表达式。上面的程序只是简单地传送和显示 计算结果,并不复杂。如果读者想要编写更加复杂的混合程序,就要熟练掌握Mathematica的表达式。有关Mathematica 表达式的相关问题,请参考前面的相关章节及Mathematica的帮助文档。到现在为止,我们介绍了在外部程序中同Mathematica进行交互的基本的问题,并简单分析了一些代码。下面,我们将介绍本节开始时提到的在外部程序中利用环回处理Mathematica表达式的方法。 在使用环回同前需要建立一个连接并打开这个环回,使用完毕后要及时关闭。假设我们 已经建立了一条连接:

MLINK lp= (MLINK)0;

lp =MLOpenArgv( ep, argv, argv + argc, &err);

下面的代码显示了利用环回处理表达式的框架:

ml =MLLoopbackOpen(stdenv, &errno);   //打开一个环回

//将表达式 Power[x, 3] 放入这个环回连接上

MLPutFunction(ml,"Power", 2);

    MLPutSymbol(ml, "x");

MLPutInteger(ml,3);

……

//从环回连接上得到一个表达式

MLGetFunction(ml,&head, &n);

    MLGetSymbol(ml, &sname);

MLGetInteger(ml,&k);

……

MLClose(ml);//关闭这个环回

上面的这段代码说明了如何打开一个环回,如何通过MLPut族函数和MLGet族函数操纵这 个环回连接上的表达式以及如何关闭一个环回的方法。那么,究竟如何利用已经建立起来的连接lpMathematica交换表达式呢?这要用到一个重要的库函数MLTransferExpression() 通过调用MLTransferExpression(),我们可以将在环回上建立的表达式传送给Mathematica,同时也可以接收来自Mathematica的表达式并将其存储在环回上以便在外部程序中 作进一步的处理。

假设我们要求一个指数函数e^2的值指数函数的值,我们可以如下处理:   

double result;

 ml = MLLoopbackOpen(ep, &errno);   //打开一个环回    

//将表达式e^2放入这个环回连接上    

MLPutFunction(ml,"N",1);

      MLPutFunction(ml, "Exp",1);

       MLPutInteger(ml, 2);

MLTransferExpression(lp,ml);      //Mathematica发送数据    

MLEndPacket(lp);

    while( (pkt = MLNextPacket( lp), pkt)&& pkt != RETURNPKT) {

        MLNewPacket( lp);

        if (MLError( lp))

            error( lp);

    }

MLTransferExpression(ml,lp);     //Mathematica接收数据    

//从环回连接上得到一个表达式    

MLGetReal(ml, &result);

printf("The result is: %f/n",result);

MLClose(ml); //关闭这个环回

这个程序建立了一个环回连接,在向环回连接上输入表达式后调用库函数MLTransferExpression将环回上的表达式传递给Mathematica,然后等待Mathematica将结果传回,又 一次调用库函数MLTransferExpression将结果传入环回连接。最后调用MLGetReal函数将结果(一个实型变量)保存在一个浮点型变量result中。在这一节里,我们重点介绍了在外部程序同Mathematica交换数据的一般方法并通过几个实例说明如何编写这类外部程序。要编写好C语言的混合程序,最重要的就是深刻理解、熟练掌握Mathematica的表达式。在此基础上灵活应用各类MathLink库函数,就能编写实用的混合计算程序。

x.3.3实例

我们在工程实践中往往会遇到需要求问题解析解的情况。例如,有时我们经过某种抽象得到一个以代数式表示的求解公式。对于这个求解公式,我们希望将其表示成为多项式的形式。如果用标准C语言编程将会比较困难,但是这在Mathematica中是容易做到的。下面的例子演示了如何通过外部程序调用Mathematica的计算模块以实现这个功能。

/* math1.c

在命令行中运行这个程序:math1 -linkmode -launch

*/

#include <stdio.h>

#include <stdlib.h>

#include "mathlink.h"

#if MACINTOSH_MATHLINK

extern int mlmactty_init( char*** argvp);

#endif

static void init_and_openlink( int argc,char* argv[]);

static void error( MLINK lp);

MLENV ep = (MLENV)0;

MLINK lp = (MLINK)0;

MLINK ml = (MLINK)0;

FILE* fp;                 //将计算的结果输出到文件中

static void read_and_print_expression_tofile(MLINK lp)

{

   kcharp_ct  s;

    intn;

   long i, len;

   double r;

   static int indent;

   switch( MLGetNext( lp)) {

   case MLTKSYM:

       MLGetSymbol( lp, &s);

       fprintf(fp, "%s ", s);

       MLDisownSymbol( lp, s);

       break;

   case MLTKSTR:

       MLGetString( lp, &s);

       fprintf(fp, "/"%s/" ", s);

       MLDisownString( lp, s);

       break;

   case MLTKINT:

       MLGetInteger( lp, &n);

       fprintf(fp, "%d ", n);

       break;

   case MLTKREAL:

       MLGetReal( lp, &r);

       fprintf(fp, "%g ", r);

       break;

    case MLTKFUNC:

        indent += 3;

        fprintf(fp, "/n %*.*s",indent, indent, "");

        if( MLGetArgCount( lp, &len) == 0){

            error( lp);

        }else{

            read_and_print_expression_tofile(lp);

            fprintf(fp, "[");

           for( i = 1; i <= len; ++i){

               read_and_print_expression_tofile( lp);

                if( i != len) fprintf(fp,", ");

           }

           fprintf(fp, "]");

       }

       indent -= 3;

       break;

   case MLTKERROR:

   default:

       error( lp);

    }

}

intmain(int argc, char* argv[])

{

    char* s="3+6";

    char* q="(x-1)^2==0";

    int pkt;

    double result;

    if((fp=fopen("e://math.txt","w"))==NULL)

    {

        printf("Open filesError!/n");

        exit(1);

    }

    init_and_openlink( argc, argv);

    printf( "Computing... /n");

//在这里将代数式转换成为MathLink表达式的形式    

MLPutFunction(lp,"Collect", 2);

      MLPutFunction(lp,"Expand", 1);

         MLPutFunction(lp,"Power",2);

             MLPutFunction(lp,"Plus",4);

                MLPutInteger(lp,1);

                MLPutSymbol(lp,"x");

               MLPutFunction(lp,"Times", 2);

                   MLPutInteger(lp,2);

                  MLPutSymbol(lp,"y");

               MLPutFunction(lp,"Times", 2);

                   MLPutInteger(lp,3);

                  MLPutSymbol(lp,"z");

         MLPutInteger(lp,3);

      MLPutFunction(lp,"List", 2);

                MLPutSymbol(lp,"x");

                MLPutSymbol(lp,"y");

    MLEndPacket(lp);

    while( (pkt = MLNextPacket( lp), pkt)&& pkt != RETURNPKT) {

        MLNewPacket( lp);

        if (MLError( lp))

            error( lp);

    }

    read_and_print_expression_tofile(lp);

    MLPutFunction( lp, "Exit", 0);

    fclose(fp);

    return 0;

}

staticvoid error( MLINK lp)

{

    if( MLError( lp)){

        fprintf( stderr, "Error detectedby MathLink: %s./n",

            MLErrorMessage(lp));

    }else{

        fprintf( stderr, "Error detectedby this program./n");

    }

    exit(3);

}

staticvoid deinit( void)

{

    if( ep) MLDeinitialize( ep);

}

staticvoid closelink( void)

{

    if( lp) MLClose( lp);

}

staticvoid init_and_openlink( int argc, char* argv[])

{

    long err;

#if MACINTOSH_MATHLINK

    MLYieldFunctionObject yielder;

    argc = mlmactty_init( &argv);

#endif

    ep = MLInitialize( (MLParametersPointer)0);

    if( ep == (MLENV)0) exit(1);

    atexit( deinit);

#ifMACINTOSH_MATHLINK

    yielder = MLCreateYieldFunction( ep,NewMLYielderProc(MLDefaultYielder), 0);

#endif

    lp = MLOpenArgv( ep, argv, argv + argc,&err);

    if(lp == (MLINK)0) exit(2);

    atexit( closelink);

#ifMACINTOSH_MATHLINK

    MLSetYieldFunction( lp, yielder);

#endif

}

程序的输出如下:          

          Plus [3 ,

             Times [6 , y ],

             Times [9 , z ]]],

       Times [

          Power [y , 2 ],

          Plus [12 ,

             Times [36 , z ]]],

       Times [y ,

          Plus [6 ,

             Times [36 , z ],

             Times [54 ,

                Power [z , 2 ]]]],

       Times [x ,

          Plus [3 ,

             Times [12 ,

                Power [y , 2 ]],

             Times [18 , z ],

             Times [27 ,

                Power [z , 2 ]],

             Times [y ,

                Plus [12 ,

                   Times [36 , z ]]]]]]

在上面的程序中,我们需要展开的代数式是已知的。在一些情况下,根据工程应用的具体情况,我们需要动态地生成这些代数式。这时,可以对上面的程序作出某些改动,"动态"地调用MLPut族函数生成相应的Mahtematica表达式。

下面的例子是一个工程中经常遇到得线性规划问题。

目标函数是w=x+5y-8z,我们求其在约束条件:x+7y>102x-8z<=19x+y+z<10条件下的最小值。为了节省篇幅,我们仅给出表达式转换的代码以及程序的输出。

MLPutFunction(lp,"N",1);

  MLPutFunction(lp,"ConstrainedMin",3);

      MLPutFunction(lp,"Plus",3);                //目标函数w=x+5y-8z

        MLPutSymbol(lp,"x");

        MLPutFunction(lp,"Times", 2);

          MLPutInteger(lp,5);

          MLPutSymbol(lp,"y");

        MLPutFunction(lp,"Times", 2);

          MLPutInteger(lp,-8);

          MLPutSymbol(lp,"z");

    MLPutFunction(lp,"List", 3);

      MLPutFunction(lp,"Greater",2);              // 条件x+7y>10

        MLPutFunction(lp,"Plus", 2);

          MLPutSymbol(lp,"x");

          MLPutFunction(lp,"Times",2);

            MLPutInteger(lp,7);

            MLPutSymbol(lp,"y");

        MLPutInteger(lp,10);

      MLPutFunction(lp,"LessEqual",2);              // 条件2x-8z<=19

        MLPutFunction(lp,"Minus", 2);

          MLPutFunction(lp,"Times",2);

            MLPutInteger(lp,2);

            MLPutSymbol(lp,"x");

          MLPutFunction(lp,"Times",2);

            MLPutInteger(lp,8);

            MLPutSymbol(lp,"z");

        MLPutInteger(lp,19);

      MLPutFunction(lp,"Less",2);              // 条件x+y+z<10

        MLPutFunction(lp,"Plus", 3);

          MLPutSymbol(lp,"x");

          MLPutSymbol(lp,"y");

          MLPutSymbol(lp,"z");

      MLPutInteger(lp,10);

    MLPutFunction(lp,"List", 3);

      MLPutSymbol(lp,"x");

      MLPutSymbol(lp,"y");

      MLPutSymbol(lp,"z");

程序的输出结果为:    

List [-61.4286 ,

       List [

          Rule [x , 0 ],

          Rule [y , 1.42857 ],

          Rule [z , 8.57143 ]]]

即:在x->0 , y-> 1.42847 ,z->8.57143时,在上述条件x+7y>102x-8z<=19x+y+z<10的约束下,表达式x+5y-8z趋于最小值-61.4286

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....


MathLink混合编程(四)

x.4高级主题

在本章的最后一节里,我们打算讨论几个同MathLink相关的高级主题。有些内容可能超出了混合编程的范围,然而我们希望通过这对几个主题的讨论加深大家对MathLink结构 的认识。

x.4.1 Session间的通讯

Mathematica中,几个Session之间进行通讯是可能的。这可能出于以下两种考虑:

2   在两个Session之间简单地交换数据,取代利用中间文件交换数据的方法;

2   将一个计算任务分配到不同的Session上;另外利用操作系统提供的网络通讯功能,我们将能在几台主机上同时进行一个计算任务 不同部分的运算。这也可以借助Session间的通讯机制来实现。其实,Mathematica的这 Session间的通讯机制同样是由MathLink来实现的,每一个Session都相当于一个MathLink外部程序。

下表列出了常用的进行Session间通讯的Mathematica函数:

Mathematica函数                说明

LinkCreate["name"]             创建一个MathLink链接

LinkConnect["name"]           连接由其他程序创建的MathLink链接

LinkClose[link] 关闭            MathLink连接

LinkWrite[link,expr]           MathLink连接写入表达式

LinkRead[link]                    MathLink连接中读出表达式

LinkRead[link,Hold]             读表达时,并立即用Hold绑定它

LinkReadyQ[link]              查找是否有准备要从链接读出的数据

表五 Session间通讯的Mathematica函数

下面我们看几个例子:

2   利用端口8000建立一条连接

Session A

In[1]:=link=LinkCreate["8000"]

Out[1]=LinkObject[8000,4,4]

Session B

In[1]:=link=LinkConnect["8000"]

Out[1]=LinkObject[8000,4,4]

2   发送/接收数据

Session A

In[1]:=LinkWrite[link, 15!]

Session B

In[1]:=LinkRead[link]

Out[1]=1307674368000

2   发送/接收表达式

Session A

In[1]:=LinkWrite[link, Unevaluated[2 + 2]]

Session B

In[1]:=LinkRead[link, Hold]

Out[1]=Hold[2+2]

2   利用未分配的端口建立一条连接

SessionHostA

In[1]:=link=LinkCreate[]

SessionHostB

In[1]:=LinkRead[link]

2   关闭连接

SessionHostA

In[1]:=LinkClose[link]

MathLink利用操作系统提供的网络通讯服务,并不依赖具体的操作系统和通讯协议,因此可以在两种使用不同操作系统的主机间建立连接。

x.4.2同前端(front ends)的通讯

Mathematica内核用MathLinkMathematica前端通信。如果从一个前端启动了一个Mathematica内核,则可以通过到该前端MathLink连接来控制该内核。 全局变量$ParentLink指定了特定核心用来输入输出的MathLink连接。在一个Mathematica会话(Session)中间重置$ParentLink有时会很有用,可以有效地改变核心要链接的 前端。 就象Mathematica核心,标准的Mathematica NoteBook前端操作指定的MathLink包。通常如果要从核心控制Mathematica前端,那最好使用象NotebookWriteFrontEndExecute这样的进程。但在某些时候,直接用LinkWrite给前端发送包还方便些。

x.4.3通过网络运行远程机上的MathLink 程序

MathLink允许调用Mathematica的外部程序,即使这个程序在远程机上运行。在一般情况 下,可以从远程机的操作系统中直接启动程序,但可以在自己的Mathematica会话中用命 令来连接它。

我们用上面创建的外部程序addtwo为例子说明建立连接的过程。我们在局域网的主机Tiger上运行这个外部程序:

addtwo-linkcreate 8000

这将建立一条8000端口的连接。

然后在另外一台主机Circle上运行Mathematica并打开一个会话,输入: Install[LinkConnect["8000@Tiger"]]

这将加载这条连接对应的外部程序。接下来就可以利用前面提到的方法调用外部程序中的函数了。需要注意的是,使用完毕要卸载这个外部程序,并中断连接。另外,用mprep创建的外部程序通常包含设置MathLink连接的代码。如果直接从操作系统中启动这样的程序,那么他们就要提示您指定您想要的那种类型的连接。 但如果您的操 作系统支持,您还可以把这信息作为命令行给外部程序。

x.4.4MathLink的错误及中断处理

自己编写的MathLink外部程序在进行数据交换的过程中可能会出现各种各样的错误。当发生错误以后,MathLink将会转换到非活动的状态。这时,你调用的所有MathLink库函数将返回0 我们通常需要在调用完一系列复杂的函数后处理这一过程中可能出现的错误。如果发现 已经产生了一个错误,就必须调用函数MLClearError()以重新激活MathLink。例如我们在前面的程序中介绍过,在向内核发送数据后需要调用MLNextPacket检查是否 返回了计算结果。下面的代码中调用了MLError函数以捕捉可能出现的错误,并调用错误处理函数error进行处理。    

while( (pkt = MLNextPacket( lp), pkt)&& pkt != RETURNPKT) {

        MLNewPacket( lp);

        if (MLError( lp))

            error( lp);

    }

下面列出三个针对错误处理的函数:

函数                                               说明

Long MLError(MLINK link)                返回当前错误的代码,如果没有出错就返回0

Char *MLErrorMessage(MLINK link)   返回描述当前错误的字符串

intMLClearError(MLINK link)                   清除当前的错误,返回值表示是否能将Mathlink转换到活动状态

表六 MathLink错误处理函数

发生错误以后,通常需要将这条连接上正在处理的剩余的包或者表达式丢弃。可以调用 MLNewPacket()达到目的,正如上面的代码中所示的那样:在尚未接收到内核返回的数据 包之前用MLNewPacket(lp)将刚才接收到的包丢弃。

最后介绍一下MathLink的中断处理:

如果在Mathematica执行外部函数的过程中试图中断它的运行,Mathemaica将会将外部程 序中的全局变量MLAbort置为1MathLink无法自动从一个外部函数调用中返回。所以, 如果你的外部函需要进行较长时间的运算或处理,最好在程序中的适当位置添加检验MLAbort全局变量的代码以识别Mathematica的中断请求并适时返回。

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....


应用软件Mathematica(1)

---------------------------------------------------------------------

注:为了对Mathematica有一定了解的同学系统掌握Mathematica的强大 功能,我们把它的一些资料性的东西整理了一下,希望能对大家有所帮助。 ---------------------------------------------------------------------

一、运算符及特殊符号        

Line1;                          执行Line,不显示结果        

Line1,line2                       顺次执行Line12,并显示结果        

?name                         关于系统变量name的信息        

??name                         关于系统变量name的全部信息        

!command                     执行Dos命令        

n!                             N的阶乘        

!!filename                       显示文件内容        

<<filename                      读入文件并执行        

Expr>> filename                  打开文件写        

Expr>>>filename                 打开文件从文件末写

()                             结合率        

[]                             函数        

{}                              一个表        

<*Math Fun*>                  c语言中使用math的函数        

(*Note*)                       程序的注释        

#n                            n个参数        

##                            所有参数        

rule&                          rule作用于后面的式子       

%                            前一次的输出        

%%                          倒数第二次的输出        

%n                           n个输出        

var::note                         变量var的注释        

"Astring "                        字符串        

Context  `                      上下文        

a+b                                   

a-b                                    

a*ba b                              

a/b                                    

a^b                            乘方        

base^^num                     base为进位的数        

a-b                                    

a*ba b                              

a/b                                    

a^b                            乘方        

base^^num                     base为进位的数        

lhs&&rhs                              

lhs||rhs                                  

!lha                                    

++,--                          自加1,自减1

+=,-=,*=,/=                     C语言        

>,<,>=,<=,==,!=                 逻辑判断(同c        

lhs=rhs                         立即赋值        

lhs:=rhs                         建立动态赋值        

lhs:>rhs                         建立替换规则        

lhs->rhs                        建立替换规则        

expr//funname                   相当于filename[expr]

expr/.rule                        将规则rule应用于expr

expr//.rule                        将规则rule不断应用于expr知道不变为止        

param_                        名为param的一个任意表达式(形式变量)        

param__                       名为param的任意多个任意表达式(形式变量)

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....


附注: () 的原始版本... 格式比较混乱  : MathLink 混合编程(二)

发信站: 同舟共济站 (2002 04 11 16:53:35 星期四 ), 站内信件

x.2 Mathematica 中调用外部程序

我们首先来看一下如何在 Mathematica 中装载外部的 MathLink 程序,如何利用程序中的函数 、如何卸载程序;然后通过一个实例重点介绍如何利用 Visual C++ 6.0 创建并生成一个MathLink 程序;最后简单介绍如何处理各种类型的表达式。

x.2.1 调用的步骤及相应的命令

MathLink 最通常的用法是在 Mathematica 中调用外部 MathLin k程序。当外部程序建立完成后,我们就可以装载这个程序,并且在当前的 Mathematica 进程中调用程序中的函数。

装载及卸载外部 MathLink 程序的语句是:

? Install["prog"] 装载一个外部 MathLink 程序

? Uninstall[link] 卸载外部程序

例一:我们举一个例子,这个例子中使用的例子可以在 MathLink 开发工具文件夹的"MathLinkExamples" 中找到源代码,已经编译好的程序 "addtwo.exe" "PrebuiltExamples" 中可以找到。

外部函数的源代码如下:

in addtwo(int i, int j){ return(i+j); // 返回两数之和 }

下面,我们就在 Mathematica 中调用这个函数实现两数相加的运算:

运行Mathematica ,输入下面的命令: In[1] := link =Install[LinkLaunch[]] 确认后会出

一个文件打开对话框选择要加载联接的外部程序,选择编译好的程序 "addtwo.exe" 。这样,外部程序就被加载了,返回的信息是:

Out[1] = LinkObject[C:/MathLink/addtoe.exe,2,2]

其中,"C:/MathLink/addtoe.exe" 是选择的外部程序。然后,就可以使用外部程序中的函数了。

2 显示该外部函数 AddTwo 的用法:

2 调用该函数:

2 如果传递的参数不符合原型函数的要求,将不会得到返回值:

2 卸载该外部程序:

常用的处理外部函数连结的命令主要有:

命令格式 用法说明

Install["prog"] 装载一个外部程序

Unstall[link] 卸载一个外部程序

Links["prog"] 显示同外部程序 "prog" 相关联的活动连结

Links[] 显示所有的活动连结

LinksPatterns[link] 显示特定连结上的求值模式注:

其他处理MathLink Mathematica 命令请参考帮助文档。

x.2.2 创建 MathLink 外部程序的一般步骤

如果已经在外部文件中定义并且实现了函数原型,那您将要作的就是在源代码中加入一些

MathLink 相关的代码,从而实现从 Mathematica 中调用这个函数并得到函数的返回值。简单

情况下,可以使用建立 MathLink 临时文件,通过代码自动生成程序自动生成调用 MathLink

关函数的C 语言源代码。一般地说,建立一个 MathLink 外部程序主要要完成三步工作:

? 建立 MathLink 临时文件;

? 处理临时文件生成的源代码;

? 提取源代码,加入函数实现的主代码并编译生成外部程序。

MathLink 临时文件由一系列指令组成,可以看作是一个代码生成模版。其中的指令规定了外

程序如何在Mathematica 中被调用。临时文件建立了在 Mathematica 中定义函数同外部程序相

应函数的联系关系。临时文件用生成的源代码必须包含在外部文件中,以使外部程序具有MathLink 兼容性。

下面是MathLink 模板中包含的元素:

指令 说明:

Begin 标志对应特定函数的模板的起始:

Function 外部程序中的函数名:

Pattern 调用此函数方式的定义:

Arguments 函数的参数:

ArgumentTypes 函数参数值类型:

ReturnType 返回值类型:

End 标志对应特定函数的模板的结束:

Evaluate 在函数加载时 Mathematica 的输入计算

注:函数参数值类型及函数返回值类型类表请参看下表。

例一:接着我们给出一个 MathLink 模板的例子:

Begin :: Function

addtwo Pattern

AddTwo[x_Integer , y_Integer] Arguments

{x , y} ArgumentTypes

{Integer , Integer} ReturnType

Integer End :Evaluate: AddTwo::usage = "AddTwo[x,y] gives the sum of two

machine integers x and y."

这个例子定义了例一中 AddTwo 函数的 MathLink 模板,并保存在文件 addtwo.tm 中。编写好模板后将其保存在模板文件( *.tm )中,利用代码生成程序生成源代码。不同平台不同操作系统上对应不同版本的代码生成程序,一般是 mprep 处理程序。 Windows 系统下的用户在 MathLink的安装目录中可以找到 "MPREP16.EXE" 或者 "MPREP32.EXE" 程序(分别对应 16 位和32 位处理程序),利用这两个工具可以生成 Windows 平台下 C/C++ 编译器可以识别编译的源代码。利用命令: mprep ad dtwo.tm -o addtwom.c 将会在生成文件 addtwom.c 中包含生成的源代码。需要 注意的是,如果文件 mprep.exe addtwo.tm 不在当前目录下则要在命令中指定其路径。接下来,我们将编写外部程序主源码,并将生成的代码加入到外部程序源文件中,最后编译生成一个 MathLink 兼容的外部程序。

2 在外部程序源文件中包含 MathLink 的标准头文件 #include "mathlink.h"2 加入实际函

数代码in addtwo(int i, int j){ return(i+j); // 返回两数之和 }2 编写主函数以使

部程序接收来自 Mathematica 的调用请求 int main(int argc, char* argv[]){return

MLMain(argc, argv);}2 最后利用C/C++ 编译器编译这个外部程序,生成最终可执行程序。

下面一节将专门讨论 利用 Visual C++ 编译器编译外部程序的步骤。

x.2.3 使用 Visual C++ 6.0 创建 MathLink 程序

我们将利用VC6.0 实现一个 MathLink 程序 ,通

过这个程序说明创建外部程序的详细步骤。我们的程序中包含一个根据经验公式编写的查表计

算程序。我们简化一下这个查表函数,去掉同具体算法相关的细节,把重点放在实现同

MathLink 的接口实现上。该函数 myfun 根据调用时传递的两个参数值 int line int row

一个数据表查找对应的数据项,并将查到的数据返回。

下面,我们来看怎样分步实现这个程序:

1. 启动 Microsoft Developer Studio ,进入 Visual C++ 的集成开发环境;

2. File 菜单中选择 New 将会打开一个新建对话框;

3. New 对话框中单击 Projects 标签;

4. Location 文本框中选择工程将被创建的路径;

5. Project Name 文本框中键入工程名 MYFUN ;图二 VC 中新建一个工程 MYFUN

6. 在左边的 Project 类型列表中选择 Win32 Application

7. 在右下的 Platforms 列表框中确保 Win32 被选中了;完成后如图二所示

8. 单击 OK 确认,将出现 Win32 Application 对话框;

9. 选择 empty project 并单击 Finish 完成,将出现 New Project Information 对话框;

10. 单击 OK 确认;接下来,我们在工程中添加我们的代码文件。我们将需要编辑两个文件,

分别是myfun.c myfun.tm ,对应主程序源文件和 MathLink 的模板文件。

11. Project 菜单的 Add To Project 子菜单中选择 New ,将出现新建对话框,选中 Files

标签;

12. 在文件名中键入以下文件名,以空格隔开: "myfun.c""myfun.tm" "myfuntm.c"

13. Project 菜单的 Add To Project 子菜单中选择 Files ,将出现插入文件对话框。定位

插入文件"ml32i2m.lib" 。注意,这个文件是在安装 MathLink 时拷贝到硬盘上的,我们前面

在介绍安装MathLink 时提到过。我的 Mathematica 安装在 D:/Program Files/Wolfram

Research ,我在 MathLink 的安装目录中找到了这个文件:

D:/ProgramFiles/WolframResearch/Mathematica/4.0/AddOns/MathLink/DevelopersKits/Windows/CompilerAdditions/mldev32/lib您可能觉得这个文件隐藏地太深了。没关系,您以将MathLink 拷贝到一个适当地位置,如 Microsoft Developer Studio 安装目录下。下面,我们

编写主程序以及模板文件:

14. Workspace 中打开文件 myfun.tm ,加入下面的代码:

int myfun P(( int, int));

:Begin::Function:

myfun:Pattern:

MyFun[line_Integer, row_Integer]

:Arguments: { line, row }

:ArgumentTypes: { Integer, Integer }

:ReturnType: Real

:End::

Evaluate: MyFun:

:usage = "MyFun[line, row]

retrun float value in the line/row position."

Workspace 中打开文件 myfun.c ,加入下面的代码:

#include "mathlink.h"

extern double myfun( int line, int row);

double myfun( int line, int row)

{ double ret=0.8979*line-row; /* 这里省略了具体处理的代码 */

return ret;}

#if MACINTOSH_MATHLINK

int main( int argc, char* argv[])

{ /* Due to a bug in some standard C libraries that have shipped with *MPW ,

zero is passed to MLMain below. (If you build this program * as an MPWtool,

you can change the zero to argc.) */

argc = argc; /* suppress warning */

return MLMain( 0, argv);

}

#elif WINDOWS_MATHLINK#if __BORLANDC__#pragma argsused#endifint PASCAL

WinMain( HINSTANCE hinstCurrent, HINSTANCE hinstPrevious, LPSTRlpszCmdLine, intnCmdShow)

{

char buff[512];

char FAR * buff_start = buff;

char FAR *argv[32];

char FAR * F AR *argv_end = argv + 32;

hinstPrevious =hinstPrevious; /*suppress warning */

if( !MLInitializeIcon( hinstCurrent,nCmdShow))return 1;

MLScanString( argv, &argv_end,&lpszCmdLine, &buff_start);

return MLMain( argv_end - argv,argv);

}

#else

int main(argc, argv) int argc;

char* argv[];{

return MLMain(argc, argv);

}

#endif

下面设置编译选项以及编译前的代码生成:

Project 菜单中选择 Settings ,将出现 Project Settings 对话框;

在左边的设置列表中展开工程 MYFUN ,并选择文件 myfun.tm

单击Custom Build 标签显示 Custom Build 页;

Description 文本框中输入编译的描述,如: "mpreping ..."

Build Commands 列表框的第一行中输入: mprep myfun.tm -o myfuntm.C

21. Output Files 列表框的第一行中输入: ./myfuntm.c 图三 设置编译选项

22. 单击 Ok 确认在编译之前,我们还要将相应版本的 mathlink.h 拷贝到编译器默认的包含文

件目录下或者该工程文件夹中。选择 Tools 菜单下的 Options 子菜单,在出现的对话框中的

Directories 标签栏内可以查看和设置编译器的缺省目录。另外,还要将相应的 mprep.exe

件拷贝到工程文件夹中。下面可以开始编译了:

23. 选择 Build 菜单中选择 Buile MYFUN.EXE 或者按快捷建 F7 ,将会编译生成一个 MathLink兼容程序 MYFUN.EXE 。最后,简单介绍一下编译的过程:

2 系统首先执行用户定制的步骤,即在图三所示界面上的设置,通过这一步系统调用 mprep 程序自动生成了 MathLink 相关源代码,存放在文件myfuntm.c 中。我们可以打开该文件看看到底生成了些什么代码。有兴趣的读者可以参考Mathematica 的帮助文档理解这些代码。

2 接着是常规地编译,按照顺序分别编译了myfun.c myfuntm.c 两个文件。

2 最后是连接并生成目标代码。可以看到,除了编译前的 "执行用户定制的步骤 " ,并没有特殊的地方。我们完全可以通过手工 调用命令 mprep myfun.tm -o myfuntm.c 而取代 " 执行用户定制的步骤 " 。但是这样每次对模板文件 myfun.tm 改动后都要输入该命令重新生成一次,比较麻烦。所以我们建议在编译器参数设置中加入用户定制的步骤以自动生成 myfuntm.c 文件。

x.2.4 处理各种交换数据类型

在上面的例子中,我们的从 Mathematica 传递给外部含数的数据

是整型变量,函数返回的是符点实型变量。事实上,利用 MathLink 可以同外部程序交换任何类型的数据。可以在模板文件中的: ArgumentTypes :和:ReturnType :中指定函数参数以及返回值的数据类型。下表给出了在模板文件中定以的基本数据类型以及 C 语言中相对应的类型:Mathematica 中的定义 说明C 语言中的定义 Integer 整型变量 intReal 浮点型变量doubleIntegerList 整型变量列表 int *, longRealList 浮点型变量列表double *, longString 字符串变量 char *Symbol 符号名变量chat *Manual 直接调用MathLink 程序 void 基本数据类型对照表类似我们上面介绍的例子,您可以在模板中的参数类型段使用上面任何一种变量类型 。需要注意的是,如果您使用 IntegerList 或者 RealList作为变量类型,则必须在外部的 C 函数中包含附加的参数来指定列表的长度。

例如,在模板中您这样定义参数类型:

ArgumentTypes {IntergerList, RealList,Interger}

则对应的C 函数定义可能是:

voidf(int *a, long alen, double *b, long blen, int c);

下面将介绍在 MathLink C 语言程 序间更加灵活地传递数据的方法。

先来看一个例子:模板文件:

:Begin::Function:

otherfun:Pattern: OtherFun[line_Integer, row_Integer]:Argume nts:

{ line, row }:ArgumentTypes: { Integer, Integer }:ReturnType: Manual:End:C

言函数:Void otherfun(int ling, int row){ int a[32], k;

for(k=0;k<32;k++){ ……// 根据具体的算法给数组 a 赋值} k++;

MLPutIntegerList(stdlink, a, k); return;} 我们看到,在模板中定义的函数返回值类型

Manual ,即在外部函数中直接调用 MathLink 的函数向 Mathematica 返回数据。这调用的函数 MLPutIntegerList ,下面列出其他几个常用的类似的函数,他们在 "mathlink.h" 中都有定义。从函数的名字中可以猜测其意义,这里不再详细介绍了。

MLPutInteger(MLINK link, int i)

MLPutReal(MLINK link, double x)

int MLPutIntegerList(MLINK link, int *a, long n)

int MLPu tRealList(MLINK link, double *a, long n)

int MLPutIntegerArray(MLINK

link, int *a, long *dims, char **heads, long d) int MLPutRealArray(MLINK link,double *a, long*dims, char **heads, long d)

int MLPutFunction(MLINK link, char*s, long n)

int MLPutString (MLINK link, char *s)

int MLPutSymbol(MLINK link,char *s)

上面介绍的是从 C 语言中通过调用 MathLink 函数返回数据给 Mathematica ,如果 要从外部函数中直接通过调用 MathLink 函数得到传递过来的数据,则要用到另外一组函数: int MLGetInteger(MLINK link, int *i)

int MLGetReal(MLINK link, double *x)

int MLGetIntegerList(MLINK link, i nt **a, long *n)

int MLGetRealList(MLINK link,double **a, long *n)

int MLGetIntegerArray(MLINK link, int **a, long **dims, char ***heads,long*d)

int MLGetRealArray(MLINK link, double **a, long **dims, char***heads,long*d)

下面看一个具体的例子:模板文件:

:Begin::Function:

getfun:Pattern: GetFun[a,b,c]:Arguments: { a,b,c}:ArgumentTypes:

{ Integer, Manual }:ReturnType: Real:End:C 语言函数: Void getfun(int

a){ double b,c; MLGeReal(stdlink, &b);MLGeReal (stdlink,&c);return a+b*c; }

在这个函数中,我们利用函数参数传递整型变量 int a ,通过MathLink 函数 MLGetReal 得到 另外两个浮点型变量 b c 。通过调用这些 MathLink 外部函数,可以很灵活在 Mathematica 及外部 C 语言函数间交换数据。事实上,利用 mprep 程序生成的也正是一些调用 MathLink 库的 外部函数的代码,系统利用这些自动生成的代码,建立了 Mathmatica 同外部程序之间的连接。我们可以通过阅读这些代码深刻理解 MathLink 是怎样工作的。如 果读者想了解有关 MathLink外部函数更详细的资料,可以参考 Mathematica 的帮助文档。

--

蓝色,忧郁的颜色

蓝色,我最喜欢的颜色

蓝色,澄静,纯洁的感觉

这个世界有了蓝色,更加精彩.....

 

原创粉丝点击