🛫

リストとは何か? [ ](空リスト)と: (cons演算子)について

2022/05/04に公開

この記事ではリストの仕組みについて解説します。
リストを作るためには2つのモノが必要となります。[](空リスト)と:cons演算子です。

[](空リスト) → 型コンストラクタ、空リスト
:(cons演算子) → 値とリストを組み合わせるための関数

[]型コンストラクタ、空リストという2つの使い方をします。:(cons演算子)の元々の機能は関数です。それぞれの仕組みを詳しく解説していきます。

[ ](型コンストラクタ、空リスト)

まずは角カッコ[]について詳しく知るため、:iコマンドで調べてみます。

Prelude> :i []
type [] :: * -> * //型コンストラクタとして定義
data [] a = [] | a : [a] //リストの値として定義

同じ角カッコ[]であっても、2つの使われ方をすることがわかります。1つが2行目にある型コンストラクタとしての使い方、もう1つが3行目にあるリストのデータ型としての使い方です。

型コンストラクタとしてはデータ型(例えばInt)を受け取って、新しいデータ型(例えば[Int])を返却する機能として定義されています。*とは難しく言えば具体型であり、型引数を取る必要がない通常のデータ型だと思って下さい(つまりInt、Char、Doubleなど)

データ型としては、OR条件としてどちらかの値を取り得るモノとして定義されています。[] | a : [a]というのは、[]がOR条件で2つの使われ方をすることを意味します。)1つ(OR条件の左側)が空リストとしての値、もう1つ(OR条件の右側)が空リストでなく、値を持つリストです。(型変数aを受け取って、[a]型の値を作っていますよね。)

つまり角カッコ[]は同じ記号でも2つの異なる使われ方をします。

データ型として使うか?値として使うか?によって、全く違った意味を持ちます。

1、型コンストラクタ

1つ目の使い方が型コンストラクタです。型コンストラクタとはデータ型を作るためのデータ型のことです。 カンタンにいえば、あるデータ型にくっつけて別のデータ型を作る機能を持ちます。

リスト(というデータ型)に他のデータ型をくっつけて、リスト型の値を作ります。図にすると下記のようなイメージです。

この仕組みを使ってリストへデータ型を指定することも可能です。

Prelude> [1,2,3] :: [Int]
[1,2,3]

ここでは[1,2,3]というリストに対して、[Int]というデータ型を指定しました。ただのIntではなく、リスト型のIntである点に注意して下さい。

改めて型コンストラクタとは?

データ型を作るためのデータ型..というと難しそうな感じがします。型コンストラクタはそのまま日本語で考えると分かりやすそうです。

型=データ型
コンストラクタ=構築子、構築するモノ、作るモノ

つまり型コンストラクタとは、データ型を構築するモノ(作るモノ)です。 Int、Charなどのデータ型とは全く違います。それ単独ではデータ型になれない、半人前の存在です。他のデータ型と組み合わせることで初めて一人前になれます。

他にも有名な型コンストラクタとしてMaybeがあります。

Prelude> :i Maybe
type Maybe :: * -> *
data Maybe a = Nothing | Just a

こちらも[]と同じで、2行目では型コンストラクタ、3行目ではOR条件のデータ型として定義されています。
型コンストラクタとしては、データ型(例えばInt)を受け取って、新しいデータ型(例えばMaybe Int)を返却するとされています。データ型としてはNothingでない場合、どのデータ型の値を返却するかを指定することができます。以上を図にすると下記のようなイメージです。

[]という型コンストラクタを別のデータ型とくっつけることで、リストを作成します。これがまず1つ目の役割です。

2、空リスト

2つ目が値としての使われ方です。[]は空のリストという値を持ちます。今回はあくまでも値として使った場合です。なので同じ[]であっても、型コンストラクタして使うか?値として使うか?によって全く意味は異なります。

Prelude> []
[]

ここではただ単に値として空リストを出力しました。

ちなみに[][[]]は全く意味が異なります。1つ目は空リストであり、1つ目が1つの空リストを含むリストです。

:(cons演算子)

リストを作るために必要な2番目です。cons演算子とは通常の値からリストを出力する関数です。 consというのはconstructorの略であり、その起源はLispにあります。

cons演算子の具体的な中身を見てみましょう。

Prelude> :t (:)
(:) :: a -> [a] -> [a]

:は関数として定義されていることに注目して下さい。通常の値リストリスト化された通常の値
という機能を持ちます。通常の値に加えてリストが必要なことに注意して下さい。cons演算子はもう片方がリストでないと、リストを作ることはできません。

Prelude> 1 :[]
[1]

上記では[](空リスト)と:(cons演算子)を組み合わせてリストを作りました。中置演算子として定義されているので、1[]の間にcons演算子を置いています。

構文糖衣

リストの元々の書き方は1:2:3:[]です。それを[1,2,3]と書けるのは構文糖衣の仕組みを使っているからです。構文糖衣とはカンタンに記述するための仕組みです。1:2:3:[]でも[1,2,3]でも全く同じですが、後者の方が直感的に理解しやすいですよね。

Prelude> 1:2:3:[]
[1,2,3]

1:2:3:[]というのはこう考えて下さい。[]は空のリストです。その先頭に3を追加すると[3]になります。次に先頭へ2を追加すると[2]になります。最後に先頭へ1を追加すると[1]になります。

Prelude> 1:2:3:[4,5,6]
[1,2,3,4,5,6]

上記のような書き方をすることも可能です。[4:5:6]というリストは4:5:6:[]と書くのと全く同じです。なのでcons演算子を使って、[]の先頭へ数値をどんどん追加していることがわかります。

こちらはエラー
Prelude> [1,2,3] : [4,5,6]
// リスト同士をつなげるには++演算子を使う

ちなみにリスト同士をcons演算子でつなごうとするとエラーになります。こちらの場合は++演算子が必要になるので注意して下さい。

Discussion