FORCAL新手参考
FORCAL新手参考
目 录
1 概述 | |
2 Forcal中的表达式(函数) | 表达式、函数、变量、不同类型的函数调用 |
3 Forcal中的模块 | 模块、全局函数、私有函数 |
4 Forcal中的变量 | 自变量、动态变量、静态变量、模块变量和全局变量释疑 |
5 函数的传值调用和传址调用 | 传址调用可返回多个参数 |
6 用表达式作为函数的参数 | 函数句柄的使用 |
7 标准输入输出 | printff和printf函数 |
8 工程实例 | 演示Forcal的功能 |
9 Forcal程序 | Forcal程序由来自多个文件的若干个模块组成 |
10Forcal扩展动态库 | Forcal扩展动态库中的函数可无缝地紧密合作完成一个复杂任务 |
11 指针和对象 | 这是复杂的东西,一带而过 |
12 Forcal中的多线程 | 通过MForcal实现多线程 |
13 并行运算设想 | 就是一个设想 |
14 中文编程 | 这是很多人的梦想 |
15 其他问题 | 需要进一步系统地了解Forcal |
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)=x+1; //定义一元函数;
g(x)=sin[x]+0.8; //定义一元函数;
SimpIntegrate(0,2,0.0001,"f"); //变步长辛卜生一元积分,对函数f从0到2进行积分,精度为0.0001;
SimpIntegrate(1,2,0.0001,"g"); //变步长辛卜生一元积分,对函数g从1到2进行积分,精度为0.0001。
(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的一个函数。
一般,在函数的说明中会指定需传递的表达式参数的类型,或者传递字符串形式的表达式名称,或者传递表达式的句柄。
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}; //输出数据
以下工程实例来自网上,例子有所修改,仅仅说明Forcal是如何解决这类复杂问题的,这些代码体现了Forcal代码的可读性及可写性。
这些例子用到了三个库:核心库forcal32w.dll、Forcal数据类型扩展库FcData32W.dll和徐士良算法库XSLSF32W.dll。核心库是必然要使用的,后面两个是Forcal扩展动态库。三个库协同工作,共同完成计算任务。注意徐士良算法库XSLSF32W.dll中提供了命名空间“XSLSF”,用using函数启用该命名空间可简化函数的输入。
这些例子的运算量较大,可能将运行几秒到十几秒的时间。
(1)公式如图所示:
已知k0=2*pi/0.6328e-6,N=1.543674,求β值?
算法分析:公式看起来似乎有些复杂,但只有一个变量,解单变量方程即可,采用求非线性方程实根的对分法dhrt函数解这个方程。注意最后一个式子实际上是一个常数;第二个式子中包含了一个积分项,而第二个式子包含在第一个式子等号前的积分式中,故需要做两次积分,这里采用龙贝格求积法romb;第一个式子等号后比较简单,其中用到了最后一个式子。问题的难点在于解方程时的区间选择,分析从略,选择在区间k0~k0*ns(ns的意义见代码中的数据)中搜索可解此方程。
Forcal代码:
//这里所有表达式属于同一个模块
//本例子使用模块变量传递参数,模块变量在本模块内有相同的地址,因而可在同一个模块的表达式之间传递参数
//在这里不推荐使用全局变量,全局变量可在所有模块中的表达式之间传递参数,如果使用全局变量,可能影响到其他模块中的表达式//步骤1:定义一些常量
//这个表达式用模块变量传递一些常量,这些常量用在下面的表达式中,注意dnx就是最后一个式子
(::ns,k0,D,dN,pi,N,dnx)= ns=1.520167,D=2e-6,dN=0.0235072,pi=3.14159265358979,k0=2*pi/0.6328e-6,N=1.543674,
dnx=-2*dN/pi*{1/1e-6*exp(-0.25)+sqrt(pi)/D*XSLSF::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,pi,hg,XX,β,该表达式在调用前,这些变量必须预先赋值。
//这个函数就是第一个公式中的等号前的积分式部分,在计算n(x)中的积分式前,将x通过模块变量XX传递过去
f(x::ns,k0,D,dN,pi,hg,XX,β)= XX=x, sqrt{k0*k0*{ns+dN*x/(pi*D)*exp[-((x/D)^2)]*XSLSF::romb[hg,-0.5,0.5,1e-9]}^2-β^2};//步骤4:定义对分法解方程所用到的函数
//定义函数bt。有一个局部变量s,局部变量只在本函数中使用,函数退出时将销毁。使用 了模块变量β,hf,pi,k0,N,dnx
//这个函数就是第一个公式,不过是f(x)=0的形式,这是求非线性方程实根的对分法dhrt函数所要求的
bt(t:s:β,hf,pi,k0,N,dnx)={
β=t, //将自变量t传给模块变量β,因为romb函数将通过句柄hf调用函数f,f要求β预先赋值
s=sqrt[k0*k0*N*N-t*t],
XSLSF::romb[hf,1e-10,1e-6,1e-9]-pi/4-atan{sqrt[t*t-k0*k0]/s+k0*k0*N*dnx/[2*s^3]}
};//步骤5:定义一个整数表达式,用于输出一维数组,看不懂不要紧,只要知道它的功能就可以了
i: OutVector(p:k,i)= k=FCDLen(p),printff{"/r/n"},i=0,(i<k).while{printff{"{1,r,14.6}",get[p,i]},i++},printff{"/r/n"};//步骤6:定义主函数main,当然也可以是其他名字,省略函数名也是可以的
main(:a,i,j:hg,hf,k0,N,ns)=
{
hg=HFor("g"), //预先获得函数g的句柄,存放到模块变量hg。函数f中的XSLSF::romb函数需要对函数g做积分运算,需要函数g的句柄hg
hf=HFor("f"), //预先获得函数f的句柄,存放到模块变量hf。函数bt中的XSLSF::romb函数需要对函数f做积分运算,需要函数f的句柄hf
a=new[rtoi(real_s),rtoi(6)], //申请工作数组,长度设为6
i=XSLSF::dhrt[HFor("bt"),k0,k0*ns,0.1*1e6,1e-6,a], //在区间k0~k0*ns中用函数dhrt搜索所有解
printff{"/r/n搜索到{1,r}个实根,即下列数组的前{1,r}个值:",i},
OutVector[a], //输出工作数组的所有值
j=0,(j<i).while{ //验证所求的解
printff{"验证函数bt的值({1,r})={2,r}/r/n",a.get[rtoi(j)],bt[a.get[rtoi(j)]]},
j++
},
delete[a] //销毁数组a
};
(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.683.851292783
1.14.50093936
1.636.749172247
2.079.112062872
2.679.691657366
3.0911.16928013
3.6410.91451823
4.6516.44279204
5.118.29615885
5.5821.63989025
6.1125.78611421
6.6326.34282633
7.0626.50581287
7.6227.63951823
8.6635.02757626
9.0435.5562035
9.6336.10393415
已知z在t=0.01时的初值为5000,求k和b的最优值?
算法分析:用求n维极值的单形调优法求最优参数值k和b。用连分式法对微分方程组积分一步函数pbs1求理论值,与实验值做比较,理论值与实验值差的平方和最小为最优。
Forcal代码:
!using["XSLSF"]; //使用命名空间XSLSF
//函数定义,用于计算微分方程组中各方程右端函数值,连分式法对微分方程组积分一步函数pbs1将调用该函数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
};
//t1,t2为积分的起点和终点。
//h,s为自动变量。
//模块变量:hf为函数f的句柄,要预先获得该句柄;Array为工作数组;step为积分步长;eps为积分精度。积分(t1,t2:h,s:hf,Array,step,eps)=
{
s=ceil[(t2-t1)/step], //计算积分步数
h=(t2-t1)/s, //重新计算积分步长
{ pbs1[hf,t1,Array,h,eps], //对微分方程组积分一步
t1=t1+h //积分步长增加
}.until[abs(t1-t2)<h/2] //连续积分至t2
};//目标函数定义,自变量_k,_b为需要优化的参数,需要将这些参数传递给对应的模块变量k,b。
//模块变量:Array为工作数组;数组tArray存放实验数据ti;数组yArray存放实验数据yi;max为数组tArray和yArray的长度;k,b为优化变量。优化(_k,_b:i,s,y,z,yy,t1,t2:Array,tArray,yArray,max,k,b)=
k,b
{
k=_k,b=_b, //传递优化变量,函数f中要用到//模块变量hf保存函数f的句柄,预先用函数HFor获得该句柄
Array.setra(0,2.329101563,5000), //设置积分初值,通过模块变量Array传递,Array是一个数组
i=1,s=0,t1=0.01,
(i<max).while{
tArray.getra(i,&t2), yArray.getra(i,&yy),
积分(&t1,t2), //从t1积分到t2
Array.getra(0,&y,z), //从数组Array获得t2时的积分值
s=s+[y-yy]^2, //累加理论值与实验值差的平方
i++
},
s
};
验证(_k,_b:i,y,z,yy,t1,t2:Array,tArray,yArray,max,k,b)=
{
k=_k,b=_b,
printff{"/r/n t y实验值 y模拟值 z模拟值/r/n"},
Array.setra(0,2.329101563,5000), //设置积分初值,通过模块变量Array传递,Array是一个数组
i=1,t1=0.01,
(i<max).while{
tArray.getra(i,&t2), yArray.getra(i,&yy),
积分(&t1,t2), //从t1积分到t2
Array.getra(0,&y,&z), //从数组Array获得t2时的积分值
printff{"{1,r,18.8}{2,r,18.8}{3,r,18.8}{4,r,18.8}/r/n",t1,yy,y,z},
i++
}
};
main(:x,xx,g,d,u,v,k,i,t1,t2,t3,_eps:Array,tArray,yArray,max,hf,step,eps)=
{
hf=HFor("f"),step=0.01,eps=1e-6,
//积分步长step要合适,积分精度eps越小越精确,用于对微分方程组积分一步函数pbs1//申请工作数组
Array=new[rtoi(real_s),rtoi(2*15)],//实验数据组数
max=18,//存放实验数据ti
tArray=new{rtoi(real_s),rtoi(max),rtoi(EndType),//存放实验数据yi
0.01,
0.68,
1.1,
1.63,
2.07,
2.67,
3.09,
3.64,
4.65,
5.1,
5.58,
6.11,
6.63,
7.06,
7.62,
8.66,
9.04,
9.63
},
yArray=new{rtoi(real_s),rtoi(max),rtoi(EndType),//申请工作数组
2.329101563,
3.851292783,
4.50093936,
6.749172247,
9.112062872,
9.691657366,
11.16928013,
10.91451823,
16.44279204,
18.29615885,
21.63989025,
25.78611421,
26.34282633,
26.50581287,
27.63951823,
35.02757626,
35.5562035,
36.10393415
},
x=new[rtoi(real_s),rtoi(3)],xx=new[rtoi(real_s),rtoi(2),rtoi(3)],
//申请工作数组g=new[rtoi(real_s),rtoi(3)],
//申请工作数组//变换d、u、v进一步求解,k为允许的最大迭代次数
_eps=1e-100, d=550,u=1.6,v=0.4,k=500,i=jsim[HFor("优化"),d,u,v,x,_eps,k,xx,g],
//求n维极值的单形调优法//获得最优参数值及目标终值
x.getra(0,&t1,&t2,&t3),//验证计算结果
printff{"/r/n实际迭代次数={1,i}, k={2,r}, b={3,r}, 目标函数终值={4,r}/r/n",i,t1,t2,t3},
验证(t1,t2),
delete[Array],delete[tArray],delete[yArray],delete[x],delete[xx],delete[g]
};
一个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,出色地完成了这个任务。
对象是一种复杂的实体,用复杂的数据结构进行描述。对象通过指针进行标识。Forcal数据类型扩展动态库FcData就是专门来管理指针和对象的。实际上,任何一个Forcal扩展动态库中都可以生成和管理自己的对象。
12 Forcal中的多线程 [返回页首]
核心库Forcal32W.dll中输出的函数大多数只能在单线程中使用。Forcal模块化编译运行库MForcal使多线程程序中使用Forcal成为可能。
在多核计算机中进行并行运算时,可复制Forcal32W.dll为多个副本Forcal32W1.dll、Forcal32W2.dll、... ...,同时加载Forcal32W.dll及各个副本,Forcal32W.dll及各个副本都有自己的地址空间,可独立运行互不影响,这样可实现并行计算。
中文编程是许多人的梦想,Forcal使用Unicode字符串作为默认字符串,可很好地实现中文编程。Forcal标识符可全部使用中文,尽管本软件包中的Forcal扩展动态库提供的都是英文函数,但你自己来设计这些库时,完全可以全部使用中文函数,或者提供中英文两套函数名。
例子:
两数加(加数1,加数2)=加数1+加数2;
两数加(2,3);
中文编程或许使普通人学会编程成为可能。
还有许多Forcal技术问题本文没有讲到,如:Forcal标识符、常量、字符串、静态数组、递归调用、命名空间、类成员运算符、类、... ...。如果您对Forcal感兴趣,可以进一步系统地了解Forcal。
版权所有© Forcal程序设计 2002-2010,保留所有权利
E-mail: [email protected] QQ:630715621
最近更新: <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%Y年%m月%d日" startspan -->2010年01月23日<!--webbot bot="Timestamp" i-checksum="1304" endspan -->