FORCAL新手参考

欢迎访问 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

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)=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的一个函数。

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

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 工程实例 [返回页首]

以下工程实例来自网上,例子有所修改,仅仅说明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.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的最优值?

算法分析:用求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=_k,b=_b, //传递优化变量,函数f中要用到
k,b

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"),
//模块变量hf保存函数f的句柄,预先用函数HFor获得该句柄
step=0.01,eps=1e-6, //积分步长step要合适,积分精度eps越小越精确,用于对微分方程组积分一步函数pbs1
Array=new[rtoi(real_s),rtoi(2*15)],
//申请工作数组

max=18,
//实验数据组数
tArray=new{rtoi(real_s),rtoi(max),rtoi(EndType),
//存放实验数据ti
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),
//存放实验数据yi
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)], //申请工作数组
_eps=1e-100, d=550,u=1.6,v=0.4,k=500,
//变换d、u、v进一步求解,k为允许的最大迭代次数
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]
};

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-2010,保留所有权利
E-mail:
forcal@sina.com
QQ:630715621
最近更新: <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%Y年%m月%d日" startspan -->2010年01月23日<!--webbot bot="Timestamp" i-checksum="1304" endspan -->

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值