このブログ記事では、リストやタプルといった、Haskellにおいて重要な概念を紹介します。

リスト
リストは、既にご存じの通り、多くのプログラミング言語で広く使われる非常に重要なデータ構造です。Haskellも例外ではありません。Haskellでは、リストは一つの型しか格納できません。
[1,2,3,4,5]::[Integer]
Haskellには二つのリストのコンストラクタがあります:
- 空のリスト:
[]
- 前置リスト:
x:xs
これらのコンストラクタを使って、次のようにしてプログラム的に [1,2,3,4,5]
を作成できます。
1:2:3:4:5:[]
これは [] -> [5] -> [4,5] -> [3,4,5] -> [2,3,4,5] -> [1,2,3,4,5]
となります。
リストの関数
リストは頻繁に使用するため、Haskellにはリスト用の便利な標準関数が用意されています。
import Data.List
--- これは標準関数が存在する場所です。以下の関数を試すときは、このインポートを行うだけです。
--- 最初の要素を取得する
head :: [a] -> a
head [1,2,3,4,5] --- Output: 1
--- 最初の要素を削除する
tail :: [a] -> [a]
tail [1,2,3,4,5] --- Output: [2,3,4,5]
--- 長さを取得する
length :: [a] -> Integer
length [1,2,3,4,5] --- Output: 5
--- 最後の要素を削除する
init :: [a] -> [a]
init [1,2,3,4,5] --- Output: [1,2,3,4]
--- リストが空かどうかを確認する
null :: [a] -> Bool
null [] --- Output: True
null [1,2,3,4,5] --- Output: False
ブールリストの関数
ブール値のリストに対して論理演算を行う必要がある場合があります(例えば、すべての条件が満たされているかを確認する場合)。 そのような状況で以下の関数が役立ちます。
--- AND演算子
and :: [a] -> Bool
and [True, False, True] --- Output: False
--- OR演算子
or :: [a] -> Bool
or [True, False, True] --- Output: True
リストの基本的な関数を理解したところで、リストの操作方法を見てみましょう。
リスト内包表記
リスト内包表記には次のような構文を使用します:
[<gen> | <elem> <- <list>, ..., <guard> , ...]
--- <gen> : ガードが満たされた場合に要素に対して行う操作
--- <elem>: リストの各要素
--- <list>: リスト
--- <guard>: ガード条件(詳しくはRoad to Haskeller #2を参照)
--- 例
[2*x | x <- [1,2,3]] --- Output: [2,4,6]
[2*x | x <- [1,2,3], x > 1] --- Output: [4,6]
[(x,y) | x <- [1,2,3], y <- [a,b], x>1]
--- Output: [(2, a), (2, b), (3, a), (3, b)]
リストやガードは好きなだけ使うことができます。
リストパターン
前回のRoad to Haskeller (#2)の記事では、パターンマッチングについて説明しました。 この概念を用いて、リストに対する関数を非常に簡潔に定義できます。
--- すべての要素の合計を取る
sum :: [a] -> a
sum [] = 0
sum (x:xs) = x + sum xs
--- リストを分解し、要素を再帰的に加算しています。
-- 偶数を抽出する
even :: [a] -> [a]
even [] = []
even (x:xs) =
| mod x 2 == 0 = x:even(xs) --- `mod` は余りを計算します
| otherwise = even(xs) --- 奇数は前に追加しません
タプル
タプルもHaskellにおける重要なデータ型であり、以下のように定義できます:
(1, "Hello") :: (Integer, String)
(1, 2) :: (Integer, Integer)
リストとは異なり、タプルは複数の型を持つことができます。
タプルの関数
リストと同様に、タプルにも標準関数があります。
--- 最初の要素を取得する
fst:: (a, b) -> a
fst (x, _) = x
fst (1,2) --- Output: 1
--- 最後の要素を取得する
snd:: (a, b) -> b
snd (_, y) = y
snd (1,2) --- Output: 2
これらのデータ型をしっかりと理解することが非常に重要ですので、必ず理解するようにしましょう!
クイズ
この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。
リソース
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #4 - Lists and Tuples. YouTube.
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #5 - Lists Exercises. YouTube.