欢迎访问 Forcal程序设计
Forcal中的类模块及数据结构
著名的瑞士计算机科学家沃思(N.Wirth)教授曾提出:算法+数据结构=程序。在Forcal中,算法是用函数来描述的,而模块是函数的集合,因而Forcal的模块即代表了算法。你也许会感到意外,Forcal模块由Forcal核心库来实现,但Forcal的数据结构却主要是由Forcal扩展动态库FcData实现的。在FcData中主要通过类的概念实现Forcal的数据结构,所有的FcData数据都通过指针进行标识。
在C++中通过类实现了面向对象设计,在Forcal中与此相关的概念是类模块(FcData中的类与Forcal模块的结合)。与C++中的类定义不同,FcData中的类与Forcal模块是相互独立的。C++是高效的静态语言,类函数必须知道类的结构才能工作,而Forcal是动态编译的,在运行前无需也不可能知道类的结构,因而也没有必要将类结构与模块函数绑定到一起。因而有理由认为:FcData中的类与Forcal模块相互独立是Forcal的一个优点。
以下函数OutClass可以输出类的结构,本文中的例子将直接使用该函数,不再给出函数的实现。要演示这些例子,将该函数放到例子的前面和例子一起编译即可。
i:OutClass(pClass,n:len,i,j,str,BType,PArray,nPArray,pc)= //pClass是类指针,n是缓冲区大小,n太小时,有些基类将无法输出
{
printff{"\r\n\r\n***** 类简易分析程序 *****"},
PArray=new[pointer_s,n],nPArray=CDFSBase[pClass,PArray],
str="\[80]",
i=0,
(i<nPArray).while{
pc=PArray.get(i),len=FCDLen(pc),
printff{"\r\n\r\n类:{1,i}\r\n",pc},
j=0,
(j<len).while{
CGetNameI[pc,j,str,80],
printff{"\r\n类成员:{1,ns,-20}指针:{2,i,-12}删除属性:{3,i,-5}数据类别:",str,pc.CGetPI[j],pc.CGetAI[j]},
FCDType[pc.CGetPI[j],&BType],
which{
BType==int8, printff{"int8"},
BType==Uint8, printff{"Uint8,char"},
BType==int16, printff{"int16"},
BType==Uint16, printff{"Uint16"},
BType==wchar, printff{"wchar"},
BType==int32, printff{"int32"},
BType==Uint32, printff{"Uint32"},
BType==int64, printff{"int64,int"},
BType==Uint64, printff{"Uint64,pointer"},
BType==real32, printff{"real32"},
BType==real64, printff{"real64,real"},
BType==complex, printff{"complex"},
BType==int8_s, printff{"int8_s"},
BType==Uint8_s, printff{"Uint8_s,char_s"},
BType==int16_s, printff{"int16_s"},
BType==Uint16_s, printff{"Uint16_s"},
BType==wchar_s, printff{"wchar_s,string"},
BType==int32_s, printff{"int32_s"},
BType==Uint32_s, printff{"Uint32_s"},
BType==int64_s, printff{"int64_s,int_s"},
BType==Uint64_s, printff{"Uint64_s,pointer_s"},
BType==real32_s, printff{"real32_s"},
BType==real64_s, printff{"real64_s,real_s"},
BType==complex_s,printff{"complex_s"},
BType==class, printff{"class,基类"},
BType==0, printff{"非FcData数据"},
printff{"外部数据类型"}
},
j++
},
i++
},
printff{"\r\n\r\n"},
if{nPArray==n,printff{"警告:指定的缓冲区太小,可能有些基类无法搜索到。\r\n\r\n"}},
printff{"***** 类分析结束 *****\r\n\r\n"},
delete[PArray]
};
1 Forcal模块命名空间
使用命名空间可以有效地避免函数重名问题。一个Forcal模块命名空间是一棵树或者是一张连通图。Forcal所有的模块命名空间是一张图。图包含了树,因此下面的例子是用图来说明的。
图1是Forcal中可能存在的某种模块布局:
图1 模块命名空间举例
图中每个文本框表示一个模块,文本框内冒号前为模块名,冒号与分号之间为该模块的基模块,分号之后为该模块通过命名空间输出的函数,模块私有函数没有包含在该说明中。例如:
B:C,D,E;
set,get,me
表示模块B继承自模块C,D,E,模块B通过命名空间输出的函数为set,get和me。该模块命名空间定义及输出函数说明用Forcal代码表示为:
#MODULE# //定义模块B
!Module("B":"C","D","E"); //定义模块命名空间
... ...; //模块中的函数定义
!OutFun("set","get","me"); //输出模块命名空间中的函数#END# //模块B定义结束
注意到函数Module和OutFun前面有一个感叹号,表示该函数编译后将立即执行。本例中,如果Module和OutFun没有执行,Forcal编译器将不知道模块命名空间B及其输出函数的存在,后续的编译过程将无法正常进行。
图1中所有模块的一种定义如下:
//单个模块
#MODULE# //定义模块A
!Module("A"); //定义模块命名空间
me()=1; //模块中的函数定义
!OutFun("me"); //输出模块命名空间中的函数
#END# //模块A定义结束//树形模块
#MODULE# //定义模块B
!Module("B":"C","D","E"); //定义模块命名空间
set(x::xx)=xx=x; //模块中的函数定义
get(::xx)=xx; //模块中的函数定义
me()=2; //模块中的函数定义
!OutFun("set","get","me"); //输出模块命名空间中的函数
#END# //模块B定义结束#MODULE# //定义模块C
!Module("C");
set(x::xx)=xx=x;
get(::xx)=xx;
cc()=3;
!OutFun("set","get","cc");
#END##MODULE# //定义模块D
!Module("D":"F","G");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=4;
!OutFun("set","get","me");
#END##MODULE# //定义模块E
!Module("E":"H");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=5;
!OutFun("set","get","me");
#END##MODULE# //定义模块F
!Module("F");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=6;
!OutFun("set","get","me");
#END##MODULE# //定义模块G
!Module("G");
set(x::xx)=xx=x;
get(::xx)=xx;
gg()=7;
!OutFun("set","get","gg");
#END##MODULE# //定义模块H
!Module("H");
set(x::xx)=xx=x;
get(::xx)=xx;
hh()=8;
!OutFun("set","get","hh");
#END#//连通图形模块
#MODULE# //定义模块I
!Module("I":"J","K","L"); //定义模块命名空间
set(x::xx)=xx=x; //模块中的函数定义
get(::xx)=xx; //模块中的函数定义
me()=9; //模块中的函数定义
!OutFun("set","get","me"); //输出模块命名空间中的函数
#END# //模块B定义结束#MODULE# //定义模块J
!Module("J":"K","M");
set(x::xx)=xx=x;
get(::xx)=xx;
jj()=10;
!OutFun("set","get","jj");
#END##MODULE# //定义模块K
!Module("K":"M","N");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=11;
!OutFun("set","get","me");
#END##MODULE# //定义模块L
!Module("L":"P");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=12;
!OutFun("set","get","me");
#END##MODULE# //定义模块M
!Module("M");
set(x::xx)=xx=x;
get(::xx)=xx;
me()=13;
!OutFun("set","get","me");
#END##MODULE# //定义模块N
!Module("N");
set(x::xx)=xx=x;
get(::xx)=xx;
nn()=14;
!OutFun("set","get","nn");
#END##MODULE# //定义模块P
!Module("P":"I");
set(x::xx)=xx=x;
get(::xx)=xx;
pp()=15;
!OutFun("set","get","pp");
#END#
下面是一些测试代码,你可以将这些测试代码添加到上面的模块代码的最后进行测试,或者是先将上面的代码保存为模块文件并进行编译,然后输入下面的测试代码进行测试。
例1 直接调用不同模块命名空间中的同名函数
A::me();
B::me();
F::me();
I::me();
M::me();
例2 间接调用模块命名空间B中的函数
B::cc();
B::gg();
B::hh();
例3 间接调用模块命名空间I或P中的函数
I::jj();
I::nn();
I::pp();
L::jj();
L::nn();
L::pp();
例4 模块命名空间的多级调用
B::me();
B::D::me();
B::D::F::me();
L::me();
L::P::me();
L::P::I::me();
L::P::I::J::me();
L::P::I::J::K::me();
L::P::I::J::K::M::me();
例5 通过多个模块命名空间间接调用同一个函数
I::nn();
J::nn();
K::nn();
L::nn();
P::nn();
例6 模块命名空间的循环调用
I::nn();
I::L::nn();
I::L::P::nn();
I::L::P::I::nn();
I::L::P::I::L::nn();
I::L::P::I::L::P::nn();
以上例子中没有测试模块命名空间中的函数set和get,大家自己可以进行测试。你也可以在任意的模块中增加私有函数,看其他模块能否调用。
2 FcData中的类
FcData中的类通过函数new(class,EndType,"a","b",... ...)定义。关键字class说明要定义一个类。字符串"a","b",... ...定义了类的成员,每一个字符串标识一个类成员,区分大小写。注意类成员名称不要重复,否则后定义的类成员将无法访问。在类定义时无法确定类成员的类型,类成员的类型在申请类对象并进行赋值时确定,类成员的类型可以是一个类对象。
定义完类后,可以通过函数CSetItem(pClass:"a",x:"b",y,NotDelete:... ...)给类的成员赋值。pClass是类指针。本例中,给成员"a"赋初值为x;给成员"b"赋初值为y,关键字NotDelete指出,删除该类对象时,不删除y。一般情况下,x和y都是FcData数据指针,当然也可以是任意的整数。
每一个类成员都有删除属性,如果属性为true,在销毁类对象时,将先销毁该类成员,如果属性为false,将不销毁该类成员。在创建类对象时,类成员的删除属性缺省情况下为true,但可以用关键字NotDelete指明该类成员的删除属性为false。对于已经存在的类对象,可以用函数CGetA()和CSetA()获得或设置类成员及属性。
如果类A是类B的对象成员,则称类A为类B的基类(父类),类B为类A的派生类(子类)。类可以多重继承,也可以形成循环链表互为基类或派生类。可以用函数CSD()或CSB()获得基类的对象成员,这两个函数搜索基类对象成员的方法不同,CSD()是深度优先的,而CSB()广度优先。
类通常是一棵树或者是一张连通图。FcData中所有的类是一张图。图包含了树,因此下面的例子是用图来说明的。
图2是FcData中可能存在的某种类定义的布局:
图2 类定义举例
你一定注意到了图1和图2是完全相同的。但图1是模块命名空间的布局,图2是类定义的布局,有本质上的区别。模块命名空间是函数的集合,因而按图1实现的函数集,在内存中只能存在一个;而按图2的类定义实现的类对象,在内存中可存在任意多个。
图中每个文本框表示一个类定义,文本框中的每一个字符串都表示一个类的成员,类的成员可以是任意一个FcData指针,当然也可以是一个类指针,如果是一个类指针,表示了类的继承关系。为了方便,规定:冒号前的类成员表示该类的名称(但类的名称不是该类的唯一标识,类的唯一标识是类指针);冒号与分号之间的类成员为类指针,表示该类的基类;分号之后为该类的普通成员(非类成员)。例如:
B:C,D,E;
set,get,me
表示类B继承自类C,D,E,有三个普通的类成员set,get和me。该类定义及类对象实现用Forcal代码表示为:
pB=new(class,EndType,"B":"C","D","E":"set","get","me"), //定义类,返回一个类指针pB
pB.CSetItem("C",pC,"D",pD,"E",pE:"me",new(int)); //给类pB赋初值,为了简单,"B","set","get"等类成员没有赋值
下面我们实现图2的类定义及类对象,我们用函数A实现(a)单个类,用函数B实现(b)树形类,用函数I实现(c)连通图形类。每一个函数被调用时,都返回一个相应的类对象指针,通过该指针可访问该类或其基类的任意一个对象成员。在这些例子中没有自己管理申请的内存,是由系统自动回收的。
i::A()={ //i:表示是个整数表达式,第二个冒号表示该函数仅在被调用时执行
new(class,EndType,"A","me").CSetItem["me",new(int,1)] //定义类 ,并给类成员赋值
};i::B(:pB,pC,pD,pE,pF,pG,pH)={
pB=new(class,EndType,"B":"C","D","E":"set","get","me"),
pC=new(class,EndType,"C":"set","get","cc"),
pD=new(class,EndType,"D":"F","G":"set","get","me"),
pE=new(class,EndType,"E":"H":"set","get","me"),
pF=new(class,EndType,"F":"set","get","me"),
pG=new(class,EndType,"G":"set","get","gg"),
pH=new(class,EndType,"H":"set","get","hh"),
pH.CSetItem("hh",new(int,8)),
pG.CSetItem("gg",new(int,7)),
pF.CSetItem("me",new(int,6)),
pE.CSetItem("H",pH:"me",new(int,5)),
pD.CSetItem("F",pF,"G",pG:"me",new(int,4)),
pC.CSetItem("cc",new(int,3)),
pB.CSetItem("C",pC,"D",pD,"E",pE:"me",new(int,2))
};i::I(:pI,pJ,pK,pL,pM,pN,pP)={
pI=new(class,EndType,"I":"J","K","L":"set","get","me"),
pJ=new(class,EndType,"J":"K","M":"set","get","jj"),
pK=new(class,EndType,"K":"M","N":"set","get","me"),
pL=new(class,EndType,"L":"P":"set","get","me"),
pM=new(class,EndType,"M":"set","get","me"),
pN=new(class,EndType,"N":"set","get","nn"),
pP=new(class,EndType,"P":"I":"set","get","pp"),
pP.CSetItem("pp",new(int,15)),
pN.CSetItem("nn",new(int,14)),
pM.CSetItem("me",new(int,13)),
pL.CSetItem("P",pP:"me",new(int,12)),
pK.CSetItem("M",pM,"N",pN:"me",new(int,11)),
pJ.CSetItem("K",pK,"M",pM:"jj",new(int,10)),
pI.CSetItem("J",pJ,"K",pK,"L",pL:"me",new(int,9)),
pP."I".CSetP[pI], //让类对象pP的类成员"I"指向类对象pI
pI
};
下面是一些测试代码,你可以将这些测试代码添加到上面的模块代码的最后进行测试。注意这些例子中使用的函数get不是文本框中定义的类的成员"get",该函数由FcData定义,可得到一个FcData数据的值。
例1 输出类的结构:用到了本文开头定义的函数OutClass。
i: A().OutClass(8);
i: B().OutClass(8);
i: I().OutClass(8);
例2 直接输出不同类中的同名成员
i: A()."me".get(); //i:表示是个整数表达式,下同
i: B()."me".get();
i: I()."me".get();
例3 间接输出类B中的成员:尽管每一次函数调用B()返回的对象指针都不同,但 由前面的定义可知,所有类B的相应的基类对象中的"cc"、"gg"和"hh"都相同。
i: B().CSB("cc").get(); //用函数CSB得到基类成员指针(广度优先),下同
i: B().CSB("gg").get();
i: B().CSB("hh").get();
例4 直接输出类B中的成员:尽管每一次函数调用B()返回的对象指针都不同, 但由前面的定义可知,但所有类B的相应的基类对象中的"me"都相同。
i: B()."me".get();
i: B()."D"."me".get(); //输出基类成员D的"me"成员
i: B()."D"."F"."me".get(); //输出基类成员F的"me"成员
例5 输出类I中的成员:注意每一次函数调用I()返回的对象指针都不同, 但不影响演示这个例子。
i: I().CSB("jj").get();
i: I().CSB("nn").get();
i: I().CSB("pp").get();i: I().CSB("P").CSB("jj").get(); //先用函数CSB得到基类成员P的指针,再用函数CSB得到类对象P的基类成员jj的指针,下面与此类似
i: I().CSB("P").CSB("nn").get();
i: I().CSB("P").CSB("pp").get();i: I().CSB("J").CSB("me").get();
i: I().CSB("K").CSB("me").get();
i: I().CSB("L").CSB("me").get();
i: I().CSB("M").CSB("me").get();
i: I().CSB("P").CSB("me").get();
例6 类成员的循环调用
i: I()."me".get();
i: I()."L"."me".get();
i: I()."L"."P"."pp".get();
i: I()."L"."P"."I"."me".get();
i: I()."L"."P"."I"."L"."me".get();
i: I()."L"."P"."I"."L"."P"."pp".get();
例7 类P的成员"get"的使用举例
i: (::pI)= pI=I(), pI.CSB("P")."get".CSetP[new(int,111)];
i: (::pI)= pI.CSB("P")."get".get();
i: (::pI)= pI."L"."P"."get".get();i: (::pP)= pP=I().CSB("P"), pP."get".CSetP[new(int,222)];
i: (::pP)= pP."get".get();
3 Forcal类模块
FcData中的类与Forcal模块的结合即类模块,相当于C++中的类。下面举一个例子说明一下。
在这个例子中,类Num3有三个数据成员"a"、"b"和"c",用于表示三角形的三条边;模块Area有三个输出函数NewNum3、SetNum3和Area3,NewNum3用于获得一个Num3类对象,SetNum3用于设置三角形的三条边,Area3用于计算三角形的面积。 模块中的函数和数据都是整数。代码如下:
#MODULE# //定义模块
!Module("Area");
i::NewNum3()= //申请类Num3,该函数不会自动执行
{
new(class,EndType:"a","b","c").CSetItem["a",new(int),"b",new(int),"c",new(int)]
};
i: SetNum3(p,a,b,c)= p."a".set(a), p."b".set(b), p."c".set(c), p; //设置三角形的三条边F(a,b,c:s)= s=(a+b+c)/2,sqrt[s*(s-a)*(s-b)*(s-c)]; //定义三角形面积公式,私有函数
i: Area3(p)= //调用私有函数F计算三角形面积
{
F[p."a".get().itor(), p."b".get().itor(), p."c".get().itor()].rtoi()
};
!OutFun("NewNum3","SetNum3","Area3");#END#
i: (:p)= p=Area::NewNum3(), //申请Num3对象
p.Area::SetNum3(3,4,5), //Num3对象赋值
p.Area::Area3(); //计算Num3的面积i: Area::NewNum3().Area::SetNum3(30,40,50).Area::Area3();
类模块可以实现C++中类的功能,大家自己可以给出这方面的例子。
版权所有© Forcal程序设计
2002-2010,保留所有权利
E-mail: forcal@sina.com
QQ:630715621
最近更新:
2010年01月23日