欢迎访问 Forcal程序设计

FORCAL新手参考

目  录

概述  
Forcal中的表达式(函数) 表达式、函数、变量、不同类型的函数调用
Forcal中的模块 模块、全局函数、私有函数
Forcal中的变量 自变量、动态变量、静态变量、模块变量和全局变量释疑
函数的传值调用和传址调用 传址调用可返回多个参数
用表达式作为函数的参数 函数句柄的使用
标准输入输出 printff和printf函数
工程实例 演示Forcal的功能
Forcal程序 Forcal程序由来自多个文件的若干个模块组成
10 Forcal扩展动态库 Forcal扩展动态库中的函数可无缝地紧密合作完成一个复杂任务
11 指针和对象 这是复杂的东西,一带而过
12 Forcal中的多线程 通过MForcal实现多线程
13 中文编程 这是很多人的梦想

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)计算下图中的三重积分

    方法1:用计算多重积分的高斯方法

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

    方法2:用单重积分和二重积分构造三重积分:第一层用单重积分函数QDAGS,第二、三层用二重积分函数TWODQ

fyz(y,z::x)=x*y*z; //x是模块变量
gyz(y)=0;         
//积分下限
hyz(y::x)=1-x-y;  
//积分上限
//以下函数f中,x=_x的意思是将自变量_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

    方法3:用单重积分和二重积分构造三重积分:第一、二层用二重积分函数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维极值的单形调优法 )求最优参数值kb。用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 中文编程 [返回页首]

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

    例子:

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

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


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