すごいHaskell六章 (後半6.3から終わりまで)

連想リスト

 山場が来たぞー!ドコドコドコドコドコドコドコドコ


 キー(Stringだけど)が重複してる(ペアのリスト型)連想リストにfindKey(Maybeでない)を使うとどうなるか。

phoneBook :: [(String, String)]
phoneBook = 
    [("betty", "555-2938")
    ,("bonnie", "452-2928")
    ,("bonnie", "452-2928")
    ]
findKey' :: (Eq k) => k -> [(k, v)] -> v
findKey' key xs = snd .head .filter (\(k, _) -> key == k) $ xs
ghci> findKey "betty" phoneBook
"555-2938"
ghci> findKey "bonnie" phoneBook
"452-2928"
ghci> findKey' "penny" phoneBook
"*** Exception: Prelude.head: empty list

 実装がfilterしてheadだから最初の要素が返ってくるのか。


 Maybe版findKey、ミスがあるな。ガード一つ目のところ、key == x じゃなくて key == kだ。

findKey :: (Eq k) => k -> [(k, v)] -> Maybe v
findKey _ [] = Nothing
findKey key ((k,v):xs)
    | key == k  = Just v
    | otherwise = findKey key xs

 実行してみる。

ghci> findKey "betty" phoneBook
Just "555-2938"
ghci> findKey "bonnie" phoneBook
Just "452-2928"
ghci> findKey "penny" phoneBook
Nothing

 こっちも重複したキーは、最初に一致したキーの値が返ってくる。


 リストに対するふつうな再帰パターン(リストの先頭要素headを取り出して、残りtailを再帰)だと、再帰より畳込みで書いた方がいいらしい。
 ここのfindKeyの畳込みはかっこいいので引用する。

findKey :: (Eq k) => k -> [(k, v)] -> Maybe v
findKey key xs = foldr
                   (\(k,v) acc -> if key == k then Just v else acc)
		   Nothing xs

 ガードの条件文をif文にして畳込みの関数に渡しちゃうんだな。そして、アキュムレータの初期値は再帰の基低部を使う。
 いまさらだけど、このスタイルシートだとソースコードの引用がものすごく見づらくなるのなんとかしたい。→スタイルシート変えた。

Map.Map

 なんかMapをimportしようとすると警告が出る。

import qualified Data.Map as Map
ghci> :l imports.hs
[1 of 1] Compiling Main             ( imports.hs, interpreted )

imports.hs:6:1:
    Warning: The import of `Data.Map' is redundant
               except perhaps to import instances from `Data.Map'
             To import instances alone, use: import Data.Map()

<no location info>:
Failing due to -Werror.
Failed, modules loaded: none.

 インスタンスをimportする時以外冗長だって言われてる。うーむ。
 でもData.Map()でimportするとasが使えないっぽい。警告を抑制する方法がいるな。
 などと言いつつ -Werror オプションを外すことにした。
 なんでMapの時だけ警告が出るんだろう。
 とりあえず、これで進められるようになった。
 (追記)コードの中でMap.Mapを使ったら出なくなった。使われてないモジュールをimportするなってことだったのかな。


 fromListの表示がfromListだったりMapの表示もMap.Mapだったりするのあんまり納得いかない…
 こういうの

ghci> :t Map.fromList
Map.fromList :: Ord k => [(k, a)] -> Map.Map k a


 Map.mapが関数を適用するのは、キーじゃなくて値の方だけなのかな。

ghci> Map.map  string2digits ( Map.fromList [("234", "123")])
fromList [("234",[1,2,3])]

 ぽいな。こういうデータ構造だと大抵、キーは変更不可能になってるはずだしな。

モジュールを作る

 ファイル名とモジュール名は同じにする。ファイルの先頭にモジュール名とエクスポートする関数を書く。
 なんかGeometryディレクトリの下にCube.hsとか書いたけど、importできない。うーん。
 GHCiからは別ディレクトリにあるモジュールはimportできない?起動ディレクトリの.hsにimport文を書くとimportできるみたいだけど。