欢迎访问 Forcal程序设计

FORCAL 新手参考

目 录

1 概述 |
2 Forcal中的表达式（函数） | 表达式、函数、变量、不同类型的函数调用
3 Forcal中的模块 | 模块、全局函数、私有函数
4 Forcal中的变量 | 自变量、动态变量、静态变量、模块变量和全局变量释疑
5 函数的传值调用和传址调用 | 传址调用可返回多个参数
6 用表达式作为函数的参数 | 函数句柄的使用
7 标准输入输出 | printff和printf函数
8 工程实例 | 演示Forcal的功能
9 Forcal程序 | Forcal程序由来自多个文件的若干个模块组成
10 Forcal扩展动态库 | Forcal扩展动态库中的函数可无缝地紧密合作完成一个复杂任务
11 指针和对象 | 这是复杂的东西，一带而过
12 Forcal中的多线程 | 通过MForcal实现多线程
13 并行运算设想 | 就是一个设想
14 中文编程 | 这是很多人的梦想
15 其他问题 | 需要进一步系统地了解Forcal

1 概述 [返回页首]

Forcal是一个小巧、快速、语法简单、具有强大可扩充性的轻量级嵌入式脚本。

与其他语言或脚本相比，Forcal似乎有一些特殊的语法，这些特殊的语法主要体现在函数声明、变量定义、模块定义等方面；在语句实现上，如表达式语句、函数调用语句、流程控制语句等，与主流语言如C/C++等还是比较接近的。Forcal特殊的语法如函数声明、变量定义、模块定义等是简洁高效的，不仅输入速度快，在视觉感受上，也是一目了然的，体现了Forcal语法简单的特点。

本文主要介绍Forcal语法上的一些特殊之处，本文也将介绍学习Forcal必须具备的一些语法知识。但本文不对Forcal进行系统地介绍，而是将Forcal主要的语法特点和功能，概括性地介绍给初次接触Forcal的新手。本文的介绍也许并不全面，但如果您阅读本文不是太难，进一步系统地了解Forcal就会变得轻松愉快。

在此之前，您应当至少学习过一门计算机语言，具备将一个数学公式转换成计算机语言形式的表达式的能力。

2 Forcal中的表达式（函数） [返回页首]

Forcal表达式由一些逗号（或冒号）隔开的语句组成，通常用分号表示一个表达式的结束。如果表达式带有名字，就定义了一个函数。如下列：

> //在每行中两个‘//’后的字符将被忽略，因而是注释行
> 函数名(自变量1,自变量2,... ...)= //在定义一个函数时，小括号（必须是小括号）和等号不可缺省
> { //一对花括号{ }可以缺省
> 自变量1=自变量1+自变量2, //语句1
> ... ..., //语句i
> 自变量1*自变量2 //最后一个语句返回表达式的值
> };

这是Forcal特殊的函数定义方法，但很容易理解，与数学公式极为相似。例如定义两个数相加：

> f(x,y)=x+y;

在函数中，可以使用5种变量：自变量、动态变量、静态变量、模块变量和全局变量。自变量、动态变量和静态变量只能被定义该变量的表达式所访问；模块变量可被同一模块的所有表达式所访问，其他模块的表达式无法访问；全局变量可被所有的表达式所访问。自变量用于向表达式传递参数，因此自变量也称为形式参数。动态变量只在表达式执行时起作用，一旦表达式执行完毕，动态变量也随之失效。静态变量存在于表达式的整个生命周期，每次表达式执行完毕，静态变量的值被保留。FORCAL在编译表达式时，将所有静态变量初始化为0，其余的变量均不进行初始化。

Forcal函数的完整定义为：

F(a,b:x,y,static,u:s,t,common,v)=
{x=1,y=2,
a+b+x+y+static+u+s+t+common+v
}

F是表达式的名字，a和b是自变量，x和y是动态变量，static和u是静态变量，s和t是模块变量，common和v是全局变量。自变量、动态变量和静态变量以及模块变量和全局变量之间用冒号分隔，即第一个冒号前为自变量，两个冒号之间为动态变量和静态变量，第二个冒号后为模块变量和全局变量。两个冒号之间用关键字static分隔动态变量和静态变量，static之前为动态变量，static及以后变量均为静态变量，关键字static只能用在两个冒号之间。第二个冒号后用关键字common分隔模块变量和全局变量，common之前为模块变量，common及以后变量均为全局变量，关键字common只能用在第二个冒号后。FORCAL中的所有变量均可缺省。以下都是合法的变量定义的例子：

F()= ... ... //没有使用任何变量，称无参表达式；
F(::)= ... ... //没有使用任何变量，称无参表达式；
F(a,b)= ... ... //定义了两个自变量a和b；
F(:x,y)= ... ... //定义了两个动态变量x和y；
F(:static,u)= ... ... //定义了两个静态变量static和u；
F(::s,t)= ... ... //定义了两个模块变量s和t；
F(::common,v)= ... ...//定义了两个全局变量common和v；
F(a,b:x,y)= ... ... //定义了两个自变量a和b，两个动态变量x和y；
F(a,b::s,t)= ... ... //定义了两个自变量a和b，两个模块变量s和t；
F(:x,y:s,t)= ... ... //定义了两个动态变量x和y，两个模块变量s和t；

变量的使用见下面的例子：

F(a,b:x,y,static,u:s,t,common,v)={a=1,b=2,x=3,y=4,static=5,u=6,s=7,t=8,common=9,v=10};//函数定义及变量赋值；
A(a,b:x,y,static,u:s,t,common,v)=a;//函数定义；
B(a,b:x,y,static,u:s,t,common,v)=b;//函数定义；
X(a,b:x,y,static,u:s,t,common,v)=x;//函数定义；
Y(a,b:x,y,static,u:s,t,common,v)=y;//函数定义；
_S(a,b:x,y,static,u:s,t,common,v)=static;//函数定义；
U(a,b:x,y,static,u:s,t,common,v)=u;//函数定义；
S(a,b:x,y,static,u:s,t,common,v)=s;//函数定义；
T(a,b:x,y,static,u:s,t,common,v)=t;//函数定义；
_C(a,b:x,y,static,u:s,t,common,v)=common;//函数定义；
V(a,b:x,y,static,u:s,t,common,v)=v;//函数定义；
F(11,22);//函数调用，进行变量赋值，返回值=10；
A(11,22);//函数调用，返回值=11；
B(11,22);//函数调用，返回值=22；
X(11,22);//函数调用，返回值=随机数值；
Y(11,22);//函数调用，返回值=随机数值；
_S(11,22);//函数调用，返回值=0；
U(11,22);//函数调用，返回值=0；
S(11,22);//函数调用，返回值=7；
T(11,22);//函数调用，返回值=8；
_C(11,22);//函数调用，返回值=9；
V(11,22);//函数调用，返回值=10；

这是Forcal特有的变量定义方法，输入方便，简洁直观，一目了然。Forcal依靠这5种变量，成为描述能力极强的脚本，可适应大型的工程。

语法快速浏览：

（1）在Forcal语句中（函数定义中，等号后面的表达式中）可任意使用三对括号：()、[]、{}，只要成对使用即可。这将使表达式层次非常清晰。如：f(x)=2+exp{1/[2*(3-x)]};
（2）如果括号不是用来说明函数参数的，则任意括号中可包含任意多个由逗号隔开的语句，括号具有一个值，即最后一个语句的值，这就是Forcal的括号运算符。如：[3,1,2]的值等于2。
（3）一般，Forcal程序只计算无参表达式，对于有参数的表达式，只编译，不计算。例如：

F(x,y)=x+y; //函数定义， 有自变量参数，只编译，不计算
F[2,3]+5; //无参表达式，执行计算

（4）Forcal表达式只有三种：整数表达式、实数表达式和复数表达式。一般，Forcal程序中以i:、r:、c:开头的表达式分别是整数、实数、复数表达式，缺省是实数表达式。例如：

i: a(x,y)=x+y; //函数定义， 整数表达式
r: 2.2+3; //无参表达式，实数表达式
c: (2-3i)*(3+2i); //无参表达式，复数表达式
2.2+3; //无参表达式，缺省是实数表达式

（5）FORCAL不会为不同类型的函数调用进行类型转换，用户可根据需要自行转换（传递句柄或指针参数时无需转换）。例如：

i:f(a,b)=a+b; //整数表达式定义
f(2,3); //实数表达式调用整数表达式，但未进行数据转换
itor{f[rtoi(2),rtoi(3)]}; //实数表达式调用整数表达式，函数rtoi将一个实数转换成一个整数，函数itor将一个整数转换成一个实数，计算值是正确的

//以下函数调用无需进行数据转换，当然这种函数调用是没有必要的，在这里仅仅是一个例子
a(x,y)=x+y; //实数表达式定义
i:b(x,y)=a(x,y); //整数表达式调用实数表达式，没有进行数据转换
b[2.2,3]; //实数表达式调用整数表达式，没有进行数据转换，计算值是正确的

3 Forcal中的模块 [返回页首]

FORCAL支持表达式的模块化编译。

在用FORCAL编译表达式时，要给该表达式指定模块号，模块号用一个整数进行标识。

在FORCAL中，一个模块由一个或多个表达式组成。模块用一个整数标识，整数可正可负，只要绝对值相等，就属于同一个模块。一般用正整数表示该模块名。模块共有两类，即主模块（0#模块）和普通模块（其他标号的模块）。

同一模块中，模块号为负的表达式称私有表达式，只能被本模块的表达式所访问（即调用），在其他模块中是不可见的；模块号为正的表达式称公有表达式或全局表达式，能被任何一个表达式所访问。主模块（0#模块）中的表达式都是私有表达式。任何一个表达式，既可以访问本模块中的表达式，也可以访问其他模块中的全局表达式，如果本模块中的一个私有表达式与其他模块的一个全局表达式重名，将优先调用本模块中的私有表达式。

由以上规定可以看出，主模块可以访问本模块中的表达式，也可以访问其他模块中的全局表达式。因此，主模块常常用在主程序中。

通常，可以用编译符#MODULE#及#END#定义一个模块，用编译符~输出模块中的全局表达式。另外，若表达式名称前有编译符!（首先解释为立即执行编译符，而不是逻辑非运算符），在编译后将立即执行；若表达式名称前有编译符:，只编译，不执行。如下例：

> #MODULE# //定义一个子模块
> !a()=printff("字符串！"); //模块私有表达式，编译后立即执行
> f(x)= x+1; //模块私有表达式
> :g()= 100; //模块私有表达式，只编译，不执行
> ~h(x)= f(x)+g()+2; //全局表达式
> #END# //子模块定义结束
> f(x)= x+5; //主模块中的私有表达式，可以使用与其他模块中的表达式相同的名字
> f[3]; //调用主模块中的函数f
> h[3]; //调用子模块中的全局表达式

编译指令小结：一般，Forcal程序中#MODULE#、#END#、~、i:、r:、c:、:、!等称为编译指令，用以确定一个表达式的类型、所在模块、是否私有函数等属性。这些编译指令必须位于表达式的开头，有些指令能同时使用，有些指令不能同时使用，并且在使用时有一定的次序，按先后顺序依次为：

1）编译指令#MODULE#和#END#必须在表达式的最前面，一般单独成行。#MODULE#表示开始一个子模块的编译，#END#表示回到主模块的编译。

2）~表示该表达式是一个全局表达式，否则是私有表达式。该编译符对主模块（0#模块）中的表达式无效，主模块（0#模块）中的表达式都是私有表达式。

3）编译指令i:、r:、c:不能混合使用，只能使用其中的一个，强制指定表达式的类型。如果都没有用，表达式按实数类型进行编译。

4）编译指令:、!不能混合使用，只能使用其中的一个。“:”表示该表达式只编译，不执行；“!”表示该表达式编译后立即执行，但以后执行时不再自动执行。

如果表达式前没有使用任何一个编译指令，则按实数类型编译为私有表达式，若该表达式是无参表达式，则执行模块时将自动执行。

4 Forcal中的变量 [返回页首]

本节介绍自变量、动态变量、静态变量、模块变量和全局变量的功能用法，这是学好Forcal的必备基础知识，如果你对此非常熟悉，可以跳过这一节。

自变量用于向表达式传递参数，例如：

F(x,y)=x+y; //函数定义，自变量x,y用于向函数传递参数
F[2,3]+5; //简单的函数调用

动态变量只在表达式执行时起作用，一旦表达式执行完毕，动态变量也随之失效。例如：

F(x: t)= t=1,x+t; //函数定义，t为动态变量，每次进入该函数，t开始起作用，退出该函数时，t的值不会保留。动态变量应先赋值，后使用
F[2]; //简单的函数调用

静态变量存在于表达式的整个生命周期，每次表达式执行完毕，静态变量的值被保留。FORCAL在编译表达式时，将所有静态变量初始化为0。例如：

F(:static,t)= t++;//函数定义，t为静态变量，初始值为0。每次调用该函数，返回值增1
F[]; //简单的函数调用
F[]; //简单的函数调用

模块变量可被同一模块的所有表达式所访问，其他模块的表达式无法访问；全局变量可被所有的表达式所访问。例如：

> #MODULE# //定义一个子模块
> a(::mm,common,cc)= mm=11,cc=22; //mm是模块变量，cc是全局变量
> b(::mm)= mm; //输出模块变量mm
> c(::common,cc)= cc; //输出全局变量cc
> #END# //子模块定义结束
> #MODULE# //定义一个子模块
> b(::mm)= mm; //输出模块变量mm，mm没有预先赋值，输出的是随机值
> c(::common,cc)= cc; //输出全局变量cc
> #END#

注意：全局变量在整个Forcal系统中只有一个备份，故所有表达式均可访问；Forcal系统中有多个模块，模块变量在同一模块中只有一个备份，只有本模块的表达式可以访问；每个模块由多个表达式组成，自变量、动态变量及静态变量只有所在的表达式可以访问到。另外，静态变量在一个表达式中只有一个备份，但自变量和动态变量不是这样，每当该表达式被调用时，系统都将重新给他们分配变量空间，只不过自变量自然地接受函数调用时所传过来的值，而动态变量中是一些随机值，故动态变量要先赋值，后使用。

以下例子演示了动态变量与静态变量及模块变量的区别：

SetRealStackMax(1000); //设置实数堆栈为1000
//阶乘函数Fact的递归实现；故意使用了动态变量t，每次进入函数Fact，都将给t分配空间，故函数能正常工作。若将t改成静态变量或模块变量，函数将不能正常工作，可以试一下
Fact(n:t)= t=n,which{t<=1,1,Fact(t-1)*t}; //阶乘函数Fact的递归实现，使用动态变量t
//Fact(n:static,t)= t=n,which{t<=1,1,Fact(t-1)*t}; //阶乘函数Fact的递归实现，使用静态变量t
//Fact(n::t)= t=n,which{t<=1,1,Fact(t-1)*t}; //阶乘函数Fact的递归实现，使用模块变量t
Fact(3); //计算3!
Fact(5); //计算5!
Fact(10); //计算10!

给上面的例子添加验证代码，如下所示：

SetRealStackMax(1000); //设置实数堆栈为1000
//阶乘函数Fact的递归实现；故意使用了动态变量t，每次进入函数Fact，都将给t分配空间，故函数能正常工作。若将t改成静态变量或模块变量，函数将不能正常工作，可以试一下
Fact(n:s,t)={ //阶乘函数Fact的递归实现，使用动态变量t。若使用静态变量t，可改为：Fact(n:s,static,t)；若使用模块变量t，可改为：Fact(n:s:t)
t=n,
printff{"函数Fact调用前的t={1,r}\r\n",t},
s=which{t<=1,1,Fact(t-1)*t},
printff{"函数Fact调用后的t={1,r}\r\n",t},
s
};
Fact(3); //计算3!
Fact(5); //计算5!
Fact(10); //计算10!

5 函数的传值调用和传址调用 [返回页首]

传值调用是把变量值拷贝到被调函数的形式参数中，函数在执行时，参数的改变不会影响到调用函数时所用的变量。
传址调用（也叫引用调用）是把变量的地址拷贝到被调函数的形式参数中，函数在执行时，参数的改变会影响到调用函数时所用的变量。
通常，FORCAL是按传值调用的，除非你用取地址运算符&（也叫引用运算符）显示地通知FORCAL编译器，要按传址方式使用参数。在使用运算符&时，&只能放到（用逗号或冒号隔开的）单独存在的变量的前面。如下列：

a(x,y)= x=2,y=3;
(:x,y)= x=5,y=6,a(x,y),x; //传值调用，x=5
(:x,y)= x=5,y=6,a(x,y),y; //传值调用，y=6
(:x,y)= x=5,y=6,a(&x,y),x; //传址调用，x=2
(:x,y)= x=5,y=6,a(x,&y),y; //传址调用，y=3

以下函数交换了两个数的值：

a(x,y:t)= t=x,x=y,y=t;
(:x,y)= x=5,y=6,a(&x,&y),x; //传址调用，x=6
(:x,y)= x=5,y=6,a(&x,&y),y; //传址调用，x=5

6 用表达式作为函数的参数 [返回页首]

在函数调用时，有时候需要用表达式（即自定义函数）作为函数的参数。

（1）用表达式的名称作为字符串（两个双引号"..."之间的内容为一个字符串）传给函数

例子：

F(x,y)=sin[x]+0.8-y; //定义二元函数；
sum("F",1,2,0.01,2,5,0.1); //对函数F循环求和：x自1到2，增量为0.01；y自2到5，增量为0.01。

（2）用二级函数HFor("ForName",ForType)获得表达式句柄（也称为表达式指针、函数指针）传给函数

例子：

i: aa(x)=x+8; //定义一元函数aa；
i: (:x)=sys::call[1,HFor("aa",1),7,&x],x; //调用一元函数aa，并将参数7传递给该函数；sys::call是 Forcal系统扩展库FcSystem32W 的一个函数。

一般，在函数的说明中会指定需传递的表达式参数的类型，或者传递字符串形式的表达式名称，或者传递表达式的句柄。

7 标准输入输出 [返回页首]

Forcal数据类型扩展动态库FcData中提供了Forcal自己的标准输入输出函数printff和scanfs；而标准输入输出库FcIO中提供了printf、sprintf、fprintf、sscanf、nsscanf、fscanf等函数，这些函数与C语言中的相应函数的功能用法基本相同。这里仅举例说明printff和printf的用法：

> !using["io"]; //使用命名空间io
> printff{"Hello Forcal!大家好！\r\n"}; //输出字符串
> printf {"Hello Forcal!大家好！\r\n"}; //输出字符串
> printff{"整数i={1,i,-6}，实数r={2,r,15.6}\r\n",123,123.456789}; //输出数据
> printf {"整数i=%-6d，实数r=%15.6e\r\n", 123,123.456789}; //输出数据

8 工程实例 [返回页首]

这些例子用到了5个库：核心库forcal32w.dll、Forcal数据类型扩展库FcData32W.dll、徐士良算法库XSLSF32W.dll、IMSL库FcIMSL32W.dll和优化库FcOpt32W.dll。核心库是必然要使用的，后面三个是Forcal扩展动态库。这些库可以单独工作，也可以协同工作，共同完成计算任务。注意徐士良算法库XSLSF32W.dll中提供了命名空间“XSLSF”，IMSL库FcIMSL32W.dll中提供了命名空间“IMSL”，优化库FcOpt32W.dll中提供了命名空间“fcopt”，用using函数启用命名空间可简化函数的输入。

8.1 简单例子

（1）求函数f(x)=ln(x)/sqrt(x)在0～1上的积分值，注意这个函数当x=0时有奇点。准确值为-4。

> f(x)=ln(x)/sqrt(x); //一元函数定义
> main(:val,err)=
> {
> val=IMSL::QDAGS[HFor("f"),0,1,0,0.001,&err], //得到积分值及绝对误差
> printff{"val={1,r}, err={2,r}\r\n",val,err} //用函数printff输出计算结果
> };

结果：

> val=-4.0000000000000853, err=1.354472090042691e-013

（2）计算函数f(x,y)=y*cos(x+y*y)的近似积分值。外层区间取[0,1]，内层区间取[-2*x,5*x]。如下图：

> f(x,y)=y*cos(x+y*y); //被积函数
> g(x)=-2*x; //积分下限
> h(x)=5*x; //积分上限
> IMSL::TWODQ[HFor("f"),0,1,HFor("g"),HFor("h"),0.001,0,6,0]; //计算积分值

结果：-8.288981913740839e-002

（3）计算下图中的三重积分

方法１：用计算多重积分的高斯方法

> f(x,y,z)=x*y*z; //被积函数
> yx(j,x1,x2,x3,y0,y1)=
> {
> which{
> j==0 : [ y0=0, y1=1 ], //返回第一层积分下限及上限
> j==1 : [ y0=0, y1=1-x1 ], //返回第二层积分下限及上限
> j==2 : [ y0=0, y1=1-x1-x2 ] //返回第三层积分下限及上限
> }
> };
> XSLSF::igaus[HFor("f"),HFor("yx"),5,5,5]; //返回积分值

结果：1.388888889097291e-003

方法２：用单重积分和二重积分构造三重积分：第一层用单重积分函数QDAGS，第二、三层用二重积分函数TWODQ

> fyz(y,z::x)=x*y*z; //x是模块变量
> gyz(y)=0; //积分下限
> hyz(y::x)=1-x-y; //积分上限
> //以下函数ｆ中，x=_x的意思是将自变量_x传给模块变量ｘ，模块变量ｘ在其他函数中将用到
> f(_x::x)= x=_x, IMSL::TWODQ[HFor("fyz"),0,1-x,HFor("gyz"),HFor("hyz"),0,0.001,2,0];
> IMSL::QDAGS[HFor("f"),0,1,0,0.001,0]; //得到积分值

结果：1.388888888888889e-003

方法３：用单重积分和二重积分构造三重积分：第一、二层用二重积分函数TWODQ，第三层用单重积分函数QDAGS

> fz(z::x,y)=x*y*z;
> f(_x,_y::x,y)= x=_x,y=_y, IMSL::QDAGS[HFor("fz"),0,1-x-y,0,0.001,0];
> g(x)=0; //积分下限
> h(x)=1-x; //积分上限
> IMSL::TWODQ[HFor("f"),0,1,HFor("g"),HFor("h"),0,0.001,2,0];

结果：1.388888888888889e-003

（4）求解含积分的方程

> !using["fcopt","IMSL"]; //使用命名空间fcopt和IMSL
> pp(x::p)=exp{-[(x/p)^2]}; //函数定义，用于积分
> f(_p,q,y1,y2::p)= //函数定义，用于解方程
> {
> p=_p, //传递模块变量
> y1=q*QDAGS[HFor("pp"),0,p-q,0,1e-6,0]-1.99,
> y2=q*QDAGS[HFor("pp"),0,p+q,0,1e-6,0]-2.87
> };
> solve[HFor("f")]; //解方程

结果（每行前面的数是解，最后一个数是误差，有2组解）：

> 3.20186397420115 1.074732389098163 0.
> -3.20186397420115 -1.074732389098163 0.
> 2.

8.2 复杂例子

以下工程实例来自网上，例子有所修改，仅仅说明Forcal是如何解决这类复杂问题的，这些代码体现了Forcal代码的可读性及可写性。

这些例子的运算量较大，可能将运行几秒到十几秒甚至几分钟的时间。

（1）公式如图所示：

已知k0=2*pi/0.6328e-6，N=1.543674，求β值？

算法分析：有3个公式，看起来似乎有些复杂，但只有一个变量，解单变量方程即可。注意最后一个式子实际上是一个常数；第二个式子中包含了一个积分项，而第二个式子包含在第一个式子等号前的积分式中，故需要做两次积分；第一个式子等号后比较简单，其中用到了最后一个式子。 该方程用普通解方程方法求解比较困难，难点在于解方程时的初值或初始区间的选择，分析从略，选择在区间k0～k0*ns（ns的意义见代码中的数据）中对分法搜索可解此方程；使用优化库FcOpt中的isolve函数，无需费力寻找初值，可方便地解此方程，但耗时较长。

Forcal代码：

> //这里所有表达式属于同一个模块
> //本例子使用模块变量传递参数，模块变量在本模块内有相同的地址，因而可在同一个模块的表达式之间传递参数
> //在这里不推荐使用全局变量，全局变量可在所有模块中的表达式之间传递参数，如果使用全局变量，可能影响到其他模块中的表达式

> //步骤0：使用命名空间math、XSLSF、IMSL 和fcopt
> !using["math","XSLSF","IMSL","fcopt"];

> //步骤1：定义一些常量
> //这个表达式用模块变量传递一些常量，这些常量用在下面的表达式中，注意 dnx就是最后一个式子
> init(::ns,k0,D,dN,N,dnx)= ns=1.520167, D=2e-6, dN=0.0235072, k0=2*pi/0.6328e-6, N=1.543674, dnx=-2*dN/pi*{1/1e-6*exp(-0.25)+sqrt(pi)/D*ERF(0.5)};

> //步骤2：定义一个积分函数
> //定义函数g，使用了模块变量XX,D，该函数在调用前，XX,D必须预先赋值。
> //这个函数就是第二个公式中的积分函数定义，该积分式对 ξ进行积分，所用到的x用模块变量XX传递过来
> g(ξ::XX,D)=exp(-(ξ^2))/(ξ^2+(XX/D)^2);

> //步骤3：定义一个积分函数
> //以下函数f使用了模块变量ns,k0,D,dN,hg,XX,β，该表达式在调用前，这些变量必须预先赋值
> //这个函数就是第一个公式中的等号前的积分式部分，在计算n(x)中的积分式前，将x通过模块变量XX传递过去，因为QDAGS函数将调用函数g，函数g要求XX预先赋值
> f(x::ns,k0,D,dN,XX,β)= XX=x, sqrt{k0*k0*{ns+dN*x/(pi*D)*exp[-((x/D)^2)]*QDAGS[HFor("g"),-0.5,0.5,0,1e-6,0]}^2-β^2};

> //步骤4：定义解方程所用到的函数
> //定义函数bt。有一个局部变量s，局部变量只在本函数中使用，函数退出时将销毁。使用了模块变量β,hf,k0,N,dnx
> //这个函数就是第一个公式，不过是f(x)=0的形式，这是解方程函数所要求的
> bt(t:s:β,hf,k0,N,dnx)={
> β=t, //将自变量t传给模块变量β，因为QDAGS函数将调用函数f， 函数f要求β预先赋值
> s=sqrt[k0*k0*N*N-t*t],
> QDAGS[HFor("f"),1e-10,1e-6,0,1e-6,0]-pi/4-atan{sqrt[t*t-k0*k0]/s+k0*k0*N*dnx/[2*s^3]}
> };

> //步骤5：定义主函数main，当然也可以是其他名字，省略函数名也是可以的
> main(:a,i,j:k0,ns)=
> {
> a=array[3].free(), //申请工作数组，长度设为3，自动销毁该数组
> i=dhrt[HFor("bt"),k0,k0*ns,0.01*1e6,1e-6,a], //在区间k0～k0*ns中用函数dhrt搜索所有解
> printff{"\r\n搜索到{1,i}个实根：",i},
> j=0,(j<i).while{ //输出并验证所求的解
> printff{"\r\nβ={1,r}, bt({1,r})={2,r}",a(j),bt[a(j)]},
> j++
> }
> };

结果：

> 搜索到1个实根：
> β=15093875.755650569, bt(15093875.755650569)=7.6380607616499674e-008

用下面的代码代替上面的步骤5，无需费力寻找初值，可方便地解此方程，但耗时较长。

> //步骤5：用isolve函数解方程 ，耗时较长
> isolve[HFor("bt"), optmax,5000];

结果（每行前面的数是解，最后一个数是误差，有2组解）：

> -15093875.76549701 5.995204332975845e-015
> 15093875.76549701 5.995204332975845e-015
> 2.

（2）数学模型：

dy/dt=`k*y*z+0.095*b*z`
dz/dt=`-b*z-0.222*z`

实验数据（ti; yi）：

> ti yi
> `0.01` `2.329101563``
> 0.68` `3.851292783``
> 1.1` `4.50093936``
> 1.63` `6.749172247``
> 2.07` `9.112062872``
> 2.67` `9.691657366``
> 3.09` `11.16928013``
> 3.64` `10.91451823``
> 4.65` `16.44279204``
> 5.1` `18.29615885``
> 5.58` `21.63989025``
> 6.11` `25.78611421``
> 6.63` `26.34282633``
> 7.06` `26.50581287``
> 7.62` `27.63951823``
> 8.66` `35.02757626``
> 9.04` `35.5562035``
> 9.63` `36.10393415`

已知z在t=0.01时的初值为5000，求k和b的最优值？

算法分析：用优化库FcOpt中的SimOpt函数（求n维极值的单形调优法 ）求最优参数值k和b。用IMSL库的微分方程组 求解函数IVPRK求理论值，与实验值做比较，理论值与实验值差的平方和最小为最优。

Forcal代码：

> `!using["IMSL","fcopt","math"]; //使用命名空间
> ` //函数定义，用于计算微分方程组中各方程右端函数值，微分方程组求解函数 IVPRK 将调用该函数f。
> //t为自变量，y和z为函数值，dy和dz为右端函数值（即微分方程的值）。
> //该函数被调用时将用到参数k和b，这2个参数正好是拟合参数，用模块变量传递这2个参数。
> `f(t,y,z,dy,dz::k,b)=``
> {
> dy=k*y*z+0.095*b*z,
> dz=-b*z-0.222*z
> };
> ` //目标函数定义，自变量_k,_b为需要优化的参数，需要将这些参数传递给对应的模块变量k,b。
> //模块变量：数组tyArray存放实验数据ti和yi；max为数组tyArray的长度；k,b为优化变量。
> `优化(_k,_b:i,s,IDO,y,z,yy,t,tt:tyArray,max,k,b)=
> {
> k=_k,b=_b, //传递优化变量，函数f中要用到` k,b`
> i=0, s=0, IDO=1, t=tyArray[0,0], y=tyArray[0,1], z=5000,
> (++i<max).while{
> tt=tyArray[i,0], yy=tyArray[i,1],
> IVPRK[&IDO,HFor("f"),&t,tt,1e-6,0,&y,&z],
> s=s+[y-yy]^2
> },
> IVPRK[3,HFor("f"),&t,tt+0.1,1e-6,0,&y,&z],
> s
> };
> 验证(_k,_b:i,IDO,y,z,yy,t,tt:tyArray,max,k,b)=
> {
> k=_k,b=_b,
> printff{"\r\n t y实验值 y模拟值 z模拟值\r\n"},
> i=0, IDO=1, t=tyArray[0,0], y=tyArray[0,1], z=5000,
> (++i<max).while{
> tt=tyArray[i,0], yy=tyArray[i,1],
> IVPRK[&IDO,HFor("f"),&t,tt,1e-6,0,&y,&z],
> printff{"{1,r,18.8}{2,r,18.8}{3,r,18.8}{4,r,18.8}\r\n",tt,yy,y,z},
> i++
> },
> IVPRK[3,HFor("f"),&t,tt+0.1,1e-6,0,&y,&z]
> };
> main(:i,k,b,min:tyArray,max)=
> {
> max=18,` //实验数据组数`
> tyArray=arrayinitns{max,2,` //存放实验数据ti`,yi`

> > `"0.01` `2.329101563``
> > 0.68` `3.851292783``
> > 1.1` `4.50093936``
> > 1.63` `6.749172247``
> > 2.07` `9.112062872``
> > 2.67` `9.691657366``
> > 3.09` `11.16928013``
> > 3.64` `10.91451823``
> > 4.65` `16.44279204``
> > 5.1` `18.29615885``
> > 5.58` `21.63989025``
> > 6.11` `25.78611421``
> > 6.63` `26.34282633``
> > 7.06` `26.50581287``
> > 7.62` `27.63951823``
> > 8.66` `35.02757626``
> > 9.04` `35.5562035``
> > 9.63` `36.10393415"`

> `}.free(),
> i=`SimOpt`[HFor("优化"), optstarter : &k,&b,&min],` //求n维极值的单形调优法`
> printff{"\r\n实际迭代次数={1,i}, k={2,r}, b={3,r}, 目标函数终值={4,r}\r\n",i,k,b,min},
> 验证(k,b)` //验证计算结果`
> };`

结果：

> 实际迭代次数=66, k=1.4083346522762968e-004, b=-1.4674037435955751e-004, 目标函数终值=24.291248590365097
> t y实验值 y模拟值 z模拟值
> 0.68 3.8512928 3.556177 4309.3894
> 1.63 6.7491722 5.9132372 3490.4673
> 2.67 9.6916574 9.2774269 2771.277
> 3.64 10.914518 13.002167 2234.708
> 5.1 18.296159 19.204465 1616.4054
> 6.11 25.786114 23.574479 1291.9246
> 7.06 26.505813 27.533614 1046.4175
> 8.66 35.027576 33.557006 733.74569
> 9.63 36.103934 36.714641 591.67934

9 Forcal程序 [返回页首]

一个Forcal程序由多个模块组成，模块之间通过全局函数、全局变量，或者通过模块命名空间的函数进行通信，这些模块可来自多个文件，也可在同一个文件之中。Forcal的多文件、多模块化编译功能可用于完成大的项目。

10 Forcal扩展动态库 [返回页首]

Forcal软件的构成是高度组合式的。Forcal32W.dll是核心库，在此基础上可设计种类繁多的Forcal扩展动态库，Forcal程序由Forcal32W.dll及零个或任意多个Forcal扩展动态库提供支持。根据完成的功能不同，一个Forcal程序的规模（包括所有支持模块）可以很小（例如只有几百K），也可以非常大。

通过Forcal扩展动态库，可向Forcal注册种类繁多的二级函数，这些函数无缝地集成在Forcal系统之中，协同工作，共同完成复杂的任务。每个Forcal扩展动态库可 （使用C/C++、delphi、Fortran等语言）单独设计，然后根据需要进行加载。 这似乎也是com技术所要达到的目的，但com技术太复杂，函数调用效率似乎也不是太高。dll技术比com技术要简单高效的多，Forcal借助于dll，出色地完成了这个任务。

11 指针和对象 [返回页首]

对象是一种复杂的实体，用复杂的数据结构进行描述。对象通过指针进行标识。Forcal数据类型扩展动态库FcData就是专门来管理指针和对象的。实际上，任何一个Forcal扩展动态库中都可以生成和管理自己的对象。

12 Forcal中的多线程 [返回页首]

核心库Forcal32W.dll中输出的函数大多数只能在单线程中使用。Forcal模块化编译运行库MForcal使多线程程序中使用Forcal成为可能。

13 并行运算设想 [返回页首]

在多核计算机中进行并行运算时，可复制Forcal32W.dll为多个副本Forcal32W1.dll、Forcal32W2.dll、... ...，同时加载Forcal32W.dll及各个副本，Forcal32W.dll及各个副本都有自己的地址空间，可独立运行互不影响，这样可实现并行计算。

14 中文编程 [返回页首]

中文编程是许多人的梦想，Forcal使用Unicode字符串作为默认字符串，可很好地实现中文编程。Forcal标识符可全部使用中文，尽管本软件包中的Forcal扩展动态库提供的都是英文函数，但你自己来设计这些库时，完全可以全部使用中文函数，或者提供中英文两套函数名。

例子：

> 两数加(加数1,加数2)=加数1+加数2;
> 两数加(2,3);

中文编程或许使普通人学会编程成为可能。

15 其他问题 [返回页首]

还有许多Forcal技术问题本文没有讲到，如：Forcal标识符、常量、字符串、静态数组、递归调用、命名空间、类成员运算符、类、... ...。如果您对Forcal感兴趣，可以进一步系统地了解Forcal。

版权所有© Forcal程序设计 2002-2011，保留所有权利
E-mail: forcal@sina.com QQ：630715621
最近更新: 2011年02月06日