处理列表、数组和其它表达式
The Wolfram Symbolic Transfer Protocol (WSTP) 可以与外部程序交换任何类型的数据. 对于更常用的数据类型,需要在 WSTP 模板文件中给出适当的:ArgumentTypes: 或 :ReturnType: 规格.
Wolfram 语言规定 | C 规定 | |
Integer | 整数 | int |
Real | 浮点数 | double |
IntegerList | 整数列表 | int*,long |
RealList | 浮点数列表 | double*,long |
String | 字符串 | char* |
Symbol | 符号名 | char* |
Manual | 直接调用 WSTP
子程序 | void |
:Begin:
:Function: h
:Pattern: h[a_List]
:Arguments: {a}
:ArgumentTypes: {IntegerList}
:ReturnType: Integer
:End:
int h(int *a, long alen) {
int i, tot=0;
for(i=0; i<alen; i++)
tot += a[i];
return tot;
}
这里的模式是匹配的,但列表中的元素与外部代码中的类型不同,所以返回到 $Failed:
:ArgumentTypes: {IntegerList, RealList, Integer}
void f(int *a, long alen, double *b, long blen, int c)
WSPutInteger32(stdlink,int i) | 放入单个整数 |
WSPutReal64(stdlink,double x) | 放入单个浮点数 |
WSPutInteger32List(stdlink,int*a,int n) | |
放入从位置 a 开始的 n 个整数列表 | |
WSPutReal64List(stdlink,double*a,int n) | |
放入从位置 a 开始的 n 个浮点数列表 | |
WSPutInteger32Array(stdlink,int*a,int*dims,NULL,int d) | |
放入一个整数阵列去形成一个深度为 d 维数为 dims 的列表 | |
WSPutReal64Array(stdlink,double*a,int*dims,NULL,int d) | |
放入一个浮点数阵列 | |
WSPutString(stdlink,char*s) | 放入一个字符串 |
WSPutSymbol(stdlink,char*s) | 放入一个字符串作为符号名 |
WSPutFunction(stdlink,char*s,int n) | |
开始放一个头部为 s,有 n 个变量的函数 |
在使用 WSTP 模板文件时,mprep 和 mcc 实际上是产生一个包含明确调用 WSTP 库函数的 C 程序. 如果要理解 WSTP库函数的工作过程,就可以查看这个程序的源代码. 注意,当使用 mcc 时,一般需要给出一个 -g 选项,否则,所产生的源代码将会被自动删除.
当外部函数返回单个整数或浮点数时,可以在 WSTP 模板文件中对 :ReturnType: 给出 Integer 或者 Real. 但由于 C 中的内存分配和撤销方式,不能直接对 :ReturnType: 给出 IntegerList 或 RealList 等指定. 要返回这些结构时,必须在 C 程序中直接调用 WSTP 库函数,并对 :ReturnType: 指定 Manual.
:Begin:
:Function: bits
:Pattern: bits[i_Integer]
:Arguments: {i}
:ArgumentTypes: {Integer}
:ReturnType: Manual
:End:
void bits(int i) {
int a[32], k;
for(k=0; k<32; k++) {
a[k] = i%2;
i >>= 1;
if (i==0) break;
}
if (k<32) k++;
WSPutInteger32List(stdlink, a, k);
return ;
}
int a[8][16][100];
int dims[] = {8, 16, 100};
WSPutInteger32Array(stdlink, a, dims, NULL, 3);
用 WSTP 函数完全能产生任何 Wolfram 语言表达式. 其基本思想是调用一系列直接与 Wolfram 语言表达式的 FullForm 表示对应的 WSTP 函数.
建立有两个变量的 Wolfram 语言函数 Plus:
WSPutFunction(stdlink, "Plus", 2);
WSPutInteger32(stdlink, 77);
WSPutSymbol(stdlink, "x");
一般地,先调用 WSPutFunction(),给出拟产生的 Wolfram 语言函数的头和所含变量的个数. 随后调用其它 WSTP 函数轮流填入每个变量. "表达式" 节讨论了 Wolfram 语言表达式的一般结构和头的概念.
WSPutFunction(stdlink, "List", 2);
WSPutInteger32List(stdlink, r, 10);
WSPutFunction(stdlink, "List", 2);
WSPutReal64(stdlink, 4.5);
WSPutInteger32(stdlink, 11);
WSPutInteger32Array() 和 WSPutReal64Array() 可以输送由 C 预分配在内存中按一维形式存放的数组. 但在执行 C 程序过程中要产生数组时,通常将它们设置为指针的嵌套集合. 可以将这种数组送入 Wolfram 语言,其方法是一系列 WSPutFunction() 调用和一个结束调用的 WSPutInteger32List().
int ***a;
WSPutFunction(stdlink, "List", n1);
for (i=0; i<n1; i++) {
WSPutFunction(stdlink, "List", n2);
for (j=0; j<n2; j++) {
WSPutInteger32List(stdlink, a[i][j], n3);
}
}
重要的一点是要知道,用 WSTP 函数产生的表达式当送入 Wolfram 语言后就立即计算. 例如,要对送到 Wolfram 语言的一个数组进行转置时,只需要将 Transpose 放在该数组表达式外即可. 为此,在产生表示这个数组的表达式之前要调用 WSPutFunction(stdlink,"Transpose",1);.
Flatten 压平了这个列表:
Sequence 自动压平了自己:
建立围绕结果的 List:
WSPutFunction(stdlink, "List", 1);
while( condition ) {
/* generate an element */
产生下一层的 Sequence 对象:
WSPutFunction(stdlink, "Sequence", 2);
WSPutInteger32(stdlink, i );
}
关闭最后一个 Sequence 对象:
WSPutFunction(stdlink, "Sequence", 0);
WSGetInteger32(stdlink,int*i) | 得到一个整数,将其存放在地址 i 中 |
WSGetReal64(stdlink,double*x) | 得到一个浮点数,将它存放在地址 x 中 |
WSTP 提供的 WSPutInteger32() 等函数可以从外部程序向 Wolfram 语言发送数据. WSTP 也提供了 WSGetInteger32() 等函数从 Wolfram 语言向外部程序发送数据.
:Begin:
:Function: f
:Pattern: f[i_Integer, x_Real, y_Real]
:Arguments: {i, x, y}
:ArgumentTypes: {Integer, Manual}
:ReturnType: Real
:End:
double f(int i) {
double x, y;
WSGetReal64() 从链接直接得到数据:
WSGetReal64(stdlink, &x);
WSGetReal64(stdlink, &y);
return i+x+y;
}
WSTP 函数如 WSGetInteger32(link,pi) 等的工作过程类似于标准 C 库函数如 fscanf(fp,"%d",pi) 等. 第一个变量指定要得到数据的链接,最后一个变量给出数据应该存放的地址.
WSCheckFunction(stdlink,"name",int*n) | |
检查函数的头,保存它的变量数目 |
:Begin:
:Function: f
:Pattern: f[a:{___Integer}]
:Arguments: {a}
:ArgumentTypes: {Manual}
:ReturnType: Integer
:End:
int f(void) {
int n, i;
int a[MAX];
WSCheckFunction(stdlink, "List", &n);
for (i=0; i<n; i++)
WSGetInteger32(stdlink, a+i);
在简单情况下,在 Wolfram 语言这一方可以保证发送到外部程序的数据具有客观存在所需要的结构. 但仅当数据是由给定名称的函数时,WSCheckFunction() 的返回值将是 MLSUCCESS.
注意,要得到嵌套列表集合或者其它对象时,可以调用适当的 WSCheckFunction() 函数来实现.
WSGetInteger32List(stdlink,int**a,int*n) | |
得到一个整数列表,分配存储它需要的内存 | |
WSGetReal64List(stdlink,double**a,int*n) | |
得到一个浮点数列表 | |
WSReleaseInteger32List(stdlink,int*a,int n) | |
释放与整数列表有关的内存 | |
WSReleaseReal64List(stdlink,double*a,int n) | |
释放与浮点数列表有关的内存 |
WSGetInteger32List(stdlink,&a,&n) 将自动地进行这种分配,并将 a 设置为结果的指针. 注意, WSGetInteger32List() 等函数分配的内存总是放在一个特殊的区域中,不能直接修改或者释放它.
int f(void) {
int n;
int *a;
WSGetInteger32List(stdlink, &a, &n);
WSReleaseInteger32List(stdlink, a, n);
...
}
由 IntegerList 指定 :ArgumentTypes: 时,在外部函数退出时,WSTP 将自动释放由该列表所占用的内存. 而用 WSGetInteger32List() 直接得到一个整数列表时,与该列表有关的任务完成后用户不要忘记释放内存.
WSGetInteger32Array(stdlink,int**a,int**dims,char***heads,int*d) | |
得到任意深度的整数数组 | |
WSGetReal64Array(stdlink,double**a,int**dims,char***heads,int*d) | |
得到任意深度的浮点数数组 | |
WSReleaseInteger32Array(stdlink,int*a,int*dims,char**heads,int d) | |
释放与一个整数数组有关的内存 | |
WSReleaseReal64Array(stdlink,double*a,int*dims,char**heads,int d) | |
释放与一个浮点数数组有关的内存 |
WSGetInteger32List() 从单个 Wolfram 语言列表得到一维整数数组. WSGetInteger32Array() 从列表集合或具有任意深度的嵌套 Wolfram 语言函数得到一个整数数组.
向外部程序传递复数列表时,WSGetReal64Array() 将产生一个二维数组,该数组是实部和虚部组成的序列. 此时,heads[0] 将是 "List",而 heads[1] 将是 "Complex".
WSGetString(stdlink,char**s) | 得到一个字符串 |
WSGetSymbol(stdlink,char**s) | 得到一个符号名 |
WSReleaseString(stdlink,char*s) | 释放与一个字符串有关的内存 |
WSReleaseSymbol(stdlink,char*s) | 释放与一个符号有关的内存 |
在 :ArgumentTypes: 指定中用 String 时,WSTP 在函数撤销后将自动释放该字符串所占用的内存. 这意味着还要继续引用这个字符串时,就要对它分配内存,并将它所含的字符明确拷贝进去.
用 WSGetString() 得到一个字符时,当函数退出时,WSTP 将不会自动释放用于字符串的内存. 结果,用户可以继续引用这个字符串. 请注意不要因为写入 WSGetString() 返回的内存,而修改字符串的内容. 当用户不再需要该字符串时,就要明确调用 WSReleaseString() 去释放与它有关的内存.
WSGetFunction(stdlink,char**s,int*n) | |
开始获取一个函数,将它的头部名存在 s 中,变量个数存在 n 中 | |
WSReleaseSymbol(stdlink,char*s) | 释放与函数名有关的内存 |
知道外部程序中需要的函数时,就可以简单地调用 WSCheckFunction(). 而不知道外部程序中需要的函数时,就只好调用 WSGetFunction(). 这样做时,就一定要调用 WSReleaseSymbol() 去释放与 WSGetFunction() 所找出的函数的名称有关的内存.