
列挙型を扱う
クラスを扱う
型に別名を付ける
1列挙型を扱う
1.1列挙型を作る
列挙型とは、いくつかの取り得る値を定めた型のことで、例えば「dog」「cat」「fox」のいずれかの値になる「Animal」型といったものを作ることができます。
列挙型を作成するには「enum 型名」から「end enum」の間に取り得る値の名前を書いていきます(図1-1)。
- enum Animal
- dog
- cat
- fox
- end enum
-
- func main()
- var animal: @Animal
- do animal :: %dog
- var isCat: bool :: animal = %cat {false}
- end func
1~4行目で列挙型「@Animal」を宣言することで、8行目のように型として扱うことができます。
9~10行目のように「%値の名前」と書くと、列挙型の値として解釈されます。
どの列挙型の値なのかは型を推論して決定されますが、型が決定できないときにはコンパイルエラーとなり、その場合には明示的に「%値の名前 $ 列挙型の名前」とキャストして示す必要があります(図1-2)。
- enum Animal
- dog
- cat
- fox
- end enum
-
- func main()
- var array: []@Animal :: [%dog $ @Animal, %fox] {[%dog, %fox]}
- end func
1.2列挙型の値に数値を持たせる
列挙型の各値にint型の数値を持たせることができます。 「列挙型の値 :: int型の値」と書きます(図1-3)。
- enum Animal
- dog
- cat :: 5
- fox
- end enum
-
- func main()
- var animal: @Animal
- var n: int
- do animal :: %dog
- do n :: animal $ int {0}
- do animal :: %cat
- do n :: animal $ int {5}
- do animal :: %fox
- do n :: animal $ int {6}
- end func
3行目で、列挙型の値「cat」にint型の値「5」を設定しています。 このように値を設定しなかった場合にも、自動でint型の値が割り当てられ、「0」から順に「1」「2」…と1ずつ増やしながら値が設定されます。
数値を明示した場合には、その次の要素はその数値から1ずつ増やしながら設定されるため、上記のプログラムでは結果的に「dog」は「0」、「cat」は「5」、「fox」は「6」と割り当てられます。
列挙型の中で割り当てられる数値が重複してはならず、コンパイルエラーとなります。 また割り当てた数値は11行目のようにint型にキャストすることで取り出せます。
列挙型の値は大小の比較が可能です。 大小関係は、割り当てられた数値の大小に基づいて比較されます(図1-4)。
- enum Animal
- dog
- cat :: 5
- fox
- end enum
-
- func main()
- var animal1: @Animal :: %dog
- var animal2: @Animal :: %fox
- var b: bool :: animal1 < animal2 {true}
- end func
1.3列挙型の数値でビット演算を行う
列挙型に割り当てられた数値は、bit64型と同様にビット演算を行うことができ、要素に包含関係を持たせることができます。 「.or」「.and」「.not」「.xor」メソッドが使えます(図1-5)。
- enum Color
- red :: 0x01
- green :: 0x02
- blue :: 0x04
- end enum
-
- func main()
- var red: @Color :: %red
- var yellow: @Color :: red.or(%green)
- var isGreen: bool :: yellow.and(%green) = %green {true}
- end func
この例では、9行目で「%red」と「%green」の和集合(ビット和)を「%yellow」と定義し、10行目で「%yellow」に「%green」が含まれるかどうかを共通部分(ビット積)で判定しています。
2クラスを扱う
2.1クラスを作る
クラスとは、複数の変数や関数を1つにまとめて、新しく型を作ったものです。
クラスを作成するには「class 型名()」から「end class」の間に変数や関数などの要素を書いていきます(図2-1)。
- class Person()
- +var name: []char
- var age: int
-
- +func setAge(n: int)
- do me.age :: n
- end func
- end class
-
- func main()
- var person: @Person :: #@Person
- do person.name :: "Kuina"
- do person.setAge(6)
- end func
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)。
- class Person()
- +var name: []char
- var age: int
-
- +func setAge(n: int)
- do me.age :: n
- end func
- end class
-
- class Student(@Person)
- +var mathScore: int
-
- +*func setAge(n: int)
- do super(me, n)
- do me.age :+ 1
- end func
- end class
-
- func main()
- var student: @Student :: #@Student
- do student.name :: "Kuina"
- do student.setAge(6)
- do student.mathScore :: 100
- var person: @Person :: student
- if(person =$ @Student) {true}
- var student2: @Student :: person $ @Student
- end if
- end func
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)。
- class Person()
- *func ctor()
- do me.name :: "Kuina"
- do me.age :: 6
- end func
-
- +var name: []char
- var age: int
- end class
-
- func main()
- var person: @Person :: #@Person
- end func
2~5行目で「ctor」関数をオーバーライドして、各変数に値を代入しています。 このように書くことで、12行目でインスタンスが生成されたときに変数の初期値を設定することができます。
2.4クラスを比較可能にする
クラスを「=」「<>」「<」「>」「<=」「>=」演算子や、配列の「.sort」メソッドなどで扱えるように比較可能にするには、「kuin@Class」に定義されている「cmp」関数をオーバーライドします(図2-4)。
- class Person()
- +*func cmp(t: kuin@Class): int
- var t2: @Person :: t $ @Person
- if(me.name = t2.name)
- ret 0
- else
- ret me.name > t2.name ?(1, -1)
- end if
- end func
-
- +var name: []char
- end class
-
- func main()
- var person1: @Person :: #@Person
- do person1.name :: "abc"
- var person2: @Person :: #@Person
- do person2.name :: "def"
- var b: bool :: person1 < person2 {true}
- end func
「cmp」関数は、自身のインスタンスと引数の「t」を比べ、自身が大きければ正、小さければ負、等しければ0の値を返すようにします。 こうすることで、19行目のように大小比較ができます。
3型に別名を付ける
3.1型に別名を付ける
型に別名を付けるには「alias 新しい型名: 型」と書きます(図3-1)。
- func main()
- alias Items: dict<int, [][]char>
-
- var items: Items :: #Items
- end func
2行目で「dict<int, [][]char>」型に対して「Items」という名前を付け、4行目で使っています。
このように、複雑な型に別名を付けることでソースコードを読みやすくできます。 ただし多用すると元の型が判りにくくなり、かえって読みづらくなるため、必要最小限の使用を推奨します。