FORCAL速成教程

欢迎访问 Forcal数学软件

FORCAL速成教程

目 录 [页首]

1 准备开始
2 FORCAL表达式
3 源程序格式及执行顺序
4 模块
5 运算符及优先级
6 字符串
7 流程控制
8 轻松一下
9 关于关键字
10 常量
11 函数的传值调用和传址调用
12 表达式句柄
13 用表达式作为函数的参数
14
关于递归调用

15 类成员运算符(函数参数运算符)
16 FORCAL函数

17 模块命名空间

18 二级函数命名空间
19 标识符解释规则
20
Forcal数据类型扩展动态库FcData简介
21 类模块简介
22 主要技术指标

正文

1 准备开始 [返回页首] [返回目录]

本文是为有编程经验的朋友准备的,使他们用几个小时甚至更短的时间,能够迅速地掌握Forcal,这并非难事,因为Forcal的语法构成是如此的简单。如果你已从天空软件站华军软件园或者作者网站下载了演示程序OpenFc(按readme.txt的步骤进行设置),我们就开始吧。

下面是很快就可以熟悉的内容:

1)源代码注释方法与C++语言相似,即:每行两个//后的内容为注释内容;在一行内或者多行之间/*...*/之间的内容为注释内容。

2)源文件中可以有多个表达式,表达式之间用分号“;”隔开。

3)FORCAL表达式有三种,即整数表达式、实数表达式和复数表达式。为便于区分,以i:开头的表达式作为整数表达式,以r:开头的表达式作为实数表达式,以c:开头的表达式作为复数表达式,缺省为实数表达式。如下例:

i:2.2+3.3; //以i:开头是整数表达式;
c:sin(2.2+3.3i); //以c:开头是复数表达式;
2.2+3.3; //这是实数表达式。

4)FORCAL标识符与C++语言相似,如:first last Addr1 top _of_file name23 _temp a23e3 MyVar

5)Forcal中没有关键字。Forcal中只有常量、变量和函数。但有些符号常量、变量名或函数名使用很频繁,可当作“关键字”来使用。

在熟悉Forcal常量、变量和函数的基础上,深刻理解Forcal表达式和模块的定义,就基本上掌握了Forcal。

Forcal代码中,冒号“:”是一个使用很灵活的符号,在不同的位置有不同的用途,它被用来定义变量、分隔语句或参数、链接模块命名空间及函数名、区分表达式的类型、定义表达式的编译特性等,一定要注意仔细的区分。阿弥陀佛,但愿它不会给你带来麻烦。

祝你能愉快地阅读本文

2 FORCAL表达式 [返回页首] [返回目录]

你能区别动态变量、静态变量、模块变量和全局变量吗?若能区别,真是太好了。似乎提供这些变量的动态语言并不多,但FORCAL提供了这些变量,因而Forcal可以支持大型的项目。Forcal中定义这些变量的方式也很特别:使用冒号、staticcommon

以下是FORCAL表达式的完整定义,这一部分非常重要,要仔细阅读。

F(a,b : x,y, static,u,free,v : s,t, common,w)=
{
x=1,y=2,[x+y,x*y],(x-y):
a+b+x+y+
static+u+s+t+common+v

}

F是表达式的名字,可以缺省。有名字的表达式即一个普通的函数,可以在其他表达式中调用该函数。一般表达式的名字是唯一的(模块内表达式的名字除外,模块的定义将在后面介绍)。

如果定义了表达式名字,表达式名字后必须跟一对小括号(只能用小括号),用来定义变量。变量个数可以为零,也可以有任意多个。有多个变量时,变量间以逗号或冒号分隔。

用冒号隔开的变量,从前往后依次为自变量、动态变量、静态变量、模块变量和全局变量,即第一个冒号前为自变量,两个冒号之间为动态变量和静态变量,第二个冒号后为模块变量和全局变量。两个冒号之间用关键字static分隔动态变量和静态变量,static之前为动态变量,static及以后变量均为静态变量,关键字static只能用在两个冒号之间。第二个冒号后用关键字common分隔模块变量和全局变量,common之前为模块变量,common及以后变量均为全局变量,关键字common只能用在第二个冒号后。

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

如果定义了静态变量free,Forcal在销毁表达式前将自动设置free为1,然后自动执行表达式。

所有变量以及冒号均可缺省。

在这个例子中,a和b是自变量,x和y是动态变量,static、u、free和v是静态变量,s和t是模块变量,common和w是全局变量。

即便表达式没有名字,也可以用一对小括号(只能用小括号)来定义变量,这时,小括号必须位于表达式的开头。

如果用小括号定义了变量,小括号后必须跟一个等号,该等号标志表达式可执行部分的开始,等号及等号后的可执行部分均不可缺省。

如果表达式中的变量名与一个常量名相同,则常量名被忽略。

表达式中可以不定义变量,这时,表达式中只有可执行部分,称常量表达式,或者称无参表达式。

表达式的可执行部分任何情况下都不可缺省。

表达式的可执行部分由多个语句组成,多个语句之间用逗号或冒号分隔,多个语句可以放在一对括号内。可执行部分中可以使用三对括号( )、[ ]和{ },括号必须成对使用。在FORCAL中,括号是一种运算符,具有一个值,即该括号内最后一个语句的值。表达式总是返回(最外层括号中)最后一个语句的值。另外,最外层的括号不是必须的,但表达式若有逗号或冒号隔开的多个语句,有了最外层的括号后,表达式更易读。

以下都是合法的表达式定义的例子:

2; //常量(无参)表达式(函数);
()=2;
//常量(无参)表达式(函数);
(::)=2; //常量(无参)表达式(函数);

A()=2;
//常量(无参)表达式(函数),但定义了函数名字,在其它表达式中可以调用该函数;
A(::)=2; //常量(无参)表达式(函数),但定义了函数名字,在其它表达式中可以调用该函数;

B(x)=2;
//有一个自变量的表达式(函数);
(x)=23;
//有一个自变量的表达式(函数),但没有函数名,不能在其他表达式中调用该函数;
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(x,y)=x+y; //函数定义;
2+F[2,3]+5;
//函数调用;

3 源程序格式及执行顺序 [返回页首] [返回目录]

OpenFc中使用MForcal源程序格式。MForcal可以对Forcal源程序进行模块化编译,是一个极为重要的Forcal扩展动态库。

在源文件中,可以有多个FORCAL表达式,表达式之间用分号“;”隔开。
由于FORCAL数学表达式有三种,即整数表达式、实数表达式和复数表达式。为便于区分,MForcal将以i:开头的表达式作为整数表达式,以r:开头的表达式作为实数表达式,以c:开头的表达式作为复数表达式,缺省为实数表达式。缺省的表达式类型可以重新设置:若表达式以integer:开头,表示自该表达式开始,后面的缺省表达式为整数表达式;若表达式以real:开头,表示自该表达式开始,后面的缺省表达式为实数表达式;若表达式以complex:开头,表示自该表达式开始,后面的缺省表达式为复数表达式。
同时,在源文件中可以进行注释,注释方法与C++语言相似,即:每行两个//后的内容为注释内容;在一行内或者多行之间/*...*/之间的内容为注释内容。注释不是源程序的有效部分,但可以使程序更易读。

由于表达式有些带有参数,有些不带参数,对于有参数的表达式只进行编译,不进行计算。
MForcal只顺序执行不带参数的表达式。
但是,如果表达式以冒号“ : ”开头,则无论是有参表达式还是无参表达式,都是只编译,不执行,格式如下:

i:: 2+3; //整数无参表达式,只编译,不执行;
i:: f1()=2+3; //整数无参表达式,只编译,不执行;
c:: 2+3i; //复数无参表达式,只编译,不执行;
c:: f2()=2+3i; //复数无参表达式,只编译,不执行;
: 2+3; //实数无参表达式,只编译,不执行;
: f3()=2+3; //实数无参表达式,只编译,不执行。

无参表达式f1、f2和f3可以在其他可执行的表达式中被调用。

另外,如果无参表达式以感叹号“ ! ”开头,则编译后立即执行,且以后执行模块时不再自动执行。格式如下:

i:! 2+3; //整数无参表达式,编译成功,立即执行,以后不再自动执行;
i:! f1()=2+3; //整数无参表达式,编译成功,立即执行,以后不再自动执行;
c:! 2+3i; //复数无参表达式,编译成功,立即执行,以后不再自动执行;
c:! f2()=2+3i; //复数无参表达式,编译成功,立即执行,以后不再自动执行;
! 2+3; //实数无参表达式,编译成功,立即执行,以后不再自动执行;
! f3()=2+3; //实数无参表达式,编译成功,立即执行,以后不再自动执行;

无参表达式f1、f2和f3可以在其他可执行的表达式中被调用。

现在来完整地了解一下MForcal的编译指令,这是非常必要的。在MForcal中使用的#MODULE#、#END#、#USE#、integer:、real:、complex:、~、i:、r:、c:、:、!等称为编译指令,用以确定一个表达式的类型、所在模块、是否私有函数等属性。这些编译指令必须位于表达式的开头,有些指令能同时使用,有些指令不能同时使用,并且在使用时有一定的次序,按先后顺序依次为:

1)编译指令#MODULE#、#END#、#USE#、integer:、real:和complex:之间没有先后顺序,可混合使用,但这些指令必须在表达式的最前面,一般单独成行。

2)~表示该表达式是一个全局表达式,否则是私有表达式。

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

4)编译指令:、!不能混合使用,只能使用其中的一个。:表示该表达式只编译,不执行;!表示该表达式编译后立即执行,但以后执行模块时不再自动执行。

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

到这里,冒号已经出现好几次了。但愿它没有给你造成理解上的麻烦。回过头去看看它都干了些什么,简单地总结一下再往下读会更好。

4 模块 [返回页首] [返回目录]

FORCAL支持表达式的模块化编译。一个模块由一个或多个表达式组成。

同一模块中的函数(即有名字的表达式)包括私有函数和公有函数(全局函数)。私有函数只能被本模块的函数所访问(即调用),在其他模块中是不可见的;公有函数或全局函数能被任何一个函数所访问。如果本模块中的一个私有函数与其他模块的一个全局函数重名,将优先调用本模块中的私有函数。

一个源程序文件可包含一个或多个模块,我们将这些模块区分为主模块和子模块(仅仅是区分,除此之外,主模块和子模块没有任何区别)。#MODULE##END#之间的表达式定义为一个子模块,否则是主模块中的表达式,子模块之间不能嵌套定义。注意#MODULE##END#必须位于表达式的开头。在模块中,以~开头的表达式被编译为全局表达式,能被其他模块访问到,其余的表达式均被编译为私有表达式,其他模块无法访问。在源程序的任何地方,可用指令#USE#调用另一个模块源文件(看清楚了,是模块源文件,有时,我也简称它为模块)。

模块源文件的格式如下:

//单行注释:模块源文件名:myModule
/*
多行注释:#MODULE#和#END#之间的表达式定义为一个子模块;
多行注释:以~开头的表达式为全局表达式,可被其他模块的表达式所访问;
多行注释:不以~开头的表达式为私有表达式,只能被该模块的表达式所访问。
*/
#MODULE# //定义一个子模块;

i:a(x)=10+x;
//私有表达式,只能被该模块的表达式所访问;
c:!b()=100+100i;
//私有表达式,只能被该模块的表达式所访问,该表达式是在编译时执行的;
~_c(x)=x-5;
//全局表达式,任意模块包括本模块均可访问;
~_f(x)=a(x)+b();
//全局表达式,任意模块包括本模块均可访问;
~i:g(x)=a(x)+_c(x);
//全局表达式,任意模块包括本模块均可访问;
#USE# Module1; //使用模块源文件Module1,必须存在该模块文件;
~_ff(5)+_gg(6);
//函数_ff()和_gg()在模块源文件Module1中定义;
#END# //子模块定义结束,可缺省;

#MODULE#
//定义一个子模块;
i:a(x)=10-x; //私有表达式,只能被该模块的表达式所访问;

~ff(x)=a(x);
//全局表达式,任意模块包括本模块均可访问;
#END#
//子模块定义结束,不可缺省;
_f(1);
//主模块中的表达式。
ff(1);
//主模块中的表达式。
integer: //设置缺省表达式为整数表达式;
2.2+3; //这是整数数表达式;

在其他模块(源文件)中使用该模块(源文件)的格式如下:

#USE# myModule; //关键字USE必须为大写,myModule是模块(源文件)名称;
_f(2)+g(3); //调用myModule模块中定义的函数;

模块有什么用呢?举个简单的例子:在OpenFc中,运行“命令->函数图像->随机图形”,将执行“随机图形.afc”,这是一个Forcal源文件,将作为一个模块被编译执行。接下来,你可以继续使用OpenFc编译运行其他程序,没有任何影响。“随机图形.afc”中没有使用全局变量,没有输出,也没有使用任何一个全局函数,是完全封闭的一个模块。

哦,你一定想到了,可以使用全局变量或全局函数进行模块间的通信,确实是这样,见下面的例子。

#MODULE# //定义一个子模块,模块名为Module1;
set(x::xx)= xx=x;
//私有函数,设置模块变量xx的值;
get(::xx)= xx;
//私有函数,获得模块变量xx的值;
~Module1_set(x)= set(x);
//全局函数,调用私有函数set;
~Module1_get()= get();
//全局函数,调用私有函数get;
~Module1_SetA(x::common,A)= A=x;
//全局函数,设置全局变量A的值;
#END#
//子模块定义结束,可缺省;

#MODULE#
//定义一个子模块,模块名为Module2;
set(x::xx)= xx=x;
//私有函数,设置模块变量xx的值;
get(::xx)= xx;
//私有函数,获得模块变量xx的值;
~Module2_set(x)= set(x);
//全局函数,调用私有函数set;
~Module2_get()= get();
//全局函数,调用私有函数get;
~Module2_GetA(::common,A)= A;
//全局函数,获得全局变量A的值;
#END#
//子模块定义结束,不可缺省;

set(x::xx)= xx=x;
//主模块中的私有函数,设置模块变量xx的值。
get(::xx)= xx;
//主模块中的私有函数,获得模块变量xx的值。
set(33);
//主模块中的私有表达式,调用主模块中的私有函数set。
GetA(::common,A)= A;
//主模块中的私有表达式,获得全局变量A的值。

Module1_set(11);
//主模块中的私有表达式,全局函数调用。
Module2_set(22);
//主模块中的私有表达式,全局函数调用。
Module1_get();
//主模块中的私有表达式,全局函数调用。
Module2_get();
//主模块中的私有表达式,全局函数调用。

get();
//主模块中的私有表达式,调用主模块中的私有函数get。

Module1_SetA(55); //主模块中的私有表达式,全局函数调用。

Module2_GetA();
//主模块中的私有表达式,全局函数调用。
GetA();
//主模块中的私有表达式,调用主模块中的私有函数GetA。

前面讲过,只有无参表达式才有输出结果,看一下这些结果,对号入座,同时找出哪个表达式输出了未初始化变量的随机值。

在这个例子中,我们看到主模块及两个子模块中的模块变量xx、私有函数set及get是互不影响的,而全局变量A是公用的。

子模块在哪一个模块源文件中是无关紧要的,这个你自己测试吧。

5 运算符及优先级 [返回页首] [返回目录]

FORCAL中的运算符有算术运算符、关系运算符、逻辑运算符、逗号或冒号运算符、括号运算符等。

FORCAL中共有八个算术运算符,即:+(加或正)、-(减或负)、*(乘)、/(除)、%(求模)、^(乘方)、++(自增)、--(自减)。其中%(求模)仅在整数表达式中使用;^(乘方)仅在实数和复数表达式中使用。
注意:单目运算符-(负)与双目运算符^(乘方)需用括号区分计算的先后顺序。例如:

(-2)^2; //返回值为4;
-(2^2);
//返回值为-4;
-2^2;
//编译出错;
-(2+3)^2; //编译出错;
-sin(2+3)^2;//编译出错;

关系运算是对两个值的大小进行比较,返回一个逻辑值。逻辑值只有两个:逻辑真和逻辑假。在FORCAL中用0表示逻辑假,其他任何非0值表示逻辑真。例如:

3>2; //返回逻辑真;
2>3; //返回逻辑假;

关系运算符共有6个:>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、==(等于)、!=(不等于)。

逻辑值之间的运算称逻辑运算,逻辑运算的结果仍然是一个逻辑值。有三个逻辑运算符:&(逻辑与)、|(逻辑或)、!(逻辑非)。

表达式中如果有多个语句,可以用逗号或冒号进行分隔,FORCAL将按从左到右的顺序计算各个语句,并返回最后一个语句的值。也可以将多个用逗号或冒号分隔的语句放在一个括号内,FORCAL也将按从左到右的顺序计算各个语句,并返回最后一个语句的值。例如:

(:x)= x=2,x=5,x; //返回值为5;
(:x)={x=2,x=5,x};
//返回值为5;
(:x,y)={x=2,y=5,x=[x=x+1,y=y+1,x+y]:x};
//返回值为9;
(:x,y)={x=2,y=5,[x=x+1,y=y+1:x+y]};
//返回值为9;

虽然逗号或冒号运算符是通用的,但逗号的用法很常见。冒号运算符仅用在一些特殊场合,例如分隔一些不同类别的变量。本文中除了对冒号运算符进行说明外,只使用逗号运算符。

FORCAL中的运算符及优先级如表5-1所示,在表中,同一行中运算符优先级相同,不同行中运算符的优先级从上往下依次降低。

表5-1:FORCAL运算符及优先级

运 算 符

说 明

( )、[ ]、{ }括号运算符,返回(最外层括号中)最后一个语句的值
::命名空间成员访问符
.类成员运算符(函数参数运算符)
++、--后置单目运算符(自增、自减)
!、+、-、++、--前置单目运算符(非、正、负、自增、自减)
^算术运算符(乘方)
*、/、%算术运算符(乘、除、求模)
+、-算术运算符(加、减)
>、>=、<、<=、==、!=关系运算符(大于、大于等于、小于、小于等于、等于、不等于)
&逻辑与
|逻辑或
=赋值运算符,对变量进行赋值
,、:逗号、冒号运算符

注意在FORCAL中,通常运算符不能连用,例如 !-2 是错误的,应当添加括号分隔开各个运算符,即应当写成 !(-2) 形式。

还有一个FORCAL运算符在表5-1中没有列出:&(取变量的地址)。取地址运算符&只能用于(被逗号或冒号隔开的)单独的变量,与其他运算符不发生任何关系,所以这个运算符在表5-1中没有列出,其用法我们将在下面详细介绍。

类成员运算符(函数参数运算符)“.”及命名空间成员访问符“::”也将在下面详细介绍。

6 字符串 [返回页首] [返回目录]

在FORCAL中,用两个双引号定义一个字符串,即"..."表示一个字符串。在FORCAL字符串中,还可以用反斜杠转义字符输入一些特殊字符,见表6-1。

表6-1 反斜杠转义字符

//反斜杠“/”
/"双引号“"”
/NNN任意字符,NNN是该字符ASCII码的10进制值,NNN必须是三个数字,例如ASCII码为9的字符,应写成/009
/xNN任意字符,NN是该字符ASCII码的16进制值,NN必须是两个16进制数字 (字母A到F以单个数字的形式表示10进制数的10到15),例如ASCII码为11的字符,应写成“/x0B”

除了表中定义的之外,FORCAL没有定义其它的转义字符(“/”和其他字符的组合都是非法的)。
可以看出,反斜杠“/”和双引号“"”只能通过转义字符输入。

例如:

OutNStr("字符串!!!"); //用OutNStr函数输出字符串;

FORCAL在编译表达式时,将同一个表达式中的字符串连续存放,同时将每一个字符串都转换成一个整数,该整数即该字符串的首字符所在的位置。因而,两个相邻字符串的差即前一个字符串的长度的负值。

例如:

(:i,j)={i="Hello Forcal !",j="end",OutNStr(i),j-i}; //该函数返回第一个字符串的长度;

Forcal字符串有二种类型:Forcal静态字符串和Forcal动态字符串。Forcal静态字符串即在Forcal表达式中定义的字符串,长度是固定的,不能随意进行销毁(但Forcal静态字符串也并非一成不变);Forcal动态字符串由Forcal扩展动态库FcData进行管理,可由函数new进行申请,由函数delete进行销毁。

Forcal静态字符串又分为近程静态字符串和远程静态字符串。近程静态字符串仅用一个整数(该整数即字符串在表达式中的地址)就可以标识,远程静态字符串需由表达式句柄和一个整数进行标识。函数OutFStr可以输出远程静态字符串,如下例:

bb()="adfg"; //定义表达式bb,函数返回一个Forcal静态字符串地址;
OutFStr[2,HFor("bb",2),bb()];
//用函数OutFStr输出表达式bb中的字符串,函数HFor用于获得表达式bb的句柄。

由此可见,可在自定义表达式之间通过表达式句柄和字符串地址传递Forcal远程静态字符串。

通常,涉及字符串的规范的二级函数命名将按以下约定:函数名中含有NStr(Forcal近程静态字符串),表示需要一个Forcal静态字符串地址参数;函数名中含有FStr(Forcal远程静态字符串),表示需要表达式句柄和一个Forcal静态字符串地址参数;函数名中含有Str(Forcal动态字符串),表示需要一个FcData字符串指针参数。

7 流程控制 [返回页首] [返回目录]

FORCAL的流程控制仅仅依赖以下5个函数,但可以实现任意的流程,简单而高效。

7.1 立即返回函数 return(x)

结束计算并立即返回表达式的值为x。

7.2 判断函数 if(x,y1,y2,... ...,yn)

当逻辑语句x的值为真(非0数表示逻辑真)时,依次执行语句y1,y2,... ...,yn,否则,不执行语句y1,y2,... ...,yn。
该函数至少要有2个自变量参数,其中第一个参数为逻辑语句。
该函数的返回值无意义。

7.3 自定义分段函数

which{逻辑语句1,语句1,
逻辑语句2,语句2,
... ...,
逻辑语句n,语句n,
缺省语句
};

FORCAL从前往后计算并检测逻辑语句的值,当检测到逻辑真时,计算与此逻辑真对应的语句并返回该语句的值(如果有多条语句,要加上括号,即使用括号运算符),如果没有检测到逻辑真,则计算缺省语句的值作为返回值,若此时没有缺省语句,则产生一个运行错误(错误代码为0)。
该函数至少要有2个自变量参数。
例如下式定义了一个分段函数:

(x)=which{x>0,2*x-1,
x*x-1
};

如果舍弃该函数的返回值,则该函数可以作为一个选择计算函数使用。

7.4 while循环函数

while循环是“当型”循环,其特点是:先判断条件是否成立,当条件成立时,则执行循环体,否则退出循环体,即“当条件成立时执行循环”。“当型”循环的循环体有可能一次也不执行。
while循环函数的格式如下:

while{x,
y1,y2,
...,
break(),
...,
continue(),
...,
yn
};

其中x为逻辑语句;y1,y2,...,break(),...,continue(), ...yn为循环体语句。当x的值为真时,依次执行循环体语句,直到x的值为假时退出循环。当执行到break()函数时,跳出while循环,执行while循环后面的语句部分;当执行到continue()函数时,返回到while循环的开始语句x处继续执行。
在循环体语句中,必须有能修改逻辑语句x的值的语句,使x的值为假,退出循环体,否则将产生无限循环。
该函数至少要有2个自变量参数,其中第一个参数为逻辑语句。
该函数的返回值无意义。
以下是一个while循环的例子:

(:i,k)=
{ i=0,k=0,
while{i<=1000000,k=k+i,i++},
//当i<=1000000时,计算k=k+i,然后i++;

k
};

7.5 until循环函数

until循环是“直到型”循环,其特点是:先执行循环体,再判断条件是否成立,当条件成立时,退出循环体,否则继续执行循环体,即“执行循环直到条件成立”。“直到型”循环的循环体至少执行一次。
until循环函数的格式如下:

until{x1,x2,
...,
break(),
...,
continue(),
...,
y
};

until为先执行后判断的循环函数。即先执行循环体语句x1,x2,...,break(),...,continue(),...,然后计算逻辑语句y的值,直到y的值为真时退出循环。当执行到break()函数时,跳出until循环,执行until循环后面的语句部分;当执行到continue()函数时,返回到until循环的开始语句x1处继续执行。
在循环体语句中,必须有能修改逻辑语句y的值的语句,使y的值为真,退出循环体,否则将产生无限循环。
该函数至少要有2个自变量参数,其中最后一个参数为逻辑语句。
该函数的返回值无意义。
以下是一个until循环的例子:

(:i,k)=
{ i=0,k=0,
until{k=k+i,i++,i>1000000},
//计算k=k+i,i++,当i>1000000时退出;

k
};

注意:
(1)break()和continue()是两个无参函数,只能用在while和until两个循环函数中。
(2)FORCAL支持多线程,在多线程的程序中,如果不慎进入了无限循环,可以通过另一个线程退出。

8 轻松一下 [返回页首] [返回目录]

看到这里,应该对Forcal有一个大致的了解了。如果你没有被冒号或者其他什么地方搞晕,真是佩服你了!很想知道你现在会有哪些疑问?请E-Mail给我:forcal@sina.com

我们决定在这里轻松一下,因为下面将进入Forcal的高级话题,尽管这对你来说,可能不在话下,我还是决定要休息一下。

运行下面的代码放松一下:

8.1 简单的数值计算

2+sin[2+3*sqrt(3)]*exp[5]; //实数表达式;
i:222%5+8;
//整数表达式;
c:sin[2+3i]-ln[i];
//复数表达式;

8.2 三角形面积公式

F(a,b,c:s)= s=(a+b+c)/2,sqrt[s*(s-a)*(s-b)*(s-c)]; //定义三角形面积公式;
F[3,4,5];

8.3 变步长辛卜生一元积分(这涉及到用表达式的名称作为函数的参数问题,将在下面讲到)

f(x)=sin[x]+0.8; //定义一元函数;
SimpIntegrate(1,2,0.0001,"f");

8.4 求和函数sum(这涉及到用表达式的名称作为函数的参数问题,将在下面讲到)

F3(x,y)=cos{1-sin[1.2*[x+0.1]^(y/2-x)+cos{1-sin[1.2*[x+0.2]^(y/3-x)]}]
-cos{1-sin[1.2*[x+0.3]^(y/4-x)]}-cos{1-sin[1.2*[x+0.4]^(y/5-x)
+cos{1-sin[1.2*[x+0.5]^(y/6-x)]}]-cos{1-sin[1.2*[x+0.6]^(y/7-x)]}}};
sum["F3",0,1,0.011,1,2,0.11];

8.5 循环的嵌套:打印字符串

(:i,j)=
{
i=20,
while
{
i,
OutNStr
["aa "],
j=0,
while
{
i-j,
OutNStr
["*"],
j++
},
OutNStr
[" bb"],
OutNStr
["/013/010"],
i--
}
}
;

8.6 循环的嵌套:将1~10这几个数字打印10遍

(:i,j)=
{
i=0,
while
{
i<10,
//循环判断语句;


j=1,
while
{
j<11,
//循环判断语句;

out(j),
j++
},
OutNStr["/013/010"],
i++
}
}

8.7 检测一个数是否为素数

Prime(n:i)=
{
i=1,
while
{
i<n/2,

if
{
!fmod(n,++i),
//i先增1,然后进行模运算;

out[n],OutNStr["不是一个素数!/013/010"],
return[i]
}
},
out[n],OutNStr["是一个素数!/013/010
"],
i
};
Prime[5];
//检测5是否是一个素数;

8.8 递归打印数据(递归将在下面讲到)

SetRealStackMax[1000];
z_a(i)=if[i<10,z_a(i+1),out(i)];
//按降序打印;

z_a[0];
a_z(i)=if[i<10,out(i),a_z(i+1)];
//按升序打印;
a_z[0];

8.9 无限循环的退出

while[1,1]; //无限循环函数,可通过Forcal运行监视器退出FORCAL!

8.10 静态变量演示

(:static)=static++; //编译成功后,连续按F7运行,观察输出结果

简单地进行一下小结:

前面主要介绍了Forcal表达式的三种类型、Forcal源代码的格式、Forcal表达式的定义、五种Forcal变量、Forcal模块、Forcal运算符、Forcal字符串、Forcal循环等内容,冒号的大部分用法都接触到了。这些是Forcal最基本的东西,如果你还不熟,应该返回去再看一下。

9 关于关键字 [返回页首] [返回目录]

在一般的编程语言中,关键字是事先定义的有特别意义的字,关键字是保留字,不能用来做标识符(如变量名),使用关键字来做变量名是一种语法错误,不能通过编译。按此定义,则Forcal中没有关键字。Forcal中只有常量、变量和函数。但有些符号常量、变量名或函数名使用很频繁,可当作“关键字”来使用,不过不符合上述关键字的定义,例如:

f(return) = return(return+1); //return是自变量,同时也是一个二级函数。
f(2);

Forcal允许符号常量、变量和函数用同一个标识符表示,参考标识符解释规则。但尽量避免这种用法。

Forcal核心库中的“关键字”见下表(Forcal核心库中未定义任何符号常量,但一些Forcal扩展库中定义的符号常量可当作“关键字”来使用,如FcData中定义的符号常量char、int等等。该表仅收录Forcal核心库中的“关键字”)。

关键字类型功能
static静态变量定义静态变量。
free静态变量专用静态变量,进行销毁表达式前的释放工作。
common全局变量定义全局变量。
const二级函数定义永久性符号常量或暂时性符号常量。
return二级函数结束计算并立即返回表达式的值。
if二级函数条件满足时执行计算多个语句。
which二级函数自定义分段函数,选择计算函数。
while二级函数“当型”循环函数。
until二级函数“直到型”循环函数。
continue二级函数返回while或until循环的开始。
break二级函数跳出while或until循环。
Module二级函数创建模块命名空间。
OutFun二级函数输出模块命名空间中的表达式。
HFor二级函数获得表达式的句柄。

10 常量 [返回页首] [返回目录]

常量是指那些不需要程序计算的固定值。有数值常量和符号常量两种。如100就是一个数值常量。在FORCAL中,也可以用标识符表示一个符号常量。用函数const可以定义一个永久性符号常量或暂时性符号常量,但只有const函数被执行后,定义的常量才会起作用。如下例:

const("aa",111); //const有两个参数时,定义永久性常量aa=111,不可删除
const("bb",222,1);
//const有三个参数,且第三个参数为真时,定义暂时性常量bb=222,可以删除

编译运行以上表达式,然后输入下例代码并执行:

aa; //aa=111
bb;
//bb=222
const("aa",0,0);
//试图删除永久性常量,但失败了
const("bb",0,0); //删除暂时性常量:const有三个参数,且第三个参数为假时,删除暂时性常量bb

有些软件会利用Forcal的输出函数定义一些符号常量,但在这里无法给出具体的例子。

注意:所有常量都是全局性的,可用于任何与常量同类型的表达式中。

11 函数的传值调用和传址调用 [返回页首] [返回目录]

传值调用是把变量值拷贝到被调函数的形式参数中,函数在执行时,参数的改变不会影响到调用函数时所用的变量。
传址调用(也叫引用调用)是把变量的地址拷贝到被调函数的形式参数中,函数在执行时,参数的改变会影响到调用函数时所用的变量。
通常,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;

12 表达式句柄 [返回页首] [返回目录]

FORCAL表达式可用句柄(也称为表达式指针、函数指针)进行标识,可以使用二级函数HFor("ForName",ForType)获得表达式的句柄。

ForName:表达式的名称。可以是简单名称,也可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称。
ForType:表达式的类型。如果是简单名称,只能取1(标识整数表达式)、2(标识实数表达式)、3(标识复数表达式)。如果名称包含模块命名空间访问符::,只能取-1(标识整数表达式)、-2(标识实数表达式)、-3(标识复数表达式)。

在实数表达式中,表达式句柄保存在实数的前4个字节中。在复数表达式中,表达式句柄保存在复数的前4个字节中。

表达式句柄有很多用途,例如可以作为函数的参数、获得表达式中的静态字符串等等。

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

在函数调用时,有时候需要用表达式(即自定义函数)作为函数的参数。

13.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。

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

例子:

i: aa(x)=x+8; //定义一元函数aa;
i: (:a)=call[1,HFor("aa",1),7,&a],a;//调用一元函数aa,并将参数7传递给该函数。

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

14 关于递归调用 [返回页首] [返回目录]

如果一个函数直接或者间接地调用了自己,称作函数的递归调用。FORCAL支持函数的递归调用。

为了在FORCAL中使用递归,需要在相应类型的表达式中设置好相应类型的堆栈。在整数表达式中用SetIntStackMax(n)进行设置,在实数表达式中用SetRealStackMax(n)进行设置,在复数表达式中用SetComplexStackMax(n)进行设置。注意n的值不能取得太大,当n取得很大时,函数递归调用虽不会溢出FORCAL的堆栈,但会使系统堆栈溢出,这样会使程序运行中断,丢失数据(作者还没有很好的解决这个问题!希望朋友们能够帮助解决,深表谢意。)。并不需要每次运行程序都进行堆栈的设置,如果堆栈设置的合适,可以只设置一次堆栈。

下面就是递归的最简单的例子:

SetRealStackMax(10000); //设置实数堆栈为10000;
a()=a(); //函数a递归地调用自己,属于无穷递归调用。

直接运行上面的表达式,将会返回一个堆栈溢出的运行错误。虽然溢出FORCAL的堆栈不会中断程序的运行,但对于上面的程序,无论设置多大的堆栈都会溢出,因为函数a的递归定义是错误的。递归函数应当包含一条控制该函数是继续调用其本身还是返回的语句。如果没有这样的语句,递归函数将用完分配给堆栈的所有内存空间,导致堆栈溢出错误。

下面举一个能正常递归调用返回的例子。

SetRealStackMax(1000); //设置实数堆栈为1000;
Fact(n)=which{n<=1,1,n*Fact(n-1)};
//阶乘函数Fact的递归实现;
Fact(3); //计算3!;

Fact(5);
//计算5!;

Fact(10);
//计算10!;
Fact(100);
//计算100!;

15 类成员运算符(函数参数运算符) [返回页首] [返回目录]

在FORCAL中,任何一个数都有可能是一个类或指针,这需要在运行时才能确定,所以FORCAL编译器把任何一个函数或表达式都看作类或指针的成员函数。尽管FORCAL32.dll中没有提供类或指针的概念,但FORCAL扩展动态库FcData中定义了该概念。为了存取像类或指针等复杂数据类型时,在形式上看起来更美观一些,FORCAL32.dll中提供了类成员运算符“.”操作类的成员函数或指针数据。如下例:

(:x)= x=2.3, x.sin(); //计算sin(x)。

f(x,y)= x.y.atan2().sin();//定义函数f,计算sin(atan2(x,y))。
(:x,y)= x=2.3,y=3.3, x.f(y); //计算f(x,y)。

"字符串也可以的".OutNStr[]; //执行OutNStr["字符串也可以的"];

类成员运算符(函数参数运算符)“.”前只允许是常量名、变量名、字符串、括号运算符或函数,最后一个“.”的后面必须是一个类成员函数名。运算符“.”表示它前面的参数是它后面最近的函数的一个参数,所以称为函数参数运算符,该运算符之所以也称为类成员运算符,是因为“.”就是为了表示类的成员关系而设置的。成员函数在调用时,先把类成员运算符“.”前的数据作为参数,并与函数括号内的参数一起合并,然后执行计算。即:函数的参数个数为类成员运算符“.”前的变量个数与函数括号内的参数个数之和。注意:只有成员函数前面的类成员数据才是该函数的参数。例如:

(2).(3).max[].(5).min[]

上式中,2和3max[]的参数,而max[2,3]和5min[]的参数,即:min{max[(2),(3)],(5)}

使用类成员运算符,函数if、while、until等函数有以下用法:

(x).if //x为真时执行花括号内的内容。
{
... ...
};

(x).while//x为真时循环执行花括号内的内容。
{
... ...
};

//循环执行花括号内的内容直到x为真。
{
... ...
}.until(x);

类成员运算符不但可以表示类的成员关系,合理地使用该运算符也可使程序更易读。例如:类ClassA有一个成员函数ClassA_Add(a,b),可以执行类对象a和b相加,类对象a、b、c、d连续相加的表达式为:

ClassA_Add{ClassA_Add[ClassA_Add(a,b),c],d}

用运算符“.”可表示为:

a.ClassA_Add(b).ClassA_Add(c).ClassA_Add(d)

尽管运算符“.”对程序的运行速度没有影响,但会影响编译速度,因而建议仅在需要的时候使用它。

16 FORCAL函数 [返回页首] [返回目录]

自定义的表达式可称为函数。

可按不同的分类方法对Forcal中的函数进行分类。

按所处理的数据类型不同,可分为整数函数、实数函数和复数函数三种,分别存在于整数表达式、实数表达式和复数表达式中。

按来源不同,可分为Forcal内置函数(包括一级函数和二级函数)、外部二级函数(通过Forcal扩展模块添加的函数)、用户自定义函数。

按运算速度不同可分为一级函数和二级函数,一级函数都是Forcal内置的,二级函数有些是内置的,但更多的是外置的。

按是否存在递归可分为递归函数和非递归函数。

按函数属性可分为模块私有函数和模块公有函数,私有函数只能被本模块的表达式所访问,公有函数可被任意模块的函数所访问。

按所处理的对象不同,称为某种对象的成员函数。

FORCAL中的一级函数见表16-1。

表16-1:一级函数

函数类型实数函数整数函数复数函数说明
正弦函数sin(x) sin(x) 
余弦函数cos(x) cos(x) 
正切函数tan(x)   
反正弦函数asin(x)   
反余弦函数acos(x)   
反正切函数atan(x)   
平方根函数sqrt(x)sqrt(x)sqrt(x) 
指数函数exp(x)exp(x)exp(x) 
自然对数函数ln(x)ln(x)ln(x) 
常用对数函数lg(x)lg(x)lg(x) 
双曲正弦函数sinh(x)  [exp(x)-exp(-x)]/2
双曲余弦函数cosh(x)  [exp(x)+exp(-x)]/2
双曲正切函数tanh(x)  [exp(x)-exp(-x)]/[exp(x)+exp(-x)]
取整函数int(x) int(x)截去x的小数部分
绝对值函数abs(x)abs(x)abs(x) 
共轭函数  con(x) 
实部虚部交换函数  xy(x) 
实部为0函数  x0(x) 
虚部为0函数  y0(x) 

17 模块命名空间 [返回页首] [返回目录]

使用命名空间可以有效地避免函数重名问题。Forcal中可以用函数Module创建模块命名空间,命名空间创建后,可以用函数OutFun输出该模块的表达式,不管是私有表达式还是公有表达式,都可以输出。

Module("Name":"Name1","Name2",... ...) //创建模块命名空间Name,继承自"Name1","Name2",... ...

OutFun("fun1","fun2","fun3",... ...) //输出模块命名空间中的表达式"fun1","fun2","fun3",... ...

模块命名空间只能创建一次,可以继承,甚至可以循环继承,如果确实有必要。模块命名空间是一棵树或者是一个连通图。

当模块中有表达式时,才能创建该模块的命名空间,当该模块中的最后一个表达式被销毁时,将同时销毁该命名空间。

当为一个命名空间指定父空间(基空间)时,该父空间是否存在可以是未知的,即父空间并不一定要先于子空间而存在。

模块命名空间中输出的表达式可以用命名空间成员访问符::调用,如:Name::fun1(...)。如果该命名空间中没有输出指定的表达式,而该空间的父空间中输出了同名表达式,就会调用父空间中的同名表达式。可以连续使用访问符::直接调用指定父空间(或该父空间的父空间)中的表达式,如:Name1::Name2::Name3::fun1(...)。可以看出,模块命名空间中的表达式调用规则类似于C++中的虚函数调用规则。

由于Module和OutFun是两个函数,为了使创建的空间及输出函数立即可用,应在编译完Module或OutFun所在的表达式后,立即执行该表达式。

例如:

#MODULE#                         //定义一个子模块
  !Module("AA");                 //创建模块命名空间AA,该表达式编译后将立即执行
  set(x::xx)= xx=x;              //模块私有表达式
  get(::xx)= xx;                 //模块私有表达式
  aa()= 111;                     //模块私有表达式
  !OutFun("set","get","aa");     //输出模块命名空间中的表达式,该表达式编译后将立即执行
#END#                            //子模块定义结束

#MODULE#                         //定义一个子模块
  !Module("BB","AA");            //创建模块命名空间BB,继承自"AA",该表达式编译后将立即执行
  set(x::xx)= xx=x;              //模块私有表达式
  get(::xx)= xx;                 //模块私有表达式
  !OutFun("set","get");          //输出模块命名空间中的表达式,该表达式编译后将立即执行
#END#                            //子模块定义结束

//以下都是主模块中的表达式

aa()= 999999;
aa();                            //调用主模块中的表达式aa,结果为:999999

BB::aa();                        //通过模块空间BB调用空间AA中的表达式aa,结果为:111

BB::set(33);                     //调用模块空间BB中的表达式set,结果为:33
BB::get();                       //调用模块空间BB中的表达式get,结果为:33

BB::AA::set(55);                 //调用模块空间AA中的表达式set,结果为:55
BB::AA::get();                   //调用模块空间AA中的表达式get,结果为:55
BB::get();                       //调用模块空间BB中的表达式get,结果为:33
AA::get();                       //调用模块空间AA中的表达式get,结果为:55

18 二级函数命名空间 [返回页首] [返回目录]

为了避免二级函数重名,二级函数可以采用模块命名空间中的命名方式,如:Fun2::set,称二级函数命名空间。是否采用二级函数命名方式,取决于提供二级函数的模块。

一般,二级函数名称中不应有空格,所以可用如下方式判断一个函数是一个二级函数,或者是一个模块命名空间中输出的表达式。

name ::set; //注意::前有一空格,若编译通过,一般是模块命名空间中的表达式,若编译未通过,说明是个二级函数

19 标识符解释规则 [返回页首] [返回目录]

1)标识符后若有括号,表示是一个函数,否则是一个变量或常量名。

2)如果一个变量名与常量名相同,则常量名被忽略。

3)如果是一个普通的函数名,则确定函数的顺行是:一级函数或流程控制函数、自定义表达式、二级函数。

4)如果是一个命名空间中的函数,确定函数的顺行是:模块命名空间、二级函数命名空间。

5)模块私有表达式与一个公有表达式重名时,优先调用本模块中的私有表达式。

20 Forcal数据类型动态库FcData简介 [返回页首] [返回目录]

FcData32.dll是一个标准的Forcal扩展动态库,该库对Forcal的数据类型进行了扩展。

FcData中除支持各种基本数据类型外,还可用关键字“DefineClass”进行类定义,通过关键字“class”创建类对象,实现复杂的数据结构。

通过FcData32.dll的输出函数接口,可以向FcData添加任意复杂的数据类型,这些类型称为外部基本数据类型。

FcData中的所有数据都用一个指针进行标识,通过指针可以访问到FcData数据。在Microsoft32位平台上,FcData指针是一个4字节整数,与Forcal整数表达式中使用的数据字节数相同。FcData是通过Forcal整数表达式实现的。

FcData中所有的数据都是用函数new()动态申请的,申请成功时返回一个数据指针,可通过该指针对数据进行操作,使用完后用函数delete()函数进行销毁,或者用函数DeleteAllFCD()一次性销毁所有数据。FcData中的数据是安全的,不存在内存泄漏,FcData中有自动回收垃圾的机制。

FcData详细内容可参考:FcData32.DLL V1.0(beta版) 用户指南

21 类模块简介 [返回页首] [返回目录]

FcData类和MForcal模块结合称为类模块,可实现C++类的功能。

设计一个模块,专门用来处理有特定意义的类,这样的模块称为类模块。通过模块变量、模块的私有函数,可以实现对数据处理的封装和隐藏,通过模块的公有函数,或者通过模块命名空间输出的私有函数,可以输出模块的功能。

Forcal类模块的详细内容可参考:FORCAL中的对象、函数和模块

22 主要技术指标 [返回页首] [返回目录]

1、表达式最大长度:2G。
2、自变量个数:不限。
3、动态变量、静态变量、模块变量、全局变量个数:不限。
4、最多可用的实数表达式:不限。
最多可用的复数表达式:不限。
最多可用的整数表达式:不限。
5、表达式中最多可用的字符串数:不限。
6、自定义外部函数个数:不限。
7、while循环、until循环最大循环次数:不限。
8、表达式递归调用最多层数:受系统堆栈和自定义堆栈大小的限制,自定义堆栈最大为2G。
9、最多可存储的用户自定义数据类型:约2G。
10、执行速度:一级函数速度约为FORTRAN(或C/C++)执行速度的50%左右;其他情况,速度稍有下降;
11、FORCAL32.DLL文件大小:小于100K;不使用MSVC运行库的静态库版本,约200K~300K。
12、内存需求:视FORCAL运行情况而定。


版权所有© Forcal数学软件 2002-2009,保留所有权利
E-mail: forcal@sina.com QQ:630715621

最近更新: <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%Y年%m月%d日" startspan -->2009年06月10日<!--webbot bot="Timestamp" i-checksum="1265" endspan -->

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值