Haskellerへの道 #3 - リスト & タプル

Last Edited: 6/14/2024

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

Haskell & Lists

リスト

リストは、既にご存じの通り、多くのプログラミング言語で広く使われる非常に重要なデータ構造です。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

これらのデータ型をしっかりと理解することが非常に重要ですので、必ず理解するようにしましょう!

クイズ

この記事では、学習した内容を確認するためのクイズを設けます。記事のメイン部分を読んだ後に、ぜひ自分で問題を解いてみることを強くお勧めします。各問題をクリックすると答えが表示されます。

リソース