一.参照
モジュールを参照する構文形式は以下の通り:
-- モジュール内のすべての関数をグローバル名前空間に追加
import <module>
-- 一部参照
import <module> (fn1, fn2)
-- データ型とその値コンストラクタを参照
import <module> (Datatype(constructor, constructor))
-- すべての値コンストラクタを参照
import <module> (Datatype(..))
-- hiding で除外
import <module> hiding (fn)
-- 名前空間を保持
import qualified <module>
-- 名前空間を保持し、別名を付ける
import qualified <module> as <alias>
例えば:
import Data.List
import Data.List (nub, sort)
import Data.Tree (Tree(Node, Branch))
import Data.Tree (Tree(..))
import Data.List hiding (nub)
import qualified Data.Map
import qualified Data.Map as M
hiding 構文は命名衝突の問題を緩和できますが、非常に便利というわけではありません。大量の命名衝突があるモジュールの場合、qualified で名前空間を保持することで衝突を回避できます
GHCi 環境
:m コマンドでモジュールを参照:
> :m Data.List
> :m Data.List Data.Map Data.Set
GHC 7.0 以降、GHCi 環境で直接 import 構文を使用できます:
> import qualified Data.Map as M
> M.fromList [('a', 1)]
fromList [('a',1)]
したがって、環境の違いを気にする必要はありません。詳細は import qualified in GHCI を参照
二.宣言
モジュールはコードを整理するために使用されます。例えば、機能が類似した関数を同じモジュールにまとめます
例えば二分木のモジュール定義:
module BTree
-- 公開する関数とデータ型を宣言
( Tree
, singleton
, add
, fromList
, find
) where
-- 依存モジュールを参照
-- データ型と関数を定義
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton x = Node x EmptyTree EmptyTree
注意:
-
モジュール名とファイル名を同じにする ことが強制要求されるため、対応するファイル名は
BTree.hsである必要があります -
モジュール宣言は 1 行目に配置する必要があります(前に
importなどのものは配置できません。importはwhereの後に配置できます)
モジュール内のデータ構造のエクスポートは import 構文と類似しています:
module MyModule (Tree(Branch, Leaf)) where
data Tree a = Branch {left, right :: Tree a} | Leaf a
データ構造 Tree とそのコンストラクタ Branch と Leaf のみを公開します。.. を使用してすべての値コンストラクタを公開することもできます:
module MyModule (Tree(..))
または値コンストラクタを公開せず、ファクトリメソッドなどの方法でのみ該類型の値を取得できるようにします(一般的には Map.fromList など):
module MyModule (Tree, factory)
欠点は、値コンストラクタを使用してパターンマッチできなくなることです
サブモジュール
モジュールにはツリー階層構造があります。モジュールはサブモジュールを持つことができ、サブモジュールはさらにサブモジュールを持つことができます……
ディレクトリ構造と命名に要件があります。例えば:
.
├── main.hs
└── Math
├── Number.hs
└── Vector.hs
パッケージ名は大文字で始める必要があります(Math)。サブモジュールのファイル名はサブモジュール名と一致している必要があります。大文字小文字の区別は環境によって異なります(例えば OSX は区別しません)
三.標準ライブラリモジュール
標準ライブラリには多くの強力な関数が内置されています。Hoogle で使用例、型宣言、ソースコードさえも 確認でき、非常に便利です
Data.List
大量の List 操作関数を提供します。一般的なものには map, filter などがあり、さらに:
述語:
-- every、すべてが True の場合のみ True
and :: Foldable t => t Bool -> Bool
-- some、1 つでも True なら True
or :: Foldable t => t Bool -> Bool
-- 一般的な some、List 内の任意の要素が条件を満たせば True
any :: Foldable t => (a -> Bool) -> t a -> Bool
-- 一般的な every、List 内のすべての要素が条件を満たす場合のみ True
all :: Foldable t => (a -> Bool) -> t a -> Bool
新しい List を構築:
-- 配列内に区切り要素を挿入
intersperse :: a -> [a] -> [a]
-- intersperse と類似、二次元配列内に一次元配列を区切り要素として挿入し、一次元に平坦化
intercalate :: [a] -> [[a]] -> [a]
-- 二次元配列の行列転置
transpose :: [[a]] -> [[a]]
-- 次元削減(一组の List を 1 つの List に接続)
concat :: Foldable t => t [a] -> [a]
-- マップしてから次元削減、concat . map と同等
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
-- 無限再帰呼び出し、戻り値を再度入力
iterate :: (a -> a) -> a -> [a]
-- 位置で分割、分割された 2 部分を返す
splitAt :: Int -> [a] -> ([a], [a])
-- 要素を取得、条件を満たさなくなるまで
takeWhile :: (a -> Bool) -> [a] -> [a]
-- 要素を削除、条件を満たさなくなるまで
dropWhile :: (a -> Bool) -> [a] -> [a]
-- 条件で分割(初めて条件を満たさない位置)、takeWhile と類似
span :: (a -> Bool) -> [a] -> ([a], [a])
-- 条件で分割(初めて条件を満たす位置)
break :: (a -> Bool) -> [a] -> ([a], [a])
-- 再帰 init、List が空になるまで
inits :: [a] -> [[a]]
-- 再帰 tail、List が空になるまで
tails :: [a] -> [[a]]
ソート:
-- マージソート
sort :: Ord a => [a] -> [a]
-- List 内の該要素以上である最初の要素の前に挿入
insert :: Ord a => a -> [a] -> [a]
グループ化:
-- グループ化、隣接かつ値が等しいことを基準とする
group :: Eq a => [a] -> [[a]]
-- 条件でグループ化、条件を満たす一组、満たさない一组
partition :: (a -> Bool) -> [a] -> ([a], [a])
マッチ:
-- 部分文字列マッチ(部分 List マッチ)、指定部分文字列を含むか
isInfixOf :: Eq a => [a] -> [a] -> Bool
-- 部分文字列マッチ、指定部分文字列で始まるか
isPrefixOf :: Eq a => [a] -> [a] -> Bool
-- 部分文字列マッチ、指定部分文字列で終わるか
isSuffixOf :: Eq a => [a] -> [a] -> Bool
-- 要素包含性検出、指定要素を含むか
elem :: (Foldable t, Eq a) => a -> t a -> Bool
-- 要素包含性検出、指定要素を含まないか
notElem :: (Foldable t, Eq a) => a -> t a -> Bool
検索:
-- 条件で検索、条件を満たす最初の要素を返す
find :: Foldable t => (a -> Bool) -> t a -> Maybe a
-- 検索、最初のマッチ要素インデックスまたは Nothing を返す
elemIndex :: Eq a => a -> [a] -> Maybe Int
-- すべて検索
elemIndices :: Eq a => a -> [a] -> [Int]
-- find と類似、ただし条件を満たす最初の要素のインデックスを返す
findIndex :: (a -> Bool) -> [a] -> Maybe Int
-- find と類似、ただし条件を満たすすべての項のインデックスを返す
findIndices :: (a -> Bool) -> [a] -> [Int]
結合:
-- List を結合、zip3 〜 zip7 もあり
zip :: [a] -> [b] -> [(a, b)]
-- List を結合し、map 一遍、zipWith3 〜 zipWith7 もあり
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
テキスト処理:
-- 文字列を行で分割(\n)
lines :: String -> [String]
-- 改行で結合(\n)
unlines :: [String] -> String
-- 空白文字で分割
words :: String -> [String]
-- 空格で結合
unwords :: [String] -> String
要素削除:
-- 重複除去
nub :: Eq a => [a] -> [a]
-- 最初のマッチ要素を削除
delete :: Eq a => a -> [a] -> [a]
集合演算:
-- 差集合を求める、重複要素がある場合、最初のみ削除
(\\) :: Eq a => [a] -> [a] -> [a]
-- 和集合を求める
union :: Eq a => [a] -> [a] -> [a]
-- 積集合を求める
intersect :: Eq a => [a] -> [a] -> [a]
より一般的なバージョン
length, take, drop, splitAt, !!, replicate などの関数のパラメータまたは戻り値は Int 類型が要求され、十分に一般的ではないため、より一般的な類型の対応バージョンが提供されています:
genericLength :: Num i => [a] -> i
genericTake :: Integral i => i -> [a] -> [a]
genericDrop :: Integral i => i -> [a] -> [a]
genericSplitAt :: Integral i => i -> [a] -> ([a], [a])
genericIndex :: Integral i => [a] -> i -> a
genericReplicate :: Integral i => i -> a -> [a]
nub, delete, union, intsect, group, sort, insert, maximum, minimum はすべて == で等しいかどうかを判断し、より一般的で自分で等しいかどうかを判断できるバージョンも提供されています:
nubBy :: (a -> a -> Bool) -> [a] -> [a]
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
maximumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
By 付きの関数は通常 Data.Function.on と一緒に使用されます:
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
f `on` g = \x y -> f (g x) (g y)
例えば、正負で隣接要素をグループ化するには:
groupBy ((==) `Data.Function.on` (> 0)) values
セマンティクスは非常に明確です:要素がゼロより大きいかどうかで分類します
また、sort と sortBy compare は同等です(デフォルトの比較方法は compare です)。List の長さでソートするには、このようにします:
sortBy (compare `on` length) xs
セマンティクスも同様に非常に明確です。したがって
(==) `on`
compare `on`
は非常に素晴らしい慣用パターンです
P.S.:browse <module> コマンドでモジュール内のすべての関数とデータ型の型宣言を確認できます
Data.Char
String は実際には [Char] です:
type String = [Char] -- Defined in 'GHC.Base'
したがって、文字列を処理する際、よく Data.Char モジュールを使用します。多くの文字関連関数を提供しています
文字範囲を判定:
-- 制御文字
isControl :: Char -> Bool
-- 空白文字
isSpace :: Char -> Bool
-- 小文字 Unicode 文字
isLower :: Char -> Bool
-- 大文字 Unicode 文字
isUpper :: Char -> Bool
-- 文字
isAlpha :: Char -> Bool
-- 文字または数字
isAlphaNum :: Char -> Bool
-- 印刷可能文字
isPrint :: Char -> Bool
-- ASCII 数字、0-9
isDigit :: Char -> Bool
-- 八進数
isOctDigit :: Char -> Bool
-- 十六進数
isHexDigit :: Char -> Bool
-- 文字、機能は isAlpha と同等、実装方法が異なる
isLetter :: Char -> Bool
-- Unicode 発音記号、例えばフランス語
isMark :: Char -> Bool
-- Unicode 数字、ローマ数字などを含む
isNumber :: Char -> Bool
-- 句読点記号
isPunctuation :: Char -> Bool
-- 通貨記号
isSymbol :: Char -> Bool
-- Unicode 空格または区切り記号
isSeparator :: Char -> Bool
-- ASCII 文字(Unicode 文字表の最初の 128 位)
isAscii :: Char -> Bool
-- Unicode 文字表の最初の 256 位
isLatin1 :: Char -> Bool
-- 大文字 ASCII 文字
isAsciiUpper :: Char -> Bool
-- 小文字 ASCII 文字
isAsciiLower :: Char -> Bool
所属類型を判断:
generalCategory :: Char -> GeneralCategory
返される GeneralCategory は列挙型で、合計 30 の類別 があります。例えば:
> generalCategory 'a'
LowercaseLetter
> generalCategory ' '
Space
文字変換:
-- 大文字に変換
toUpper :: Char -> Char
-- 小文字に変換
toLower :: Char -> Char
-- title 形式に変換、toUpper と類似、一部の連体文字には違いがある
toTitle :: Char -> Char
-- 文字を数字に変換、[0-9,a-f,A-F] を要求
digitToInt :: Char -> Int
-- 数字を文字に変換
intToDigit :: Int -> Char
-- 文字を Unicode コードに変換
ord :: Char -> Int
-- Unicode コードを文字に変換
chr :: Int -> Char
したがって、簡単な暗号化と復号化はこのように実装できます:
encode shift = map $ chr . (+ shift) . ord
decode shift = map $ chr . (subtract shift) . ord
-- またはより技巧的な
decode shift = encode $ negate shift
Data.Map
辞書はキー値対の順序なしリストで、平衡二叉樹 の形式で保存されます。Data.Map はいくつかの辞書処理関数を提供しています
P.S.Data.Map 内のいくつかの関数は Prelude と Data.List モジュールと命名衝突があるため、qualified import as を使用して名前空間を保持し、別名を付けます:
import qualified Data.Map as Map
新しい Map を構築:
-- List を Map に変換、重複 key がある場合、最後の value を取得
Map.fromList :: Ord k => [(k, a)] -> Map.Map k a
-- Map を List に変換
Map.toList :: Map.Map k a -> [(k, a)]
-- fromList と類似、重複 key を直接破棄せず、手動で処理可能
Map.fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> Map.Map k a
-- 空の Map
Map.empty :: Map.Map k a
-- (k, a) を挿入、新しい Map を返す
Map.insert :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
-- insert と類似、重複 key を処理可能
Map.insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map.Map k a -> Map.Map k a
-- 単一要素 Map
Map.singleton :: k -> a -> Map.Map k a
-- 新しい Map にマップ
Map.map :: (a -> b) -> Map.Map k a -> Map.Map k b
-- 新しい Map をフィルタ
Map.filter :: (a -> Bool) -> Map.Map k a -> Map.Map k a
Map 情報を取得:
-- 空かどうか判定
Map.null :: Map.Map k a -> Bool
-- 長さ
Map.size :: Map.Map k a -> Int
-- すべての key を取得
Map.keys :: Map.Map k a -> [k]
-- すべての value を取得
Map.elems :: Map.Map k a -> [a]
検索:
-- key で検索
Map.lookup :: Ord k => k -> Map.Map k a -> Maybe a
-- 包含性判断
Map.member :: Ord k => k -> Map.Map k a -> Bool
Data.Set
集合関連のツール関数を提供し、構造は Map と類似し、どちらもツリー構造で保存されます
P.S.同様に、大量の命名衝突があるため、qualified import が必要です:
import qualified Data.Set as Set
集合を構築:
-- List を Set に変換
Set.fromList :: Ord a => [a] -> Set.Set a
集合操作:
-- 積集合を求める
Set.intersection :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 差集合を求める
Set.difference :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 和集合を求める
Set.union :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 部分集合かどうか判断
Set.isSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool
-- 真部分集合かどうか判断
Set.isProperSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool
注意、関数名がいたずらですね、配列の List.intersect は集合になると Set.intersection に変わります
Map の多くの関数は Set 内にも対応バージョンがあります。例えば null, size, member, empty, singleton, insert, delete, map, filter など
同様に、集合を使用して1 行で重複除去を実装できます:
unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList
集合の重複除去効率は List.nub より高いですが、欠点は集合を構築する際に要素をソートするため、得られる重複除去結果は元の順序を保持しません(List.nub は保持します)
参考資料
-
Haskell data type pattern matching:カスタムデータ型のパターンマッチ
コメントはまだありません