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

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
基本类型规定.
这里是一个函数的 WSTP 模板,该函数的变量是一个整数列表:
: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)
注意,WSTP 把一个列表传递给 C 程序时,它的第一个元素在 C 中标准Wolfram 语言中的那样标准位置是 1.
另外,按照 C 标准,用 String 规定的字符串传递为 char* 对象,用 0 空字节结束. "WSTP 程序的可移植性" 节中将讨论怎样处理特殊字符.
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 个变量的函数
向 Wolfram 语言送数据的 WSTP函数.
在使用 WSTP 模板文件时,mprepmcc 实际上是产生一个包含明确调用 WSTP 库函数的 C 程序. 如果要理解 WSTP库函数的工作过程,就可以查看这个程序的源代码. 注意,当使用 mcc 时,一般需要给出一个 -g 选项,否则,所产生的源代码将会被自动删除.
当外部函数返回单个整数或浮点数时,可以在 WSTP 模板文件中对 :ReturnType: 给出 Integer 或者 Real. 但由于 C 中的内存分配和撤销方式,不能直接对 :ReturnType: 给出 IntegerListRealList 等指定. 要返回这些结构时,必须在 C 程序中直接调用 WSTP 库函数,并对 :ReturnType: 指定 Manual.
这是一个函数的 WSTP 模板,该函数的变量是一个整数,直接用 WSTP 函数返回它的值:
:Begin:
:Function: bits
:Pattern: bits[i_Integer]
:Arguments: {i}
:ArgumentTypes: {Integer}
:ReturnType: Manual
:End:
这个函数被说明为一个空 void 函数:
void bits(int i) {

int a[32], k;
这里给 C 数组 a 赋值:
   for(k=0; k<32; k++) {
a[k] = i%2;
i >>= 1;
if (i==0) break;
}

if (k<32) k++;
这里将数组 ak 个元素送回到 Wolfram 语言:
    WSPutInteger32List(stdlink, a, k);
return ;
}
安装包含外部函数 bits 的程序:
这个外部函数返回一个位列表:
在 C 中用 int a[n1][n2][n3] 说明一个数组时,可以用 WSPutInteger32Array() 将它作为一个深度为3的列表送入 Wolfram 语言.
说明三维的 C 数组:
   int a[8][16][100];
这里建立一个数组 dims,其初始值定义为 a 的维数:
   int dims[] = {8, 16, 100};
将这个三维数组 a 送入 Wolfram 语言中,产生一个深度为 3 的列表:
    WSPutInteger32Array(stdlink, a, dims, NULL, 3);
用 WSTP 函数完全能产生任何 Wolfram 语言表达式. 其基本思想是调用一系列直接与 Wolfram 语言表达式的 FullForm 表示对应的 WSTP 函数.
建立有两个变量的 Wolfram 语言函数 Plus
WSPutFunction(stdlink, "Plus", 2);
指定第一个变量是整数 77
WSPutInteger32(stdlink, 77);
指定第二个变量是符号 x
WSPutSymbol(stdlink, "x");
一般地,先调用 WSPutFunction(),给出拟产生的 Wolfram 语言函数的头和所含变量的个数. 随后调用其它 WSTP 函数轮流填入每个变量. "表达式" 节讨论了 Wolfram 语言表达式的一般结构和头的概念.
产生由两个元素的 Wolfram 语言列表:
WSPutFunction(stdlink, "List", 2);
该列表的第一个元素是从 C 数组 r 中得到的 10 个整数的列表:
WSPutInteger32List(stdlink, r, 10);
该列表的第2个元素本身是2个元素的列表:
WSPutFunction(stdlink, "List", 2);
这个子列表的第一个元素是一个浮点数:
WSPutReal64(stdlink, 4.5);
第二个元素是一个整数:
WSPutInteger32(stdlink, 11);
WSPutInteger32Array()WSPutReal64Array() 可以输送由 C 预分配在内存中按一维形式存放的数组. 但在执行 C 程序过程中要产生数组时,通常将它们设置为指针的嵌套集合. 可以将这种数组送入 Wolfram 语言,其方法是一系列 WSPutFunction() 调用和一个结束调用的 WSPutInteger32List().
这里说明 a 是一个整数数组的列表嵌套:
int ***a;
这里产生一个 n1 个元素的 Wolfram 语言列表:
WSPutFunction(stdlink, "List", n1);
for (i=0; i<n1; i++) {
这里产生一个有 n2 个元素的子列表:
    WSPutFunction(stdlink, "List", n2);
    for (j=0; j<n2; j++) {
这里写出整数列表:
        WSPutInteger32List(stdlink, a[i][j], n3);
    }
}
重要的一点是要知道,用 WSTP 函数产生的表达式当送入 Wolfram 语言后就立即计算. 例如,要对送到 Wolfram 语言的一个数组进行转置时,只需要将 Transpose 放在该数组表达式外即可. 为此,在产生表示这个数组的表达式之前要调用 WSPutFunction(stdlink,"Transpose",1);.
送到 Wolfram 语言的后处理数据的思想有许多用途. 例如,它是输出事先不知道其长度的列表的途径.
通过直接追加相继元素在 Wolfram 语言中产生一个列表:
产生每个后继元素在一个嵌套子列表中的列表:
Flatten 压平了这个列表:
Sequence 自动压平了自己:
调用 WSPutInteger32List()时要知道拟发送的列表长度. 但通过产生一个嵌套序列的 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
直接从 Wolfram 语言得到数据的函数.
WSTP 提供的 WSPutInteger32() 等函数可以从外部程序向 Wolfram 语言发送数据. WSTP 也提供了 WSGetInteger32() 等函数从 Wolfram 语言向外部程序发送数据.
WSTP 模板中对 :ArgumentTypes: 给出的列表可以用 Manual 结束. 它表明当其它变量接收完后将调用 WSTP 函数去得到额外的表达式.
:Begin:
:Function: f
Wolfram 语言中的函数 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;
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)
检查函数的头,保存它的变量数目
通过 WSTP 得到一个函数.
:Begin:
:Function: f
Wolfram 语言中的函数 f 的变量是一个整数列表:
:Pattern:        f[a:{___Integer}]
该列表直接传给了外部程序:
:Arguments:      {a}
外部程序重新手工地得到这些变量:
:ArgumentTypes:  {Manual}
:ReturnType:     Integer
:End:
该外部函数不显含变量:
int f(void) {
以下声明了局部变量:
    int n, i;
int a[MAX];
检查正被发送的函数是一个列表,和在 n 中存储了多少元素:
    WSCheckFunction(stdlink, "List", &n);
得到列表中的每个元素将它存在 a[i] 中:
   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)
释放与浮点数列表有关的内存
获取数字列表.
外部程序从 Wolfram 语言得到数据后要安排它们的存放位置. 如果该数据像 WSGetInteger32(stdlink,&n) 一样是单个整数,则用 intn 对其进行说明即可.
但是当该数据可能是有任意长度的整数列表时,在实际调用外部程序时就要分配保存它的内存.
WSGetInteger32List(stdlink,&a,&n) 将自动地进行这种分配,并将 a 设置为结果的指针. 注意, WSGetInteger32List() 等函数分配的内存总是放在一个特殊的区域中,不能直接修改或者释放它.
这是一个发送整数列表的外部程序:
int f(void) {
这里说明局部变量. 其中 a 是一个整数数组:
    int n;
int *a;
得到一个整数列表,将 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 语言函数得到一个整数数组.
在一个结构第 i 层的 Wolfram 语言函数名作为一个字符串存在 heads[i] 中. 第 i 层该结构的大小存在 dims[i] 中,而总深度存在 d 中.
向外部程序传递复数列表时,WSGetReal64Array() 将产生一个二维数组,该数组是实部和虚部组成的序列. 此时,heads[0] 将是 "List",而 heads[1] 将是 "Complex".
注意,可以与外部程序交换任意精度的数,其方法是用 IntegerDigitsRealDigits 在 Wolfram 语言中将它们转化为一个数字列表.
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() 所找出的函数的名称有关的内存.