すごいhaskell楽しく学ぼう 第6章

Post on 31-May-2015

4.995 Views

Category:

Education

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

スタートHaskell2で発表

TRANSCRIPT

@aomoriringo

1986-2005

2005-2011

2011-

◎ Twitter: @aomoriringo

◎最近使ってる言語 ◦ C#

◎趣味 ◦ Mathematica

◎ Haskell歴 ◦ 2ヶ月未満

◎ いくつかの関数や型、型クラスなどを定義したファイル

◎ Haskellのプログラムはモジュールの集合

◎ コードをモジュールに分割するとしあわせ ◦ 再利用性を高める

◦ 管理・修正しやすくする

◎ モジュールをインポートする

◎ 標準モジュールを使ってみる ◦ 標準モジュールでいろいろ作ってみる

◦ foldl, foldl’

◦ 連想リスト

◎ 自分でモジュールを作ってみる

モジュールをインポートする

◎ 全てをインポート import Data.List

◎ nub, sortのみをインポート import Data.List (nub, sort)

◎ nub以外の全てをインポート import Data.List hiding (nub)

import qualified Data.Map

◦ Data.Map.filterのように参照

import qualified Data.Map as M

◦ M.filterのように参照

◎ module Fooにhogeとbarがあるとき

宣言 インポートされるもの 参照の方法

import Foo hoge, bar hoge, bar Foo.hoge, Foo.bar

import Foo () (なし)

import Foo (hoge) hoge hoge, Foo.hoge

import Foo hiding (hoge) bar bar, Foo.bar

import qualified Foo hoge, bar Foo.hoge, Foo.bar

import Foo as F hoge, bar hoge, bar F.hoge, F.bar

import qualified Foo as F hoge, bar F.hoge, F.bar

◎ Data.Listをインポート ghci> :m + Data.List

ghci> :m + Data.List Data.Map Data.Set

◎ Data.Listのインポートを解除 ghci> :m – Data.List

◎ importも使用可能 ghci> import Data.List (nub, sort)

ghci> import qualified Data.Map as M hiding (foldl)

標準モジュールを使ってみる

Data.List

Data.Char

Data.Map

◎ Hackage ◦ hackage.haskell.org/packages/hackage.html

◎ Hoogle ◦ http://www.haskell.org/hoogle/

◦ 標準モジュールのソースコードが見れます

◎ Hayoo! ◦ http://holumbus.fh-wedel.de/hayoo/hayoo.html

◎ 単語の個数を調べる

ghci> :t wordNums

wordNums :: String -> [(String,Int)]

ghci> wordNums "a haskell learn haskell a a learn good"

[("a",3),("good",1),("haskell",2),("learn",2)]

◎ 文字列を空白で区切られた文字列のリストに変換する

ghci> :t words

words :: String -> [String]

ghci> words “hey these are the words”

[“hey”, “these”, “are”, “the”, “words”]

ghci> words “hey these are the words”

[“hey”, “these”, “are”, “the”, “words”]

◎ リストの同じ要素をグループ化する

ghci> :t group

group :: Eq a => [a] -> [[a]]

ghci> group [1,1,1,2,2,2,3,3,2,2,5,7]

[[1,1,1],[2,2,2],[3,3],[2,2],[5],[7]]

ghci> group [“hoge”, “var”, “var”, “hoge”, “hoge”]

[[“hoge”], [“var”, “var”], [“hoge”, “hoge”]]

◎ リストを昇順に並べ替える

ghci> :t sort

sort :: Ord a => [a] -> [a]

ghci> sort [5,4,3,7,2,1]

[1,2,3,4,5,7]

ghci> sort [“hoge”, “var”, “var”, “hoge”, “hoge”]

[“hoge”, “hoge”, “hoge”, “var”, “var”]

文字列を単語に区切って (words)

→ソートして (sort)

→グループ化して (group)

→グループごとに数を数えれば

できそうですね!

◎ wordNumsの定義 wordNums :: String -> [(String,Int)]

wordNums = map (¥ws -> (head ws, length ws)) .

group . sort . words

ghci> wordNums “a b c b c”

[("a",1),(“b",2),(“c",2)]

ghci> wordNums “”

[]

◎ あるリストが別のリストに含まれるかどうかを調べる

ghci> :t isIn

isIn :: [a] -> [a] -> Bool

ghci> “art” `isIn` “party”

True

ghci> [1,2] `isIn` [1,3,5]

False

◎ リストに対してtailを繰り返し適用する

ghci> :t tails

tails :: [a] -> [[a]]

ghci> tails “party”

[“party”, “arty”, “rty”, “ty”, “y”, “”]

ghci> tails [1,2,3]

[[1,2,3], [2,3], [3], []]

◎ 2つ目のリストが1つ目のリストで 始まっているかどうかを返す

ghci> :t isPrefixOf

isPrefixOf :: String -> String -> Bool

ghci> “hawaii” `isPrefixOf` “hawaii joe”

True

ghci> “haha” `isPrefixOf` “ha”

False

ghci> “ha” `isPrefixOf` “haha”

True

“art” `isPrefixOf` “party” -- False

“art” `isPrefixOf` “arty” -- True

“art” `isPrefixOf` “rty” -- False

“art” `isPrefixOf` “ty” -- False

“art” `isPrefixOf` “y” -- False

◎ リストのうち、述語を満たすものがあるかどうかを返す

ghci> :t any

any :: (a -> Bool) -> [a] -> Bool

ghci> any (>4) [1,2,3]

False

ghci> any id [False, False, True]

True

◎ isInの定義 isIn :: (Eq a) => [a] -> [a] -> Bool

needle `isIn` haystack =

any (needle `isPrefixOf`) (tails haystack)

needle: 針。見つけたいリスト

haystack: 干し草の山。検索対象のリスト

◎ Data.List.isPrefixOf ◦ 前方一致

◦ さっき使った

◎ Data.List.isSuffixOf ◦ 後方一致

◎ Data.List.isInfixOf ◦ 中間一致

◦ さっき作った(isIn)

◎ シーザー暗号 ◦ 平文の各文字を辞書順にn文字シフトして作る

◎ 文字<->Unicode変換 ghci> :t ord

ord :: Char -> Int

ghci> ord ‘a’

97

ghci> :t chr

chr :: Int -> Char

ghci> chr 97

‘a’

ghci> map ord “abcdefgh”

[97,98,99,100,101,102,103,104]

◎ 指定した文字数分シフトする encode :: Int -> String -> String

encode offset msg =

map (¥c -> chr $ ord c + offset) msg

◎ 指定した文字数分逆にシフトする decode :: Int -> String -> String

decode shift msg = encode (negate shift) msg

ghci> encode 3 “hey mark”

“kh|#pdun”

ghci> decode 3 “kh|#pdun”

“hey mark”

ghci> decode 3 $ encode 3 “abc”

“abc”

◎ 各桁の数の合計がnになる最初の自然数をみつける

ghci> :t firstTo

firstTo :: Int -> Int

ghci> firstTo 1

1

ghci> firstTo 13

49

◎ 文字を数に変換する

ghci> :t digitToInt

digitToInt :: Char -> Int

ghci> digitToInt ‘2’

2

ghci> digitToInt ‘F’

15

ghci> digitToInt ‘z’

*** Exception: Char.digitToInt: not a digit 'z'

◎ 各桁の数の和を返す

digitSum :: Int -> Int

digitSum = sum . map digitToInt . show

ghci> digitSum 30

3

ghci> digitSum 1234

10

ghci> :t find

find :: (a -> Bool) -> [a] -> Maybe a

ghci> find (>4) [3,4,5,6,7]

Just 5

ghci> find odd [2,4,6,8,9]

Just 9

ghci> find (==‘z’) “haskell”

Nothing

Nothing | Just a

◎ firstToの定義 firstTo :: Int -> Maybe Int

firstTo n =

find (¥x -> digitSum x == n) [1..]

ghci> firstTo 27

Just 999

ghci> firstTo 40

Just 49999

foldl, foldl’

ghci> foldl (+) 0 [1..2000000]

2000001000000 ◦ 遅い

◦ サイズ、環境によってはスタックオーバーフロー

ghci> Data.List.foldl’ (+) 0 [1..2000000]

2000001000000 ◦ foldlより速い

◦ オーバーフローしない

foldl (+) 0 [1,2,3] =

foldl (+) (0 + 1) [2,3] =

foldl (+) ((0 + 1) + 2) [3] =

foldl (+) (((0 + 1) + 2) + 3) [] =

((0 + 1) + 2) + 3 =

(1 + 2) + 3 =

3 + 3 =

6

◎ 正格な(遅延でない)左畳み込み

foldl’ (+) 0 [1,2,3] =

foldl’ (+) 1 [2,3] =

foldl’ (+) 3 [3] =

foldl’ (+) 6 [] =

6

連想リスト

◎ 添字として任意の型を使用する配列

◎ 呼び方はいろいろ ◦ Map

◦ Dictionary

◦ Hash

◎ タプルを連想リストっぽく扱う ghci> :t lookup

lookup :: Eq a => a -> [(a,b)] -> Maybe b

ghci> lookup “b” [(“a”,1), (“b”,2), (“c”,3)]

Just 2

ghci> lookup “k” [(“a”,1), (“b”,2), (“c”,3)]

Nothing

◎連想リスト(Map)のモジュール ◦ Data.Map.lookup

◦ Data.Map.insert

◦ Data.Map.delete

◦ Data.Map.size

◦ etc..

◎ Data.Mapで連想リストを表す型

◎ keyとvalueの型を持つ

ghci> let m = Data.Map.fromList [(“foo”,1), (“bar”,2)]

ghci> :t m

m :: Data.Map.Map [Char] Integer

ghci> let m2 = Data.Map.fromList [(‘a’, True), (‘b’, False)]

ghci> :t m2

m2 :: Data.Map.Map Char Bool

ghci> import qualified Data.Map as Map

◎ keyで検索する ghci> let m = Map.fromList [(“a”,1), (“b”,2)]

ghci> Map.lookup “b” m

Just 2

ghci> Map.lookup “c” m

Nothing

◎ key=“c”, value=3の要素を挿入する ghci> let m2 = Map.insert “c” 3 m

ghci> Map.lookup “c” m2

Just 3

ghci> Map.size m2

3

◎ valueに関数を適用して新しいMapを作る

ghci> m2

fromList [("a",1),("b",2),("c",3)]

ghci> Map.map (^2) m2

fromList [("a",1),("b",4),("c",9)]

ghci> Map.map show m2

fromList [(“a”,”1”), (“b”,”2”), (“c”, “3”)]

◎ keyが重複した際の振る舞いを決める

ghci> Map.fromList [(1,2),(1,50),(1,10),(2,3)]

fromList [(1,10),(2,3)]

ghci> Map.fromListWith (+) [(1,2),(1,50),(1,10),(2,3)]

fromList [(1,62),(2,3)]

ghci> Map.fromListWith max [(1,2),(1,50),(1,10),(2,3)]

fromList [(1,50),(2,3)]

モジュールを作る

◎ 先頭にモジュール宣言を書く -- Foo.hs module Foo where ◦ モジュール名とファイル名は同じにしなければならない

◎ モジュールの中身を ◦ 全て公開したい(エクスポート) module Foo where

◦ 一部だけ公開したい

◦ module Foo (hoge, var) where

◎ test1/Nums.hs

module Nums where

double x = x + x

quadruple x = double x + double x

◎ test1/Main.hs

import Nums

double2 = Nums.double 2

quadruple4 = Nums.quadruple 4

◎ test1/Nums.hs

module Nums (double) where

double x = x + x

quadruple x = double x + double x

◎ test1/Main.hs

import Nums

double2 = Nums.double 2 -- OK

quadruple4 = Nums.quadruple 4 -- コンパイルエラー

◎ モジュールは階層構造を持つことが可能

◎ 階層構造は「A.B.C」のようにドット「.」で表す

◎ A.B.Cというモジュール名にした場合は、 A/B/C.hsというファイル構成にする

◎ test1/Figure/Sphere.hs

module Figure.Sphere where

Area radius = 4 * pi * (radius ^ 2)

◎ test1/Figure/Rectangle.hs

module Figure.Rectangle where

Area width height = width * height

◎ test1/Main.hs

import qualified Figure.Sphere as Sphere

import qualified Figure.Rectangle as Rect

sphereArea = Sphere.Area

rectArea = Rect.Area

◎ インポート, エクスポートはどちらも範囲を限定できる

◎ 標準モジュールを調べる, 使う, 理解するサイクルを 身につけたい

◎ 「こんなのないかな?」と思ったらHoogleとかで探す

◎ モジュールのエクスポートを気にしてきれいなコードを 書きたいですね

top related