FORCAL32.DLL V8.0 用户指南
1 简单的例子 2 FORCAL标识符 3 表达式和数据 4 常量和变量 5 赋值语句 6 算术运算符 7 关系运算符和逻辑运算符 8 逗号(冒号)运算符和括号运算符 9 运算符及优先级 10 函数概述 11 函数的传值调用和传址调用 12 用表达式作为函数的参数 13 表达式的初始化及销毁 14 FORCAL表达式的完整定义及表达式句柄 15 流程控制 | 16 字符数据处理 17 关于递归调用 18 关于模块化编译 19 模块命名空间 20 二级函数命名空间 21 类成员运算符(函数参数运算符) 22 标识符解释规则 23 二级函数 24 关于关键字 25 效率 26 主要技术指标 |
正文:
F(x,y)=x+y; //函数定义;
2+F[2,3]+5; //简单的计算式;
在这个例子中,分号表示一个表达式的结束,两个反斜杠//后的内容表示注释,本文中将一直采用这种表示和注释方法。
在第一个表达式中定义了一个函数 F ,小括号( )内的x,y为函数的自变量,等号后为函数体,即可执行的部分,该函数计算并返回两个参数的和。在第二个表达式中调用了上面定义的函数 F 。
需要注意,在一般的Forcal程序中,只计算无自变量的表达式。因此,对第一个表达式只编译,不计算。
在FORCAL中,一个标识符可以用来表示一个表达式的名字,或者是一个函数的名字,或者是一个变量,或者是一个符号常量。标识符可由一个或多个字符组成,可以任意一个英文字母或者下划线开头,后面可以接英文字母、数字或者下划线(下划线的使用可以增加标识符的可读性,如first_name)。在FORCAL V8.0及以后的版本中,大写与小写是不相同的,count和COUNT是两个不同的名字。下面给出一些合法的标识符:
first last Addr1 top _of_file name23 _temp a23e3 MyVar
表达式是FORCAL的编译单元,如果没有语法错误,编译后将生成可执行的代码。FORCAL表达式可以很简单,例如只有一个数字;FORCAL表达式也可以很复杂,例如是一段复杂的程序。
以下是一些简单的表达式的例子:
2;
2+3;
2+sin(3)-ln[6];
可以给表达式起一个名字,这样做的好处是:以后可以用函数调用的方式通过该名字调用该表达式。表达式的名字是一个标识符,必须位于表达式的开头。例如,上面三个表达式可按如下方式定义:
A()=2;
B()=2+3;
C()=2+sin(3)-ln[6];
表达式定义的一般形式是:
Name(a,b)={a=a+2,b=10:a+b}
其中 Name 为表达式的名字,不可与其它已经定义的表达式名字相同,但可以缺省;如果定义了表达式名字,表达式名字后必须跟一对小括号(只能用小括号),用来定义自变量,自变量个数可以为零,也可以有任意多个,有多个自变量时,自变量间以逗号分隔;如果用小括号定义了自变量,小括号后必须跟一个等号,该等号标志表达式可执行部分的开始,等号及等号后的可执行部分均不可缺省。表达式的可执行部分由多个语句组成,多个语句之间用逗号或冒号分隔,表达式总是返回最后一个语句的值;另外花括号不是必须的,但有了花括号后,表达式更易读。
用这种方式定义表达式时,实际上就是自定义了一个函数,可以在任意的表达式中使用该函数(只要给该函数起了名字),由于这个原因,我们常常将表达式说成是函数,或者说表达式就是一个函数,但反过来并不成立。
下面是一个函数定义及使用的例子。
F(x,y)=x+y; //函数定义;
2+F[2,3]+5; //函数调用;
以下是一些合法的表达式例子:
2; //常量(无参)表达式(函数);
()=2; //常量(无参)表达式(函数);
A()=2; //常量(无参)表达式(函数),但定义了函数名字,在其它表达式中可以调用该函数;
B(x)=2; //有一个自变量的表达式(函数);
C(x,y)=x+y; //有二个自变量的表达式(函数);
下面说一下表达式的类型和数据的类型,FORCAL表达式有三种类型:整数表达式、实数表达式和复数表达式;相应地,FORCAL中有三种基本的数据类型:整数、实数和复数。例如:
i:2+3; //整数表达式,在一般的Forcal程序中,整数表达式以i:开头;
2.2+3; //实数表达式,在一般的Forcal程序中,缺省是实数表达式;
c:2+3i; //复数表达式,在一般的Forcal程序中,复数表达式以c:开头;
FORCAL编译器在编译表达式时,将整数表达式中的数据都转换成整数,如果是有小数部分将截断取整;将实数表达式中的数据都转换成实数;将复数表达式中的数据都转换成复数,如果数字后有i,表示一个虚数。
在FORCAL中,小数的表示非常灵活,小数点前面的零或后面的零可省,例如:5.6、5.、.6都是合法的数据表示。在FORCAL中,还可以使用科学记数法表示数字,例如:10.3E8、2E-10等,其中用E表示以10为底的指数部分,但指数不能为小数,例如:3E5.6、8.6E-6.7不合法。
常量是指那些不需要程序计算的固定值。有数值常量和符号常量两种。如100就是一个数值常量。在FORCAL中,也可以用标识符表示一个符号常量。用函数const可以定义一个永久性符号常量或暂时性符号常量,但只有const函数被执行后,定义的常量才会起作用。如下例:
const("aa",111); //定义永久性常量,不可删除
const("bb",222,1); //定义暂时性常量,可以删除
编译运行以上表达式,然后输入下例代码并执行:
aa; //aa=111
bb; //bb=222
const("aa",0,0); //试图删除永久性常量,但失败了
const("bb",0,0); //删除暂时性常量
有些软件会利用Forcal的输出函数定义一些符号常量,但在这里无法给出具体的例子。
变量是指在程序运行时,其值可以改变的量。FORCAL有五种变量,即:自变量、动态变量、静态变量、模块变量和全局变量。自变量、动态变量和静态变量只能被定义该变量的表达式所访问;模块变量可被同一模块的所有表达式所访问,其他模块的表达式无法访问;全局变量可被所有的表达式所访问。自变量用于向表达式传递参数,因此自变量也称为形式参数。动态变量只在表达式执行时起作用,一旦表达式执行完毕,动态变量也随之失效。静态变量存在于表达式的整个生命周期,每次表达式执行完毕,静态变量的值被保留。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中,可以用等号对变量进行赋值。例如:
F(x)= x=5; //函数定义,将数值5赋给变量x;
F[2]; //函数调用,返回值为5;
上面进行变量赋值的例子很简单,但等号后部分可以是任意复杂的表达式。
FORCAL中共有八个算术运算符,即:+(加或正)、-(减或负)、*(乘)、/(除)、%(求模)、^(乘方)、++(自增)、--(自减)。其中%(求模)仅在整数表达式中使用;^(乘方)仅在实数和复数表达式中使用。
运算符*、/、%和^是双目运算符。注意数字与变量相乘时,乘号不可省略;在进行乘方运算时,底数应为非负数。
运算符+、-作加、减运算时,是二元运算符,当作正、负运算时,是单目运算符。
运算符++、--是单目运算符,仅能对变量使用。++使变量的值加1,如果++在变量之前,那么运算符在程序语句访问该值之前执行加法运算,这时的++运算符称为“前置自增运算符”;如果把该运算符放在变量之后,那么运算符在程序语句访问该值之后执行加法运算,这时的++运算符被称为“后置自增运算符”。--使变量的值减1,如果--在变量之前,那么运算符在程序语句访问该值之前执行减法运算,这时的--运算符称为“前置自减运算符”;如果把该运算符放在变量之后,那么运算符在程序语句访问该值之后执行减法运算,这时的--运算符被称为“后置自减运算符”。例如:
(:x)= x=2,++x; //返回值为3;
(:x)= x=2,++x,x; //返回值为3;
(:x)= x=2,x++; //返回值为2;
(:x)= x=2,x++,x; //返回值为3;
(:x)= x=2,--x; //返回值为1;
(:x)= x=2,--x,x; //返回值为1;
(:x)= x=2,x--; //返回值为2;
(:x)= x=2,x--,x; //返回值为1;
如果在复数表达式中使用自增减运算符,则仅对复数的实部作运算,复数的虚部保持不变。例如:
c:(:x)= x=2+3i,++x; //返回值为3+3i;
c:(:x)= x=2+3i,--x; //返回值为1+3i;
单目运算符的优先级比双目运算符的优先级高,后置单目运算符的优先级比前置单目运算符的优先级高。对于同一优先级的运算,按从左到右的优先顺序进行。
注意:单目运算符-(负)与双目运算符^(乘方)需用括号区分计算的先后顺序。例如:
(-2)^2; //返回值为4;
-(2^2); //返回值为-4;
-2^2; //编译出错;
-(2+3)^2; //编译出错;
-sin(2+3)^2;//编译出错;
算术运算符的优先级如表6-1所示。在表中,同一行中运算符优先级相同,不同行中运算符的优先级从上往下依次降低。
表6-1:算术运算符及优先级
运 算 符 | 说 明 |
++、-- | 后置单目运算符,自增减运算符 |
+、-、++、-- | 前置单目运算符,“++、--”为自增减运算符 |
^ | 乘方 |
*、/、% | 乘、除、求模 |
+、- | 加、减 |
7 关系运算符和逻辑运算符 [返回页首] [返回目录]
关系运算是对两个值的大小进行比较,返回一个逻辑值。逻辑值只有两个:逻辑真和逻辑假。在FORCAL中用0表示逻辑假,其他任何非0值表示逻辑真。例如:
3>2; //返回逻辑真;
2>3; //返回逻辑假;
关系运算符共有6个:>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、==(等于)、!=(不等于)。
逻辑值之间的运算称逻辑运算,逻辑运算的结果仍然是一个逻辑值。有三个逻辑运算符:&(逻辑与)、|(逻辑或)、!(逻辑非)。通过表7-1给出的真值表可以掌握这三种运算。表中用1代表逻辑真,0代表逻辑假。
表7-1 真值表
p | q | p&q | p|q | !p |
0 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 0 |
在FORCAL中,仅使用复数的实部表示逻辑值,逻辑值的虚部没有任何意义。所以,如果在复数表达式中使用关系运算符或逻辑运算符,则仅取复数的实部作运算,复数的虚部对运算结果没有任何影响;在运算结果中,仅复数的实部有意义,虚部没有意义,但其值为参与运算的第一个参数的虚部值。例如:
c:2+3i>5+6i; //返回值为0+3i;
c:!(2+3i); //返回值为0+3i;
c:2+3i==2+6i; //返回值为1+3i;
c:2+3i!=2+6i; //返回值为0+3i;
FORCAL的这种规定使得不能直接用==、!=运算符对两个复数作相等或不等的比较。如果要比较两个复数相等或者不等,应对复数的实部和虚部都进行比较,见下面的例子。
c:(:a,b)= a=2+3i,b=2+3i,a==b&xy(a)==xy(b); //返回值为1+3i;
c:(:a,b)= a=2+3i,b=2+5i,a==b&xy(a)==xy(b); //返回值为0+3i;
在上面的例子中,xy(a)是一个复数函数,该函数用于交换复数a的实部和虚部。
关系运算符和逻辑运算符的优先级如表7-2所示。在表中,同一行中运算符优先级相同,不同行中运算符的优先级从上往下依次降低。
表7-2 关系运算符和逻辑运算符及优先级
运 算 符 | 说 明 |
! | 逻辑非 |
>、>=、<、<=、==、!= | 关系运算符 |
& | 逻辑与 |
| | 逻辑或 |
8 逗号(冒号)运算符和括号运算符 [返回页首] [返回目录]
表达式中如果有多个语句,可以用逗号或冒号进行分隔,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中的运算符及优先级如表9-1所示,在表中,同一行中运算符优先级相同,不同行中运算符的优先级从上往下依次降低。
表9-1:FORCAL运算符及优先级
运 算 符 | 说 明 |
( )、[ ]、{ } | 括号运算符 |
:: | 命名空间成员访问符 |
. | 类成员运算符(函数参数运算符) |
++、-- | 后置单目运算符(自增、自减) |
!、+、-、++、-- | 前置单目运算符(非、正、负、自增、自减) |
^ | 算术运算符(乘方) |
*、/、% | 算术运算符(乘、除、求模) |
+、- | 算术运算符(加、减) |
>、>=、<、<=、==、!= | 关系运算符(大于、大于等于、小于、小于等于、等于、不等于) |
& | 逻辑与 |
| | 逻辑或 |
= | 赋值运算符 |
,、: | 逗号、冒号运算符 |
注意在FORCAL中,通常运算符不能连用,例如 !-2 是错误的,应当添加括号分隔开各个运算符,即应当写成 !(-2) 形式。
还有一个FORCAL运算符在表9-1中没有列出:&(取变量的地址)。取地址运算符&只能用于(被逗号或冒号隔开的)单独的变量,与其他运算符不发生任何关系,所以这个运算符在表9-1中没有列出,其用法我们将在下面详细介绍。
类成员运算符(函数参数运算符)“.”及命名空间成员访问符“::”也将在下面详细介绍。
函数是FORCAL的构成模块,是FORCAL最为重要的特性。一个函数,通过传递给它的参数完成一个特定的功能。通常函数都有一个名字,可通过调用函数名并传递参数来使用函数。所有的函数都将返回一个函数值。典型的函数调用方法如下:
函数名(参数1,参数2,... ...)
函数可以有零个或多个参数,但即便有零个参数,函数名后的括号也不能缺省。函数调用时,参数一定要匹配。
在FORCAL中可以使用的函数有三种:一级函数、二级函数和自定义函数(表达式)。其中一级函数为系统内置的函数,都是单变量函数,运算速度快;二级函数部分为系统内置的函数,部分为软件为提高性能扩充的函数,功能非常丰富;自定义函数实际上就是一个表达式,由用户定义,但只有有名字的自定义函数才能被其他表达式所调用。
实际上,FORCAL还有一类为数不多的函数,称为流程控制函数,不过对于它们,函数的意义并不明显,更多的是流程控制的意义,因此我们不把它们包括在上面的分类中。
FORCAL函数有三种类型,即整数函数、实数函数和复数函数,分别对应着FORCAL的三种表达式。FORCAL规定:一级函数和二级函数只能应用在同类型的表达式中(不过可以用CalFun(...)函数间接调用其他类型的二级函数),而自定义函数可以应用在任意的表达式中,FORCAL将自动为自定义函数的调用进行类型转换。如果在整数或实数表达式中调用复数自定义函数,传递的参数个数应是复数自定义函数的自变量个数的两倍,即对复数自变量参数的实部和虚部都要进行赋值,且只能返回复数自定义函数的实部的值。如果在复数表达式中调用整数或实数类型的自定义函数,和一般的函数调用一样,参数要匹配,调用时只将复数的实部传递给整数或实数自定义函数,而函数返回值只传递给复数的实部。例如:
c:a(x,y)=x+y; //复数自定义函数,有两个参数;
a(1,2,5,6); //调用函数a时,要传递四个参数;
b(x,y)=x+y; //实数自定义函数,有两个参数;
c:b(1+2i,5+6i); //调用函数b时,仍然传递两个参数;
在整数表达式中进行一级函数运算时,将把整数转化为双精度实数后进行运算,最后再把运算结果转化为整数。
FORCAL中的一级函数见表10-1。
表10-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) |
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 用表达式作为函数的参数 [返回页首] [返回目录]
在函数调用时,有时候需要用表达式(即自定义函数)作为函数的参数。
12.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。
12.2 用二级函数HFor("ForName",ForType)获得表达式句柄(也称为表达式指针、函数指针)传给函数
例子:
i: aa(x)=x+8; //定义一元函数aa;
i: (:x)=call[1,HFor("aa",1),7,&x],x;//调用一元函数aa,并将参数7传递给该函数;call是Forcal数据类型扩展库FcData的一个函数。
一般,在函数的说明中会指定需传递的表达式参数的类型,或者传递字符串形式的表达式名称,或者传递表达式的句柄。
13 表达式的初始化及销毁 [返回页首] [返回目录]
通过静态变量,Forcal表达式可以进行初始化,也可以借助专用静态变量free进行销毁表达式前的释放工作(Forcal在销毁表达式前将自动设置free=1,然后自动执行表达式)。如下例:
f(x:y,static,d,e,free,f)= //表达式用OutNStr函数输出字符串,OutNStr是Forcal数据类型扩展库FcData的一个函数。
{ if{!d,OutNStr("初始化!"),d=1}, //注意静态变量d使初始化仅执行一次。
if{free,OutNStr("销毁!"),return(0)}, //Forcal在销毁表达式前将自动设置free=1,然后自动执行表达式,使销毁仅执行一次。
x+1 //每次调用使自变量增1。
};
f(0);
f(1);
f(2);
为了充分发挥静态变量free的作用,在表达式中不要对其有任何的赋值运算,以免引起混乱。另外,如果没有将free定义为静态变量,Forcal在销毁表达式前将不会将其自动设置为1,也不会自动执行表达式。
如果一个表达式中直接调用的其他表达式或二级函数被删除,该表达式将不能运行,这样即便在该表达式中定义了静态变量free,在Forcal销毁该表达式前也无法执行该表达式。如下例:
//函数f中调用了函数a,如果函数a先于函数f被销毁,则函数f将无法进行正常的销毁工作。
a(x)=x+1;
f(x:y,static,d,e,free,f)=
{ if{!d,OutNStr("初始化!"),d=1}, //注意静态变量d使初始化仅执行一次。
if{free,OutNStr("销毁!"),return(0)}, //如果函数a先于函数f被销毁,则函数f将无法进行正常的销毁工作。
a(x)+1 //调用函数a。
};
f(0);
f(1);
f(2);
若要上面的程序正常工作,主程序在销毁表达式时必须遵循“后进先出”的原则,即:后编译的表达式先删除。
动态调用表达式或二级函数,能使上面的程序正常工作。如下例:
//以下语句中,call()是Forcal数据扩展动态库FcData中定义的整数函数,可在运行时调用自定义函数;
//函数f动态调用了函数a,即便函数a先于函数f被销毁,也不影响函数f执行正常的销毁工作。
i: a(x)=x+1;
i: f(x:y,static,d,e,free,f)=
{ if{!d,OutNStr("初始化!"),d=1}, //注意静态变量d使初始化仅执行一次。
if{free,OutNStr("销毁!"),return(0)}, //Forcal在销毁表达式前将自动设置free=1,然后自动执行表达式,使销毁仅执行一次。
call(1,HFor("a",1),x,&x),x+1//通过函数call()动态调用函数a,使自动销毁正常进行。因为在call()执行前,不知道函数a是否存在。
};
i: f(0);
i: f(1);
i: f(2);
最后指出,能否进行销毁表达式前的释放工作,取决于主程序的设计,与主程序如何使用Forcal的输出函数有关。即便销毁表达式前没有进行释放工作,Forcal最后也将释放所有资源,因而无需担心太多。
14 FORCAL表达式的完整定义及表达式句柄 [返回页首] [返回目录]
在这里,我们总结性地给出FORCAL表达式的完整定义,以帮助用户更好地理解FORCAL,为了定义的完整性,部分地重复了前面所叙述过的内容。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是表达式的名字,必须位于表达式的开头,该名字必须是一个标识符。给表达式起一个名字,主要是以后可以通过该名字来调用该表达式。表达式也可以没有名字,但这样,在其它的表达式中将无法调用它。在FORCAL中,一般表达式的名字是唯一的,不能给两个表达式起同一个名字(模块内表达式的名字除外,模块的定义将在后面介绍)。
如果定义了表达式名字,表达式名字后必须跟一对小括号(只能用小括号),用来定义变量。变量个数可以为零,也可以有任意多个。有多个变量时,变量间以逗号或冒号分隔。用冒号隔开的变量,从前往后依次为自变量、动态变量、静态变量、模块变量和全局变量,即第一个冒号前为自变量,两个冒号之间为动态变量和静态变量,第二个冒号后为模块变量和全局变量。两个冒号之间用关键字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;
FORCAL表达式可用句柄(也称为表达式指针、函数指针)进行标识,可以使用二级函数HFor("ForName",ForType)获得表达式的句柄。表达式句柄有很多用途,例如可以作为函数的参数、获得表达式中的静态字符串等等。
在FORCAL中,表达式的各个语句一般是顺序执行的。但是某些函数可以改变语句执行的顺序,称为流程控制函数。
15.1 立即返回函数 return(x)
结束计算并立即返回表达式的值为x。
15.2 判断函数 if(x,y1,y2,... ...,yn)
当逻辑语句x的值为真时,依次执行语句y1,y2,... ...,yn,否则,不执行语句y1,y2,... ...,yn。
该函数至少要有2个自变量参数,其中第一个参数为逻辑语句。
该函数的返回值无意义。
15.3 自定义分段函数
which{逻辑语句1,语句1,
逻辑语句2,语句2,
... ...,
逻辑语句n,语句n,
缺省语句
};
FORCAL从前往后计算并检测逻辑语句的值,当检测到逻辑真时,计算与此逻辑真对应的语句并返回该语句的值,如果没有检测到逻辑真,则计算缺省语句的值作为返回值,若此时没有缺省语句,则产生一个运行错误(错误代码为0)。
该函数至少要有2个自变量参数。
例如下式定义了一个分段函数:
(x)=which{x>0,2*x-1,
x*x-1
};
如果舍弃该函数的返回值,则该函数可以作为一个选择计算函数使用。
15.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
};
15.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支持多线程,在多线程的程序中,如果不慎进入了无限循环,可以通过另一个线程退出。
在FORCAL中,用两个双引号定义一个字符串,即"..."表示一个字符串。在FORCAL字符串中,还可以用反斜杠转义字符输入一些特殊字符,见表16-1。
表16-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动态字符串由Forcal扩展动态库FcData进行管理,可由函数new进行申请,由函数delete进行销毁。
Forcal静态字符串又分为近程静态字符串和远程静态字符串。近程静态字符串仅用一个整数(该整数即字符串在表达式中的地址)就可以标识,远程静态字符串需由表达式句柄和一个整数进行标识。Forcal数据扩展动态库FcData中的函数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字符串指针参数。
如果一个函数直接或者间接地调用了自己,称作函数的递归调用。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!;
以下是一个交叉递归的例子。
i: SetIntStackMax(1000); //设置整数堆栈为1000;
//以下语句中,call()是Forcal数据类型动态库FcData中定义的函数,可在运行时调用自定义函数;
i: a(x:k)=OutNStr["a..."],if(x<1,return[x]),call[1,HFor("b",1),x-1,k]; //a(...)函数中调用了b(...)函数;
i: b(x:k)=OutNStr["b..."],if(x<1,return[x]),call[1,HFor("a",1),x-1,k]; //b(...)函数中调用了a(...)函数;
i: a[10]; //启动递归程序;
FORCAL支持表达式的模块化编译。
在用FORCAL编译表达式时,要给该表达式指定模块号,模块号用一个整数进行标识。
在FORCAL中,一个模块由一个或多个表达式组成。模块用一个整数标识,整数可正可负,只要绝对值相等,就属于同一个模块。一般用正整数表示该模块名。模块共有两类,即主模块(0#模块)和普通模块(其他标号的模块)。
同一模块中,模块号为负的表达式称私有表达式,只能被本模块的表达式所访问(即调用),在其他模块中是不可见的;模块号为正的表达式称公有表达式或全局表达式,能被任何一个表达式所访问。主模块(0#模块)中的表达式都是私有表达式。任何一个表达式,既可以访问本模块中的表达式,也可以访问其他模块中的全局表达式,如果本模块中的一个私有表达式与其他模块的一个全局表达式重名,将优先调用本模块中的私有表达式。
由以上规定可以看出,主模块可以访问本模块中的表达式,也可以访问其他模块中的全局表达式。因此,主模块常常用在主程序中。
使用命名空间可以有效地避免函数重名问题。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所在的表达式后,立即执行该表达式。
以下例子须由OpenFc(可从天空软件站、华军软件园等多家下载站下载到该程序)演示。
#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
为了避免二级函数重名,二级函数可以采用模块命名空间中的命名方式,如:Fun2::set,称二级函数命名空间。是否采用二级函数命名方式,取决于提供二级函数的模块。
一般,二级函数名称中不应有空格,所以可用如下方式判断一个函数是一个二级函数,或者是一个模块命名空间中输出的表达式。
name ::set; //注意::前有一空格,若编译通过,一般是模块命名空间中的表达式,若编译未通过,说明是个二级函数
21 类成员运算符(函数参数运算符) [返回页首] [返回目录]
在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和3是max[]的参数,而max[2,3]和5是min[]的参数,即: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)
尽管运算符“.”对程序的运行速度没有影响,但会影响编译速度,因而建议仅在需要的时候使用它。
1)标识符后若有括号,表示是一个函数,否则是一个变量或常量名。
2)如果一个变量名与常量名相同,则常量名被忽略。
3)如果是一个普通的函数名,则确定函数的顺行是:一级函数或流程控制函数、自定义表达式、二级函数。
4)如果是一个命名空间中的函数,确定函数的顺行是:模块命名空间、二级函数命名空间。
5)模块私有表达式与一个公有表达式重名时,优先调用本模块中的私有表达式。
在下面的说明中,FcErr表示相应函数的运行错误代码。
23.1 整数二级函数
23.1.1、最大值函数 max(x1,x2,x3,... ...):
若FcErr=1:没有参数。
23.1.2、最小值函数 min(x1,x2,x3,... ...):
若FcErr=1:没有参数。
23.1.3、乘方函数 pow(x,y):
求x^y。在整数表达式中进行乘方运算时,将把整数转化为双精度实数后进行运算,最后再把运算结果转化为整数。
23.1.4、符号传送函数 sign(x,y):
该函数的符号取y的符号,数值取x的绝对值,若y=0无符号传送,返回x值。
23.1.5、正差函数 dim(x,y):
当x>y时得x-y,否则返回0。
23.1.6、设置整数堆栈最大数目 SetIntStackMax(n):
在使用递归函数之前,需要先设置一定数目的堆栈。通常n的值不能取得太大,当n取得很大时,函数递归调用虽不会溢出FORCAL的堆栈,但会使系统堆栈溢出,这样会使程序运行中断。该函数返回实际设置的堆栈数目。
若FcErr=1:内存分配失败;FcErr=2:在函数递归时使用该函数。
23.1.7、调用二级函数 CalFun(m,"aa",x1,x2,...,xn):
只能调用实数或复数二级函数,整数二级函数可直接调用,无需使用该函数。m=5时调用实数二级函数;m=6时调用复数二级函数。"aa"为要调用的二级函数名称。x1,x2,...,xn为二级函数的自变量。如果调用复数二级函数,所传送的自变量参数应为复数二级函数自变量个数的两倍,分别与复数自变量的实部和虚部相对应;函数返回时,仅返回复数实部的值。
若FcErr=1:该函数至少使用两个参数,或者参数不匹配;FcErr=2:应使用字符串传递二级函数名称;FcErr=3:函数类型参数m非法;FcErr=4:找不到该二级函数;FcErr=5:内存分配失败。
23.1.8、设置常量 const("ConstStr",ConstValue)或const("ConstStr",ConstValue,bMode):
ConstStr:常量名,要符合Forcal标识符的命名规定,否则编译器找不到该常量。
ConstValue:常量的值。当bMode为逻辑假时,不使用该值。
bMode:指出工作方式。若缺省该参数(函数const只有两个参数),创建一个永久性常量,无法删除一个永久性常量,除非Forcal重新初始化。若bMode为逻辑真,创建一个暂时性常量;若bMode为逻辑假,删除一个暂时性常量。暂时性常量保持常量的基本意义,在编译表达式时不可改变其值,但可被const函数删除。
该函数返回值的意义如下:
0:设置成功。
1:已存在该常量。
2:内存分配失败。
3:不能用空字符串作为常量名。
4:参数太多或者太少。
5:需用字符串指出常量名。
6:不能删除该常量。
23.1.9、获得调用其他类型表达式或其他类型二级函数时计算结果的字节值 GetCalByte(&x0,&x1,... ...):
在整数表达式中调用实数表达式或者复数表达式,以及用CalFun函数调用实数或复数二级函数之后,紧接着调用该函数,可以获得计算结果的按字节复制来的值。由于Forcal表达式中,整数为4个字节,实数为8个字节,复数为16个字节,所以要获得实数结果的按字节拷贝,需使用两个参数,要获得复数结果的按字节拷贝,需使用4个参数。多余的参数被忽略,参数少将不能获得计算结果的完整拷贝。该函数返回计算结果前4个字节表示的整数值。
23.1.10、创建模块命名空间 Module("NameSpace":"name1","name2",... ...):
该函数为调用该函数的模块创建模块命名空间。NameSpace为空间名称,继承自"name1","name2",... ...。模块命名空间可以循环继承。模块命名空间是一棵树或者是一个连通图。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:创建成功。
1:至少需要一个参数,不能缺省模块命名空间名称。
2:不能重复创建模块命名空间,模块命名空间只能创建一次。
3:须用字符串指出模块命名空间名称。
4:内存错误。
5:该命名空间已被其他模块使用。
6:要创建的命名空间不能是空字符串。
23.1.11、输出模块命名空间中的表达式 OutFun("fun1","fun2",... ...):
该函数为模块命名空间输出表达式。模块命名空间即调用函数OutFun的模块的命名空间。须用字符串参数指出表达式的名称。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:操作成功。
i:其他正整数:前i-1个参数操作成功,第i个参数操作失败,其他参数未进行操作。
23.1.12、获得表达式的句柄 HFor("ForName",ForType):
ForName:表达式的名称。可以是简单名称,也可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称。
ForType:表达式的类型。如果是简单名称,只能取1(标识整数表达式)、2(标识实数表达式)、3(标识复数表达式)。如果名称包含模块命名空间访问符::,只能取-1(标识整数表达式)、-2(标识实数表达式)、-3(标识复数表达式)。
23.2 实数二级函数
23.2.1、反正切函数 atan2(x,y):
求x/y的正切值,所在象限由x和y的符号确定。
23.2.2、最大值函数 max(x1,x2,x3,... ...):
若FcErr=1:没有参数。
23.2.3、最小值函数 min(x1,x2,x3,... ...):
若FcErr=1:没有参数。
23.2.4、余数函数 fmod(x,y):
求x/y的余数。
23.2.5、取小数部分函数 modf(x):
该函数把x分解成整数和小数部分,并返回小数部分。
23.2.6、符号传送函数 sign(x,y):
该函数的符号取y的符号,数值取x的绝对值,若y=0无符号传送,返回x值。
23.2.7、正差函数 dim(x,y):
当x>y时得x-y,否则返回0。
23.2.8、变步长辛卜生一元积分 SimpIntegrate(a,b,eps,"F"):
a为积分下限,b为积分上限,eps为积分精度要求,F为被积函数。
例子:
a(x)=sin[x]+0.8;
SimpIntegrate(1,2,0.0001,"a");
若FcErr=1:应使用字符串传递表达式名称;FcErr=2:指定的表达式不存在;FcErr=3:指定的表达式不是一元函数;
23.2.9、求和函数 sum("F",y1min,y1max,y1dy,y2min,y2max,y2dy,... ...):
F为求和函数;y1min,y1max,y1dy为第一个自变量的初值、终值和参数增量[初值<终值,参数增量>0],依次类推。
例子:
F(x,y)=sin[x]+0.8-y;
sum("F",1,2,0.01,2,5,0.1);
FcErr=1:应使用字符串传递表达式名称;FcErr=2:指定的表达式不存在;FcErr=3:参数个数不匹配;FcErr=4:常量表达式,无法求和;FcErr=5:内存分配失败;FcErr=6:自变量参数非法。
23.2.10、求积函数 pro("F",y1min,y1max,y1dy,y2min,y2max,y2dy... ...):
用法请参见sum(),用于求积。
FcErr=1:应使用字符串传递表达式名称;FcErr=2:指定的表达式不存在;FcErr=3:参数个数不匹配;FcErr=4:常量表达式,无法求积;FcErr=5:内存分配失败;FcErr=6:自变量参数非法。
23.2.11、数据求和函数 DataSum("F",y11,y12,... ...,y21,y22,... ...):
F为求和函数;y11,y12,... ...为第一组自变量数据,依次类推。
例子:
F(x,y)=x*y;
DataSum["F",1,2,3,4,5,6,7,8,9,10]; //返回值为190;
说明:对于式子F(x,y)=x*y,求x,y分别取1,2、3,4、5,6、7,8、9,10时的值的和。即求F[1,2]+F[3,4]+F[5,6]+F[7,8]+F[9,10]的值。
若FcErr=1:应使用字符串传递表达式名称;FcErr=2:指定的表达式不存在;FcErr=3:参数个数不匹配;FcErr=4:常量表达式,无法求和。
23.2.12、数据求积函数 DataPro("F",y11,y12,... ...,y21,y22,... ...):
用法请参见DataSum(),用于数据求积。
若FcErr=1:应使用字符串传递表达式名称;FcErr=2:指定的表达式不存在;FcErr=3:参数个数不匹配;FcErr=4:常量表达式,无法求积。
23.2.13、设置实数堆栈最大数目 SetRealStackMax(n):
在使用递归函数之前,需要先设置一定数目的堆栈。通常n的值不能取得太大,当n取得很大时,函数递归调用虽不会溢出FORCAL的堆栈,但会使系统堆栈溢出,这样会使程序运行中断。该函数返回实际设置的堆栈数目。
若FcErr=1:内存分配失败;FcErr=2:在函数递归时使用该函数。
23.2.14、调用二级函数 CalFun(m,"aa",x1,x2,...,xn):
只能调用整数或复数二级函数,实数二级函数可直接调用,无需使用该函数。m=4时调用整数二级函数;m=6时调用复数二级函数。"aa"为要调用的二级函数名称。x1,x2,...,xn为二级函数的自变量。如果调用复数二级函数,所传送的自变量参数应为复数二级函数自变量个数的两倍,分别与复数自变量的实部和虚部相对应;函数返回时,仅返回复数实部的值。
若FcErr=1:该函数至少使用两个参数,或者参数不匹配;FcErr=2:应使用字符串传递二级函数名称;FcErr=3:函数类型参数m非法;FcErr=4:找不到该二级函数;FcErr=5:内存分配失败。
23.2.15、设置常量 const("ConstStr",ConstValue)或const("ConstStr",ConstValue,bMode):
ConstStr:常量名,要符合Forcal标识符的命名规定,否则编译器找不到该常量。
ConstValue:常量的值。当bMode为逻辑假时,不使用该值。
bMode:指出工作方式。若缺省该参数(函数const只有两个参数),创建一个永久性常量,无法删除一个永久性常量,除非Forcal重新初始化。若bMode为逻辑真,创建一个暂时性常量;若bMode为逻辑假,删除一个暂时性常量。暂时性常量保持常量的基本意义,在编译表达式时不可改变其值,但可被const函数删除。
该函数返回值的意义如下:
0:设置成功。
1:已存在该常量。
2:内存分配失败。
3:不能用空字符串作为常量名。
4:参数太多或者太少。
5:需用字符串指出常量名。
6:不能删除该常量。
23.2.16、获得调用其他类型表达式或其他类型二级函数时计算结果的字节值 GetCalByte(&x0,&x1,... ...):
在实数表达式中调用整数表达式或者复数表达式,以及用CalFun函数调用整数或复数二级函数之后,紧接着调用该函数,可以获得计算结果的按字节复制来的值。由于Forcal表达式中,整数为4个字节,实数为8个字节,复数为16个字节,所以要获得整数结果的按字节拷贝,仅需用一个参数,要获得复数结果的按字节拷贝,需使用2个参数。多余的参数被忽略,参数少将不能获得计算结果的完整拷贝。若复制整数结果,则整数结果仅占实数的前4个字节,其余字节无意义,且函数返回时,整数结果也保存在返回值的前4个字节中。复制复数结果时,函数返回计算结果前8个字节表示的实数值,即复数的实部。
23.2.17、创建模块命名空间 Module("NameSpace":"name1","name2",... ...):
该函数为调用该函数的模块创建模块命名空间。NameSpace为空间名称,继承自"name1","name2",... ...。模块命名空间可以循环继承。模块命名空间是一棵树或者是一个连通图。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:创建成功。
1:至少需要一个参数,不能缺省模块命名空间名称。
2:不能重复创建模块命名空间,模块命名空间只能创建一次。
3:须用字符串指出模块命名空间名称。
4:内存错误。
5:该命名空间已被其他模块使用。
6:要创建的命名空间不能是空字符串。
23.2.18、输出模块命名空间中的表达式 OutFun("fun1","fun2",... ...):
该函数为模块命名空间输出表达式。模块命名空间即调用函数OutFun的模块的命名空间。须用字符串参数指出表达式的名称。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:操作成功。
i:其他正整数:前i-1个参数操作成功,第i个参数操作失败,其他参数未进行操作。
23.2.19、获得表达式的句柄 HFor("ForName",ForType):
ForName:表达式的名称。可以是简单名称,也可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称。
ForType:表达式的类型。如果是简单名称,只能取1(标识整数表达式)、2(标识实数表达式)、3(标识复数表达式)。如果名称包含模块命名空间访问符::,只能取-1(标识整数表达式)、-2(标识实数表达式)、-3(标识复数表达式)。
在实数表达式中,表达式句柄保存在实数的前4个字节中。
23.3 复数二级函数
23.3.1、设置复数堆栈最大数目 SetComplexStackMax(n):
在使用递归函数之前,需要先设置一定数目的堆栈。通常n的值不能取得太大,当n取得很大时,函数递归调用虽不会溢出FORCAL的堆栈,但会使系统堆栈溢出,这样会使程序运行中断。该函数返回实际设置的堆栈数目。
若FcErr=1:内存分配失败;FcErr=2:在函数递归时使用该函数。
23.3.2、调用二级函数 CalFun(m,"aa",x1,x2,...,xn):
只能调用整数或实数二级函数,复数二级函数可直接调用,无需使用该函数。m=4时调用整数二级函数;m=5时调用实数二级函数。"aa"为要调用的二级函数名称。x1,x2,...,xn为二级函数的自变量,只将复数的实部传给该自变量。该函数返回时,实部为二级函数的值,虚部为0。
若FcErr=1:该函数至少使用两个参数,或者参数不匹配;FcErr=2:应使用字符串传递二级函数名称;FcErr=3:函数类型参数m非法;FcErr=4:找不到该二级函数;FcErr=5:内存分配失败。
23.3.3、设置常量 const("ConstStr",ConstValue)或const("ConstStr",ConstValue,bMode):
ConstStr:常量名,要符合Forcal标识符的命名规定,否则编译器找不到该常量。
ConstValue:常量的值。当bMode为逻辑假时,不使用该值。
bMode:指出工作方式。若缺省该参数(函数const只有两个参数),创建一个永久性常量,无法删除一个永久性常量,除非Forcal重新初始化。若bMode为逻辑真,创建一个暂时性常量;若bMode为逻辑假,删除一个暂时性常量。暂时性常量保持常量的基本意义,在编译表达式时不可改变其值,但可被const函数删除。
该函数返回值的意义如下:
0:设置成功。
1:已存在该常量。
2:内存分配失败。
3:不能用空字符串作为常量名。
4:参数太多或者太少。
5:需用字符串指出常量名。
6:不能删除该常量。
23.3.4、获得调用其他类型表达式或其他类型二级函数时计算结果的字节值 GetCalByte(&x0,&x1,... ...):
在复数表达式中调用整数表达式或者实数表达式,以及用CalFun函数调用整数或实数二级函数之后,紧接着调用该函数,可以获得计算结果的按字节复制来的值。由于Forcal表达式中,整数为4个字节,实数为8个字节,复数为16个字节,所以要获得整数或复数结果,仅需用一个参数就可以了,多余的参数被忽略。若复制整数结果,则整数结果仅占复数的前4个字节,其余字节无意义,且函数返回时,整数结果也保存在返回值的前4个字节中。若复制实数结果,则实数结果仅占复数的前8个字节,其余字节无意义,且函数返回时,实数结果也保存在返回值的前8个字节中(复数的实部)。
23.3.5、创建模块命名空间 Module("NameSpace":"name1","name2",... ...):
该函数为调用该函数的模块创建模块命名空间。NameSpace为空间名称,继承自"name1","name2",... ...。模块命名空间可以循环继承。模块命名空间是一棵树或者是一个连通图。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:创建成功。
1:至少需要一个参数,不能缺省模块命名空间名称。
2:不能重复创建模块命名空间,模块命名空间只能创建一次。
3:须用字符串指出模块命名空间名称。
4:内存错误。
5:该命名空间已被其他模块使用。
6:要创建的命名空间不能是空字符串。
23.3.6、输出模块命名空间中的表达式 OutFun("fun1","fun2",... ...):
该函数为模块命名空间输出表达式。模块命名空间即调用函数OutFun的模块的命名空间。须用字符串参数指出表达式的名称。
请参考模块命名空间了解该函数的使用。
该函数返回值的意义如下:
0:操作成功。
i:其他正整数:前i-1个参数操作成功,第i个参数操作失败,其他参数未进行操作。
23.3.7、获得表达式的句柄 HFor("ForName",ForType):
ForName:表达式的名称。可以是简单名称,也可以是包含模块命名空间访问符::(该访问符可简化为一个或多个:)的复杂名称。
ForType:表达式的类型。如果是简单名称,只能取1(标识整数表达式)、2(标识实数表达式)、3(标识复数表达式)。如果名称包含模块命名空间访问符::,只能取-1(标识整数表达式)、-2(标识实数表达式)、-3(标识复数表达式)。
在复数表达式中,表达式句柄保存在复数的前4个字节中。
在一般的编程语言中,关键字是事先定义的有特别意义的字,关键字是保留字,不能用来做标识符(如变量名),使用关键字来做变量名是一种语法错误,不能通过编译。按此定义,则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 | 二级函数 | 获得表达式的句柄。 |
FORCAL中有以下提高效率的方法。
1、将表达式中可以计算的部分放到括号中。
例如,需要将表达式:
F(x,y)=x-5-7+y
写成:F(x,y)=x-[5+7]+y或F(x,y)=x+[-5-7]+y。
2、尽量使用自增减运算符++和--。
例如要把 i=i+1 写成 i++ 形式。
3、until循环函数的速度比while循环函数要快。
4、尽量避免不同类型表达式之间的相互调用,因为类型转换需要时间。尽量少用CalFun(...)函数。
5、尽量避免使用全局变量。
6、类成员运算符“.”将降低编译速度,对运行速度没有影响,如果参数不是一个类,也没有增加程序的易读性,不要用该运算符。
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月11日<!--webbot bot="Timestamp" i-checksum="1297" endspan -->