Haskell   タプル

ホーム   目次


この章では、タプル、列挙型、直積型、直和型を紹介しています。

タプル

タプル (tuple) は、違うタイプ (type、型)のデータを複数保持できるデータ構造です。タプルに含まれる個々のデータのことを要素 (element)と呼びます。

リストが同じタイプのデータしか保持できないのに対して、タプルは違うタイプのデータを保持できる点が大きな違いです。

また、リストは、内包する要素の型が同じであれば、要素数が違っても同じ型としてみなされますが、タプルは要素数が違うものは違う型とみなされます。

タプルはデータの全体を ( と ) で囲んで、各要素を , (カンマ)で区切って作ります。

タプルを使うと関数の戻り値として複数の値を返すことができるようになります。


addsub x y = (x + y, x - y)

main = do
    print $ addsub 10 5
    

実行結果

(15,5)

タプルは要素を分割して受け取ることも可能です。


addsub x y = (x + y, x - y)
a = addsub 10 5
(a1, a2) = addsub 10 5

main = do
    print a
    print a1
    print a2
    

実行結果

(15,5)
15
5

fst 関数と snd 関数

Haskellには、要素が2つのタプルから値を取得するための、fst 関数と snd 関数があります。

main = do
    let t = (10, 20)
    print $ fst t
    print $ snd t
    

実行結果

10
20

要素が3以上のタプルから要素を取り出すには変数を使います。


main = do
    let t = (10, 20, 30)
    let (x,y,z) = t
    print x
    print y
    print z
    print t
    

実行結果

10
20
30
(10,20,30)

リストと違ってタプルには !! は使えません。

zip 関数

zipは、2つのリストからタプルを要素とする1つのリストを作ります。

a = zip [1,2,3,4,5]["one","two","three"]
main = do
    print $ a!!0
    print $ a!!1
    print $ a!!2
    print a
    

実行結果

1,"one")
(2,"two")
(3,"three")
[(1,"one"),(2,"two"),(3,"three")]

リストのどちらかの要素数が多い場合は、余った要素は無視されます。

列挙型

列挙型、直積型、直和型を合わせて、代数的データ型と呼びます。

ここでは、まず、列挙型の紹介から始めます。

data Color = Red | Green | Blue

上記のコードで Color は型と呼ばれ、RedGreenBlue はコンストラクタと呼ばれます。

命名規則

型とコンストラクタは大文字で始める決まりになっています。このことは慣習ではなく、言語仕様で決まっています。

deriving

自分で定義した型をprintするには、「deriving」宣言を使ってShow型クラスを追加しなければなりません。


data Color = Red | Green | Blue  deriving Show

main = do
    print Blue
    putStrLn $ show Green
    

実行結果

Blue
Green

derivingを使うことによって、自分で定義した型に機能が追加ですます。Showという型クラスにはshowという関数が含まれています。show関数は、値を文字列に変換します。printは、自動的にshow関数を呼び出しますが、putStrputStrLnは明示的にshow関数を呼び出さなければなりません。

Enum

Enum型クラスを追加すれば、列挙型を数値に変換することができるようになります。


data Color = Red | Green | Blue  deriving (Show, Enum)

main = do
    print $ fromEnum Red
    print $ fromEnum Green
    print $ fromEnum Blue
    print ( toEnum 0 :: Color )
    print ( toEnum 1 :: Color )
    print ( toEnum 2 :: Color )
    

実行結果

0
1
2
Red
Green
Blue

derivingで複数の型クラスを追加するには括弧 ( ) で囲んで , (カンマ)で区切ります。

fromEnumで列挙型を数値に変換し、toEnumでは::で変換する型を指定して、数値を列挙型に変換します。

優先順位の都合上、::が含まれる場合は$ではなく、括弧で囲む必要があります。

Bool

真偽値を表すBool型も、標準ライブラリで定義されている列挙型です。

直積型

直積型は、一つのデータの中に、違う型のデータを保持できるフィールドを複数持てる型です。

data Person = Person String Int deriving Show

上記のコードで、1つ目のPersonは、型です。2つ目のPersonはコンストラクです。型とコンスラクタは同じ名前でも違う名前でも構いません。StringIntは、フィールドの型になります。


data Person = Person String Int deriving Show

main = do
    let a = Person "John" 25
    let b = Person "Mary" 20
    let (Person str1 int1) = a
    let (Person str2 int2) = b
    print (str1, int1)
    print (str2, int2)
    

実行結果

("John",25)
("Mary",20)

レコード構文

レコード構文という方法を使えば、直積型や直和型のフィールドに名前をつけて呼び出すことができます。


data Person = Person { name :: String, age :: Int } deriving Show

main = do
    let a = Person "John" 25
    let b = Person "Mary" 20
    print (name a, age a)
    print (name b, age b)
    putStrLn (name a)
    putStrLn (name b)
    

実行結果

("John",25)
("Mary",20)
John
Mary

newtype

フィールドが1つだけの直積型は newtype というキーワードで定義できます。


newtype Score = Score Int deriving Show

a = Score 100
main = do
    let (Score score) = a
    print score
    

実行結果

100

newtypeで定義されたデータは、dataで定義されたデータよりも高速で動作するらしいです。

直和型

直和型は、列挙型の各データにフィールドを持たせて、複数の直積型を定義したものです。


data IntStr = DataInt Int | DataStr String deriving Show


foo (DataInt  1 ) = "hello"
foo (DataStr "1") = "world"
foo _             = "?"

main = do
    print $ foo $ DataInt  0
    print $ foo $ DataInt  1
    print $ foo $ DataStr "0"
    print $ foo $ DataStr "1"
    

実行結果

"?"
"hello"
"?"
"world"

通常関数は、同一タイプの引数しか受け付けませんが、この例だと、複数のタイプの引数を受け取れるようになっているとのことらしいです。私自身がイマイチわかっていません。

型シノニム

Stringは文字列を保持できるデータ型です。文字列は文字のリストです。Stringという名前は、文字のリスト[Char]に型シノニムという方法でをつけた別名です。

type String = [Char]


11780 visits
Posted: Jan. 18, 2019
Update: Jan. 21, 2019

ホーム   目次   ページトップ