[NAME]
ALL.dao.type.enum

[TITLE]
枚举符号类型

[DESCRIPTION]

枚举类型enum定义了一组名称符号,其中每个符号表示一个特定的整数值。 这种类型可用于表示一组状
态或标识。 道语言还支持了一种称作符号特殊的枚举类型,使得枚举类型的使用变得极为方便。

 0.1  定义 
     
   1  EnumEntry ::= Identifier [ = ConstExpr ]
   2  
   3  Enum1 ::= 'enum' Identifier '{' EnumEntry { ',' EnumEntry } '}'
   4  Enum2 ::= 'enum' Identifier '{' EnumEntry { ';' EnumEntry } '}'
   5  
   6  Enum ::= Enum1 | Enum2
   7  
   8  EnumType1 ::= 'enum' '<' EnumEntry { ',' EnumEntry } '>'
   9  EnumType2 ::= 'enum' '<' EnumEntry { ';' EnumEntry } '>'
  10  
  11  EnumType ::= EnumType1 | EnumType2
  12  
  13  Symbol ::= '$' Identifier
     
Enum1(EnumType1)的定义与 Enum2(EnumType2)的定义的唯一区别就是 前者使用了逗号,而后者使用了
分号。 这样的结果是,以逗号定义的枚举类型,它们的符号不可组合; 而以分号定义的枚举类型,它们的
符号则可以组合。 为了方便起见,符号可以组合的枚举类型将被称作标识枚举, 而符号不可组合的枚举
类型将被称作状态枚举。

 0.2  状态枚举 

下面是个简单的状态枚举类型。这个定义使用了类似C++里的语法: 
     
   1  enum MyEnum
   2  {
   3      AA    ,  # 0
   4      BB = 3,  # 3
   5      CC       # 4
   6  }
     
跟C++里一样,状态枚举里的第一个符号如果没有显式赋值,那么它将被自动地赋为零。 其它未被赋值的
符号,它们的值将自动地从它们前面符号的值加一获得。

这些符号只能通过相应的枚举类型来访问, 应为它们并没有出现在当前的命名空间。 
     
   1  var a = MyEnum::AA  # Or: MyEnum.AA;
     
因为MyEnum的符号不可组合,变量e将只能取 MyEnum的一个值或符号。

 0.3  符号枚举 

为了方便起见,符号枚举将先于标识枚举被介绍。 道语言里,可直接使用的枚举符号是由普通的标识符前
加货币符号$构成。 它们通常是与枚举类型一起使用才有特定的意义。 
     
   1  enum E1 { AA=1, BB=2 }
   2  enum E2 { AA=10, BB=20 }
   3  
   4  var e1: E1 = $AA
   5  var e2: E2 = $AA
   6  
   7  io.writeln( e1 )  # 输出: $AA(1)
   8  io.writeln( e2 )  # 输出: $AA(10)
     
尽管是同一个符号,"$AA"在分别赋给"e1"和"e2"时,被自动地做了不同的处理。 在赋给"e1"时,"$AA"以
等同E1::AA处理,而在赋给"e2"时,"$AA"以等同E2::AA处理。 因此符号所表达的东西将取决于它被用作
的枚举类型。

 0.4  标识枚举 

标识枚举的定义和状态枚举的定义语法上基本一致,只不过标识枚需要使用分号而不是逗号。 
     
   1  enum MyEnum
   2  {
   3      AA    ;  # 1
   4      BB = 3;  # 3
   5      CC       # 6
   6  }
     
除了语法的差异,标识枚举对未显式赋值的符号的处理也不一样。 这里,标识枚举里的第一个符号如果没
有显式赋值,那么它将被自动地赋为一。 其它未被赋值的符号,它们的值将自动地从它们前面符号的值按
比特位左移一位获得。 值得注意的是,枚举类型里的符号可以被任意赋值,因此,这将取决于用户给它们
赋 有意义的值。

作为标识枚举类型,它们的符号可被自由组合,因此它们可有效地用作标识: 
     
   1  enum Orientation
   2  {
   3      East ;
   4      West ;
   5      South ;
   6      North
   7  }
   8  var orientation = Orientation::East + Orientation::South
   9  io.writeln( orientation )  # Output: $East$South(5)
     


 0.5  枚举类型名 


使用枚举类型的另一种方式是使用枚举类型的类型名: 
     
   1  var s1: enum<AA,BB> = $AA
   2  var s2: enum<AA=10,BB=20> = $BB
   3  var f1: enum<AA;BB;CC> = $AA + $BB
     
类似地,状态枚举和标识枚举的类型名的主要差异是使用逗号或分号。

这种枚举类型名在函数参数里使用非常方便。例如: 
     
   1  routine MySearch( values: list<int>, direction: enum<forward,backward> )
   2  {
   3          ... 
   4  }
   5  var values = { 1, 2, 3 } 
   6  var result = MySearch( values, $forward )
     


 0.6  Switch-Case里使用枚举类型 


枚举类型可有效地用在Switch-Case里。 只要case项里的常量在编译时有确定(显式声明的或推导的)的
枚举类型, 这样的Switch-Case通常可以被优化为简单的跳转表。 例如: 
     
   1  switch( (any)e1 ){
   2  case MyEnum::AA : ...
   3  case MyEnum::BB : ...
   4  case MyEnum::CC : ...
   5  }
   6  
   7  switch( e1 ){
   8  case $AA : ...
   9  case $BB : ...
  10  case $CC : ...
  11  }
     
这两个Switch-Case都将被优化为跳转表,因为前者的Case项都有明确的枚举类型。 而后者的则可通过变
量e的类型来推导出。 但下例则不能作此优化: 
     
   1  switch( (any)e1 ){
   2  case $AA : ...
   3  case $BB : ...
   4  case $CC : ...
   5  }
     
因为switch()里的表达式没有明确的类型, Case项里的符号也没有明确的美剧类型。