Haskellのお勉強 その15
共有体スタイルで代数的データ型を使う
共用体スタイルでは、たとえば次のような感じで代数的データ型を宣言する。data PTItem = Param Int | Text String
上記のようにdata宣言を書くと、PTItem型が2つの代数的データ型「Param Int」と「Text String」の
いずれかであるという宣言となる。こうすると、「Param Int」と「Text String」はそれぞれ
独立のdata宣言で定義したのと同じように扱うことができる。
Text "asdfdsa" Text "jkl;lkj" Param 1114 Param 1231
共有体スタイルの代数的データ型のフィールドにアクセスするには、
構造体スタイルの場合と同様にパターンマッチを用いる。
data PTItem = Param Int | Text String isText :: PTItem -> Bool isText (Text _) = True isText (Param _) = False isNumeric :: PTItem -> Bool isNumeric (Text _) = False isNumeric (Param _) = True text :: PTItem -> String text (Text s) = s text (Param i) = show i main = do print $ isText (Text "asdfdsa") print $ isText (Text "jkl;lkj") print $ isText (Param 1114) print $ isText (Param 1231) print $ isNumeric (Text "asdfdsa") print $ isNumeric (Text "jkl;lkj") print $ isNumeric (Param 1114) print $ isNumeric (Param 1231) print $ text (Text "asdfdsa") print $ text (Text "jkl;lkj") print $ text (Param 1114) print $ text (Param 1231)
実行結果は
True True False False False False True True "asdfdsa" "jkl;lkj" "1114" "1231"
ここではPTItem型の値が「Textというデータコンストラクタ」で生成されたのか、
あるいは「Paramというデータコンストラクタ」で生成されたのかをパターンマッチによって判別している。
このようにすると、共用体スタイルで宣言された代数的データ型の値について、
実際の値がどのデータコンストラクタを使っているかを判断することができる。
再帰的な型
data Stack a = Empty | Push a (Stack a)
上記のようにdata宣言を書くと、「Stack a」型は「Empty」か「Push a (Stack a)」の
いずれかだと定義したことになる。ここからは4つのことがわかる。
まず、共用体スタイルの宣言に、データコンストラクタのみの選択肢があってもかまわないこと。(ここではEmpty)
次に、共用体スタイルの宣言でも型変数を使って多相的な型を宣言できること。
そして、「(Stack a)」のように、型をまとめるためにカッコを使ってもいいこと。
最後に、代数的データ型の宣言中に、宣言中のその型自身を使ってもいいということがわかる。
このように定義した「Stack a」型は、次のようにして値を生成することができる。
Empty Push 1 (Empty) Push 2 (Push 1 (Empty)) Push 3 (Push 2 (Push 1 (Empty))) Push 4 (Push 3 (Push 2 (Push 1 (Empty)))) Push 5 (Push 4 (Push 3 (Push 2 (Push 1 (Empty)))))
上記はいずれも「Stack a」型の値となる。
「Stack a」型の値に対してフィールドにアクセスしたいときは、次のように書く
data Stack a = Empty | Push a (Stack a) isEmpty :: Stack a -> Bool isEmpty Empty = True isEmpty (Push _ _) = False top :: Stack a -> a top (Push x _) = x pop :: Stack a -> Stack a pop (Push _ stk) = stk x = Push 5 (Push 4 (Push 3 (Push 2 (Push 1 (Empty))))) y = Push 5 $ Push 4 $ Push 3 $ Push 2 $ Push 1 $ Empty main = do print $ top x print $ top $ pop x print $ isEmpty x print $ isEmpty $ pop $ pop $ pop $ pop $ pop $ x print $ isEmpty $ pop $ pop $ pop $ pop $ pop $ y
実行結果は
5 4 False True