ようこそ。睡眠不足なプログラマのチラ裏です。

Haskellのお勉強 その4

concat関数

{-concat関数の型-}
concat :: [[a]] -> [a]
main = do cs <- getContents
          putStr $ expand cs

expand :: String -> String
expand cs = concat $ map expandTab cs

expandTab :: Char -> String
expandTab c = if c == '\t' then "        "
                           else [c]

concat関数は、リストのリストxsを連結して1つのリストにする。
要は、concat関数ってのはリストを一重減らす作用があるということですな。


パターンマッチ

tabStop :: Int
tabStop = 8
main = do cs <- getContents
          putStr $ expand cs

expand :: String -> String
expand cs = concatMap expandTab cs

expandTab :: Char -> String
expandTab '\t' = replicate tabStop ' '
expandTab c = [c]

パターンマッチ機能により、ある特定の引数に対する値を分けて書くことが出来る。
パターンマッチを書く場合、特定性の高い順番に書かないとうまく機能しない。
例にあるexpandTab関数のパターンマッチの順番を入れ替えると、
引数が'\tであっても、 「replicate tabStop ' '」という関数が適用されなくなってしまう。
また、関数定義で参照されない任意の引数は「_」で表現される。


リストに対するパターンマッチ

{-map関数の定義-}
-- 関数の型
map :: (a -> b) -> [a] -> [b]
-- 定義1
map f [] = []
-- 定義2
map f (x : xs) = f x : map f xs

定義1の「」は、空リストにだけマッチするパターンで、
定義2の「(x : xs)」は、空リスト以外のリストにマッチするパターンである。
つまり、「(x : xs)」は、要素が1つ以上あるリストにマッチする。
マッチしたときには、リストの最初の要素にxが束縛され、第2要素以降のリストにxsが束縛される。
例えば、[1,2,3,4,5]に(x : xs)というパターンマッチをさせると、
xの値は1で、xsの値は[2,3,4,5]となる。また、[7]にマッチさせた場合、xが7となり、xsが
となる。


また、定義2は次のように解釈することができる。

map f (x : xs) = ((f x) : (map f xs))

つまり、リストの第1要素にfを適用しつつ、残りの第2要素以降は再帰で処理している。
また、「:」はリストを生成する演算子で、リストに対するパターンマッチでは、
(x : xs)がリストを先頭要素とそれ以降の要素に分解したが、
関数定義の中で(y : ys)を使うと逆の意味となり、リストysの先頭にyを追加したリストを生成する。
例えば、1 : [2,3,4,5]の値は[1,2,3,4,5]となり、7 : []の値は[7]となる。


map関数は、結果的に、第1要素にfを適用したものと、
残りの要素にfを適用したものからリストを生成したものを返す。


練習問題:標準入力の'a'と'A'を入れ替えるコマンド

{-swapa-}
main = do cs <- getContents
          putStr $ map expandCap cs

expandCap :: Char -> Char
expandCap 'a' = 'A'
expandCap 'A' = 'a'
expandCap c = c