此为 Mathematica 4 文档,内容基于更早版本的 Wolfram 语言
查看最新文档(版本11.1)

2.12.4 处理列表、数组和其它表达式

MathLink 可以与外部程序交换任何类型的数据. 对更常用的数据类型, 需要在 MathLink 模板文件中给出适当的:ArgumentTypes::ReturnType: 规格.

基本类型规定

这里是一个函数的 MathLink 模板,该函数的变量是一个整数列表

:Begin:
:Function: h
:Pattern: h[a_List]
:Arguments:  a
:ArgumentTypes:  IntegerList
:ReturnType: Integer
:End:

这里是该函数的 C 源代码,注意额外的变量 alen 用来传递列表的长度

int h(int *a, long alen)

int i, tot=0;

for(i=0; i<alen; i++)
tot += a[i];

return tot;

安装一个包含有函数 h 规定的外部程序

这里调用外部代码

这个与模式 h[a_List] 不匹配, 所以没有调用外部程序

这里的模式是匹配的,但列表中的元素与外部代码中的类型不同,所以返回到 $Failed

变量的这几种基本类型可以混合使用. 但使用 IntegerListRealList 时,在 C 程序中必须包含一个额外的变量去代表该 列表的长度.

这是一个 :ArgumentTypes: 指定

:ArgumentTypes:  IntegerList, RealList, Integer

这里是可能对应的 C 函数陈述

void f(int *a, long alen, double *b, long blen, int c)

MathLink 把一个列表传递给 C 程序时,它的第一个元素在 C 中标准位置是零, 而不是像在 Mathematica 中的那样标准位置是 1.
另外,按照 C 标准,用 String 规定的字符串传递为 char * 对象,用 \0 空字节结束. 在 2.12.5 节中将讨论怎样处理特殊字符.

向 Mathematica 送数据的 MathList 函数

在使用 MathLink 模板文件时,mpregmcc 实际上是产生一个包含明确调用 MathLink 库函数的 C 程序. 如果要理解 MathLink 的工作过程,就可以查看这 个程序的源代码. 注意,当使用 mcc 时一般需要给出一个 -g 选项,否则,所产 生的源代码将会被自动删除.
当外部程序返回单个整数或浮点数时,可在以 MathLink 模板文件中对 :ReturnType: 指定 IntegerReal. 但由于 C 中的内存分配和撤消方式,不能直接对 :ReturnType: 给出 IntegerListRealList 等指定. 要返回这些结构时,必须在 C 程序中直接调用 MathLink 库函数,并对 :ReturnType: 指定 Manual.

这是一个函数的 MathLink 模板,该函数的变量是一个整数,直接用 MathLink 函数返回它的值

:Begin:
:Function: bits
:Pattern: bits[i_Integer]
:Arguments:  i
:ArgumentTypes:  Integer
:ReturnType: Manual
:End:

这个函数被说明是一个空函数

void bits(int i) {

int a[32], k;

这里给 C 数组 a 赋值

for(k=0; k<32; k++)
a[k] = i%2;
i >GreaterEqual 1;
if (iEqual0) break;


if (k<32) k++;

这里将数组 a 的 k 个元素送回到 Mathematica

MLPutIntegerList(stdlink, a, k);
return ;
}

安装包含外部函数 bits 的程序

这个外部函数返回一个位列表

C 中用 int a[n1][n2][n3] 说明一个数组时,可以用 MLPutIntegerArray( ) 将它作为一个深度为 3 的列表送入 Mathematica.

...

说明三维的 C 数组

int a[8][16][100];

这里建立一个数组 dims,其初始值定义为 a 的维数

long dims[] =  8, 16, 100 ;

...

将这个三维数组送入 Mathematica,产生一个深度为 3 的列表

MLPutIntegerArray(stdlink, a, dims, NULL, 3);

...

MathLink 函数完全能产生任何 Mathematica 表达式. 其基本思想是调用一系列 直接与 Mathematica 表达式的 FullForm 表示对应的 MathLink 函数.

建立有两个变量的 Mathematica 函数 Plus

MLPutFunction(stdlink, "Plus", 2);

指定第一个变量是整数 77

MLPutInteger(stdlink, 77);

指定第二个变量是符号 x

MLPutSymbol(stdlink, "x");

MLPutFunction() 一般地,先调用 MLPutFunction() 给出拟产生的 Mathematica 函数的头和所含变量的个数. 随后调用其它 MathLink 函数轮流 填入每个变量. 2.1 节讨论了 Mathematica 表达式的一般结构和头的概念.

产生有两个元素的 Mathematica 列表

MLPutFunction(stdlink, "List", 2);

该列表的第一个元素是从 C 数组 r 中得到的 10 个整数的列表

MLPutIntegerList(stdlink, r, 10);

该列表中的第 2 个元素本身是 2 个元素的列表

MLPutFunction(stdlink, "List", 2);

这个子列表的第一个元素是一个浮点数

MLPutReal(stdlink, 4.5);

第二个元素是一个整数

MLPutInteger(stdlink, 11);

MLPutIntegerArray()MLPutRealArray() 可以输送由 C 预分配在内存中按一维 形式存放的数组. 在执行 C 程序过程中要产生数组时,通常将它们设置为 指针的嵌套集合. 可以将这种数组送入 Mathematica,其方法是一系列 MLPutFunction() 调用和一个结束调用 MLPutIntegerList().

...

这里说明 a 是一个整数数组的列表嵌套

int ***a;

...

这里产生一个 n1 个元素的 Mathematica 列表

MLPutFunction(stdlink, "List", n1);

for (i=0; i<n1; i++) {

这里产生一个有 n2 个元素的子列表

MLPutFunction(stdlink, "List", n2);

for (j=0; j<n2; j++) {

这里写出整数列表

MLPutIntegerList(stdlink, a[i][j], n3);

}
}

...

重要的一点是要知道用 MathLink 函数产生的表达式当送入 Mathematica 后就立即 计算. 例如,要对送到 Mathematica 的一个数组进行转置时,只需要将 Transpose 放在该数组表达式外即可. 为此,在产生表示这个数组的表达式之前要 MLPutFunction(stdlink, "Transpose", 1).
送到 Mathematica 的后处理数据的思想有许多用途. 例如,它是输出事 先不知道其长度的列表的途径.

通过直接追加相继元素在 Mathematica 中产生一个列表

产生每个后继元素在一个嵌套子列表中的列表

Flatten 压平了这个列表

Sequence 自动压平了自己

调用 MLPutIntegerList() 时要知道拟发送的列表长度. 但通过产生一个嵌套 序列的 Sequence 对象后,就不需要事先知道整个列表的长度.

建立围绕结果的 List

MLPutFunction(stdlink, "List", 1);

while( condition ) {
generate an element

产生下一层的 Sequence 对象

MLPutFunction(stdlink, "Sequence", 2);

放入元素

MLPutInteger(stdlink, i );

}

关闭最后一个 Sequence 对象

MLPutFunction(stdlink, "Sequence", 0);

直接从 Mathematica 得到数据的函数

MathLink 提供的 MLPutInteger() 等函数可以从外部程序向 Mathematica 发送数据.同样,MathLink 也提供了 MLGetInteger() 等函数从Mathematica 向外部程序发送数据.
MathLink 模板中对 :ArgumentType: 给出的列表可以用 Manual 结束. 它表明当其它变量接收完后将调用 MathLink 函数去得到额外的表达式.

:Begin:
:Function: f

Mathematica 中的函数 f 有 3 个变量

:Pattern: f[i_Integer, x_Real, y_Real]

这些变量全部直接传递给了外部程序

:Arguments:  i, x, y

仅第一个变量直接传给了外部函数

:ArgumentTypes:  Integer, Manual

:ReturnType: Real
:End:

这个外部函数仅显含一个变量

double f(int i) {

这里对变量 xy 进行说明

double x, y;

MLGetReal() 从链接直接得了数据

MLGetReal(stdlink, &x);
MLGetReal(stdlink, &y);

return i+x+y;
}

MathLink 函数 MLGetInteger(link, pi) 等与标准 C 库函数 fscanf(fp, "%d", pi) 等的工作过程相似. 其第一个变量指定要得到数据的链接, 最后一个变量给出数据应该存放的地址.

通过 MathLink 得到一个函数

:Begin:
:Function: f

Mathematica 中的函数 f 的变量是一个整数列表

:Pattern: f[a: ___Integer ]

该列表直接传给了外部程序

:Arguments:  a

外部程序重新手工地得到这些变量

:ArgumentTypes:  Manual

:ReturnType: Integer
:End:

该外部函数不显含变量

int f(void) {

检查正被发送的函数是一

long n, i;
int a[MAX];

个列表,和在 n 中存储了多少元素

MLCheckFunction(stdlink, "List", &n);

得到列表中的每个元素将它存在 a[i]

for (i=0; i<n; i++)
MLGetInteger(stdlink, a+i);

...
}

在简单情况下,在 Mathematica 这一方可以保证发送到外部程序的数据 具有客观存在所需要的结构. 但仅当数据是由给定名称的函数时 MLCheckFunction() 的返回值将是非零的.
注意,要得到嵌套列表集合或其它对象时,可以调用适当的 MLCheckFunction() 函数来实现.

得到数字列表

外部程序从 Mathematica 得到数据后要安排它们的放存位置.如果该数据像 MLGetInteger(stdlink, &n)一样是单个整数,则用 int n 对其进行说明即可. 但是当该数据可能是有任意长度的整数列表时,在实际调用外部 程序时就要分配保存它的内存.
MLGetIntegerList(stdlink, &a, &n) 将自动地进行 这种分配,并将 a 设置为结果的指针. 注意 MLGetIntegerList() 等 函数分配的内存总是放在一个特殊的区域中,不能直接修改或释放它.

这是一个发送整数列表的外部程序

int f(void) {

这里说明局部变量,其中 a 是一个整数数组

long n;
int *a;

得到一个整数列表,将 a 作为结果指针

MLGetIntegerList(stdlink, &a, &n);

...

这里释放用于储存该整数列表的内存

MLDisownIntegerList(stdlink, a, n);

...
}

IntegerList 指定 :Argument: 时,在外部程序退出时 MathLink 将自动释放 由该列表所占用的内存. 而用 MLGetIntegerList() 直接得到一个整数列表时, 与该列表有关的任务完成后用户不要忘记释放内存.

得到数字数组

MLGetIntegerList() 从单个 Mathematica 列表得到一维整数数组. MLGetIntegerArray() 从列表集合或任意深度的嵌套函数得到一个 整数数组.
在一个结构第 层的 Mathematica 函数名作为一个字符串存在 heads[i] 层该结构的大小存在 dims[i]  中.
向外部程序传递复数列表时,MLGetRealArray() 将产生一个二维数组, 该数组是实部和虚部组成的序列.此时 heads[0] 将是 "List",而 heads[1] 将是 "Complex".
可以与外部程序交换任意精度的数,其方法是用 IntegerDigitsRealDigitsMathematica 中将它们转化为一个数字列表.

得到字符串和符号名

:ArgumentTypes: 指定中用 String 时,MathLink 在函数撤消后将自动释放 该字符串所占用的内存. 这意味着还要继续引用这个字符串时, 就要对它分配内存,并将它所含的字符明确拷贝进去.
MLGetString() 得到一个字符时,MathLink 退出所用函数时不会自动释放 该字符串所占用的内存,所以可以继续引用这个字符串. 而不再使用这个 字符串时,就要明确调用 MLDisownString() 去释放与它有关的内存.

获取任意函数

知道外部程序中需要的函数时,就可以简单地调用 MLCheckFunction(). 而不知道外部程序中需要的函数时,就只好调用 MLGetFunction(). 这样做时,就一定要调用 MLDisownSymbol() 去释放与 MLGetFunction() 所找出的函数的名称有关的内存.