垂れ流す思考

思考を垂れ流しアウトプットを行うブログ

ふつうのHaskellプログラミング 第3章 -2

ふつうのHaskellプログラミング

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

噂のすごいH本を読んで楽しくHaskellを学んでいこうというコンセプト

会社のHaskellerさんに
「関数型素人がすごいH本で入門すると中盤で詰むよ」
「初めてならふつうのH本からがおすすめ」
ということなので『ふつうのHaskellプログラミング』からやっていこうかと。

第3章 Haskellの基礎(2)型と高階関数 - 2

# expand1.hs
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]

このサンプルコードを使うとHlintの警告で
Warning: Use concatMap. Found: concat $ map expandTab cs Why not: concatMap expandTab cs

main = do cs <- getContents
          putStr $ expand cs

expand :: String -> String
expand = concatMap expandTab

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

これで警告がなくなる
concatMapの型宣言は以下

concatMap :: (a -> [b]) -> [a] -> [b]
concat :: [[a]] -> [a]
map :: (a -> b) -> [a] -> [b]

mapでリスト化したものをconcatするのがconcatMapまんまだね。

-- Hoogleの説明
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
base Prelude
Map a function over all the elements of a container and concatenate
the resulting lists.

Hoogleすげー便利だけど英語が...覚えるしかないか。
Foldableは束縛可能?束縛した関数って意味かな?=>これの書き方はまだ知らん。
Mapで生成したリストをconcatenateするって言ってるけどconcatenateって言うんスネ〜


パターンマッチ

要はif文の後にelsifとかelse ifとか使って条件を繋げてかく方法。
普段からif ~~を何個か使って条件書いてたけど汚く感じてた。
Haskellは結構キレイに書けるんだなぁ。

関数名 第一引数のパターン 第二引数のパターン ... = 定義1
関数名 第一引数のパターン 第二引数のパターン ... = 定義2
関数名 第一引数のパターン 第二引数のパターン ... = 定義3
...
...
...
expandTab :: Char -> String
expandTab '\t' = replicate tabStop ' '
expandTab c    = [c]

こんな感じに
'\t'ならこれ、
cならこれ。
cに意味はないのかな?Charのcではあるけど。
\t以外は全てcにいく。

ちなみにconcatMapはここで説明があった。
eta reduceはまだ説明ないけど、これは新しい概念なのかな?ちゃんと調べたら書く。


map関数の定義

map :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = f x : map f xs

リストに対するパターンマッチと関数の再帰定義と言う概念

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

map f [] = []
これは空リストの時の処理、見たまんま。
map f (x:xs) = f x : map f xs
(x:xs)がリストのパターン
最初の要素をxに束縛して、他をxsに束縛する。(束縛の言い方はあっているのかな?)
そしてxsmapを適応する。

関数の再帰定義

map f (x:xs) = f x : map f (x:xs) = f x : map f xs ... こんな感じで繋がっている
先頭を処理しつつ、後続はまた再帰で処理している。
この考え方は書いて覚えるしかないかな。こう言う関数の組み立てはしないからなぁ。


余談

文字列は基本的にCharのリストと言う意識は再認識しないと。
練習問題のaとAを入れ替えるswapa.hs

main = do cs <- getContents
          putStr $ map swapa cs

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

csStringだけどmap関数はそのCharリストを一つ一つ再帰して処理をするから
この場合swapaChar -> Charの型になるのは慣れないな〜

今日は以上

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門