2023年06月10日くいなちゃん


プログラミング言語Kuin」の実行環境がexe用の逆引き辞典4、列挙型とクラスとエイリアスの扱い方についてです。

列挙型を扱う

クラスを扱う

型に別名を付ける

目次

1列挙型を扱う

1.1列挙型を作る



列挙型とは、いくつかの取り得る値を定めた型のことで、例えば「dog」「cat」「fox」のいずれかの値になる「Animal」型といったものを作ることができます。
列挙型を作成するには「enum 型名」から「end enum」の間に取り得る値の名前を書いていきます(図1-1)。
  1. enum Animal
  2.   dog
  3.   cat
  4.   fox
  5. end enum
  6.  
  7. func main()
  8.   var animal: @Animal
  9.   do animal :: %dog
  10.   var isCat: bool :: animal = %cat {false}
  11. end func
図1-1: 列挙型
1~4行目で列挙型「@Animal」を宣言することで、8行目のように型として扱うことができます。
9~10行目のように「%値の名前」と書くと、列挙型の値として解釈されます。
どの列挙型の値なのかは型を推論して決定されますが、型が決定できないときにはコンパイルエラーとなり、その場合には明示的に「%値の名前 $ 列挙型の名前」とキャストして示す必要があります(図1-2)。
  1. enum Animal
  2.   dog
  3.   cat
  4.   fox
  5. end enum
  6.  
  7. func main()
  8.   var array: []@Animal :: [%dog $ @Animal, %fox] {[%dog, %fox]}
  9. end func
図1-2: 型の明示

1.2列挙型の値に数値を持たせる



列挙型の各値にint型の数値を持たせることができます。 「列挙型の値 :: int型の値」と書きます(図1-3)。
  1. enum Animal
  2.   dog
  3.   cat :: 5
  4.   fox
  5. end enum
  6.  
  7. func main()
  8.   var animal: @Animal
  9.   var n: int
  10.   do animal :: %dog
  11.   do n :: animal $ int {0}
  12.   do animal :: %cat
  13.   do n :: animal $ int {5}
  14.   do animal :: %fox
  15.   do n :: animal $ int {6}
  16. end func
図1-3: 列挙型の値の数値
3行目で、列挙型の値「cat」にint型の値「5」を設定しています。 このように値を設定しなかった場合にも、自動でint型の値が割り当てられ、「0」から順に「1」「2」…と1ずつ増やしながら値が設定されます。
数値を明示した場合には、その次の要素はその数値から1ずつ増やしながら設定されるため、上記のプログラムでは結果的に「dog」は「0」、「cat」は「5」、「fox」は「6」と割り当てられます。
列挙型の中で割り当てられる数値が重複してはならず、コンパイルエラーとなります。 また割り当てた数値は11行目のようにint型にキャストすることで取り出せます。
列挙型の値は大小の比較が可能です。 大小関係は、割り当てられた数値の大小に基づいて比較されます(図1-4)。
  1. enum Animal
  2.   dog
  3.   cat :: 5
  4.   fox
  5. end enum
  6.  
  7. func main()
  8.   var animal1: @Animal :: %dog
  9.   var animal2: @Animal :: %fox
  10.   var b: bool :: animal1 < animal2 {true}
  11. end func
図1-4: 列挙型の大小

1.3列挙型の数値でビット演算を行う



列挙型に割り当てられた数値は、bit64型と同様にビット演算を行うことができ、要素に包含関係を持たせることができます。 「.or」「.and」「.not」「.xor」メソッドが使えます(図1-5)。
  1. enum Color
  2.   red :: 0x01
  3.   green :: 0x02
  4.   blue :: 0x04
  5. end enum
  6.  
  7. func main()
  8.   var red: @Color :: %red
  9.   var yellow: @Color :: red.or(%green)
  10.   var isGreen: bool :: yellow.and(%green) = %green {true}
  11. end func
図1-5: 列挙型のビット演算
この例では、9行目で「%red」と「%green」の和集合(ビット和)を「%yellow」と定義し、10行目で「%yellow」に「%green」が含まれるかどうかを共通部分(ビット積)で判定しています。

2クラスを扱う

2.1クラスを作る



クラスとは、複数の変数や関数を1つにまとめて、新しく型を作ったものです。
クラスを作成するには「class 型名()」から「end class」の間に変数や関数などの要素を書いていきます(図2-1)。
  1. class Person()
  2.   +var name: []char
  3.   var age: int
  4.  
  5.   +func setAge(n: int)
  6.     do me.age :: n
  7.   end func
  8. end class
  9.  
  10. func main()
  11.   var person: @Person :: #@Person
  12.   do person.name :: "Kuina"
  13.   do person.setAge(6)
  14. end func
図2-1: クラス
1~8行目でクラス「@Person」を宣言することで、11行目のように型として扱うことができます。 11行目のように「#クラス名」と書くと、そのクラスのインスタンスが生成されます。
12~13行目のように「.メンバ名」と書くことで、クラスが持つ変数や関数にアクセスできます。
2行目や5行目のように、クラスの変数と関数の定義の先頭に「+」を書くと、その変数や関数は12~13行目のように外部からアクセスできます。 3行目の「age」のように「+」を書かなければ外部からアクセスできず、「person.age」とアクセスしてもコンパイルエラーが発生します。 必要最小限の要素を外部に公開することで、予期しない変数の書き換えを防ぐことができます。
クラスのメンバ関数内で自身のインスタンスを参照するには、6行目のように「me」と書きます。 13行目の「person.setAge」を呼び出すと、6行目の「me.age :: n」が実行され、結果的に「person.age」に値が代入されます。

2.2クラスを継承する



あるクラスの変数や関数を引き継いで(継承)、新しいクラスを作成することができます。 継承してクラスを作成するには「class 型名(継承元クラス)」と書きます(図2-2)。
  1. class Person()
  2.   +var name: []char
  3.   var age: int
  4.  
  5.   +func setAge(n: int)
  6.     do me.age :: n
  7.   end func
  8. end class
  9.  
  10. class Student(@Person)
  11.   +var mathScore: int
  12.  
  13.   +*func setAge(n: int)
  14.     do super(me, n)
  15.     do me.age :+ 1
  16.   end func
  17. end class
  18.  
  19. func main()
  20.   var student: @Student :: #@Student
  21.   do student.name :: "Kuina"
  22.   do student.setAge(6)
  23.   do student.mathScore :: 100
  24.   var person: @Person :: student
  25.   if(person =$ @Student) {true}
  26.     var student2: @Student :: person $ @Student
  27.   end if
  28. end func
図2-2: クラスの継承
10行目では、「@Person」クラスを継承して「@Student」クラスを作成しています。 「@Student」クラスは「@Person」のすべての変数や関数を引き継ぎ、21~22行目のようにアクセスできます。 また11行目で「@Student」に追加した変数にも23行目のようにアクセスできます。
また24行目のように、継承したクラスのインスタンス「@Student」は継承元クラスの型「@Person」とみなして扱うことができます。 逆に26行目のように、継承元クラスの型「@Person」から継承先クラスの型「@Student」に変換するときには明示的なキャストが必要で、本来のインスタンスではない型にキャストすると例外が発生します。
インスタンスがあるクラス型にキャスト可能かどうかは、25行目のように「=$」演算子で判定できます。
継承するときに、継承元の関数の処理を上書きすることができます(関数のオーバーライド)。 関数をオーバーライドするには13行目のようにfuncの直前に「*」を書き、オーバーライド元と同じ関数名、引数、戻り値にします。 関数内でオーバーライド元の関数を呼び出すこともでき、14行目のように「super(me, 第1引数, 第2引数, ...)」と書きます。 戻り値もあれば受け取れます。
継承元で「+」を付けなかった要素にも、継承先からはアクセスすることができます。 15行目のように「me.age」とアクセスしても問題ありません。

2.3クラスのインスタンス生成時に処理を行う



クラスのインスタンスが生成されると、すべてのクラスの継承元クラスである「kuin@Class」に定義されている「ctor」という関数が呼ばれます。 これをオーバーライドすることで、インスタンス生成時に処理が行われるようにできます(図2-3)。
  1. class Person()
  2.   *func ctor()
  3.     do me.name :: "Kuina"
  4.     do me.age :: 6
  5.   end func
  6.  
  7.   +var name: []char
  8.   var age: int
  9. end class
  10.  
  11. func main()
  12.   var person: @Person :: #@Person
  13. end func
図2-3: コンストラクタ
2~5行目で「ctor」関数をオーバーライドして、各変数に値を代入しています。 このように書くことで、12行目でインスタンスが生成されたときに変数の初期値を設定することができます。

2.4クラスを比較可能にする



クラスを「=」「<>」「<」「>」「<=」「>=」演算子や、配列の「.sort」メソッドなどで扱えるように比較可能にするには、「kuin@Class」に定義されている「cmp」関数をオーバーライドします(図2-4)。
  1. class Person()
  2.   +*func cmp(t: kuin@Class): int
  3.     var t2: @Person :: t $ @Person
  4.     if(me.name = t2.name)
  5.       ret 0
  6.     else
  7.       ret me.name > t2.name ?(1, -1)
  8.     end if
  9.   end func
  10.  
  11.   +var name: []char
  12. end class
  13.  
  14. func main()
  15.   var person1: @Person :: #@Person
  16.   do person1.name :: "abc"
  17.   var person2: @Person :: #@Person
  18.   do person2.name :: "def"
  19.   var b: bool :: person1 < person2 {true}
  20. end func
図2-4: クラスの比較
「cmp」関数は、自身のインスタンスと引数の「t」を比べ、自身が大きければ正、小さければ負、等しければ0の値を返すようにします。 こうすることで、19行目のように大小比較ができます。

3型に別名を付ける

3.1型に別名を付ける



型に別名を付けるには「alias 新しい型名: 」と書きます(図3-1)。
  1. func main()
  2.   alias Items: dict<int, [][]char>
  3.  
  4.   var items: Items :: #Items
  5. end func
図3-1: エイリアス
2行目で「dict<int, [][]char>」型に対して「Items」という名前を付け、4行目で使っています。
このように、複雑な型に別名を付けることでソースコードを読みやすくできます。 ただし多用すると元の型が判りにくくなり、かえって読みづらくなるため、必要最小限の使用を推奨します。
1686379282jaf