勉強日記

チラ裏

すごいH本 Ch1 Starting Out

learnyouahaskell.com


Starting Out

  • docker環境であそぶことにした

docker-compose.yml

version: "3"
services:
  haskell:
    image: haskell:8.6.3
    volumes:
      - ../src:/root/src
    entrypoint: bash
    tty: true
    working_dir: /root/src
docker-compose up -d
docker-compose exec haskell ghci
GHCi, version 8.6.3: http://www.haskell.org/ghc/  :? for help

Prelude> 
  • +*-/による四則演算
    • 乗除優先
    • ()でさらに優先
    • すごくふつう
  • 論理演算
    • notで論理反転
Prelude> True && False
False
Prelude> True && True
True
Prelude> not False
True
Prelude> not (True && True)
False
  • 比較
    • ==: 等価
    • /=: 不等
Prelude> 5 ==5 
True
Prelude> 1 == 0
False
Prelude> 5 /= 5
False
Prelude> 5 /= 4
True
Prelude> "hello" == "hello"
True
  • 型違いはNG
Prelude> 5 + "hoge"

<interactive>:20:1: error:
    • No instance for (Num [Char]) arising from a use of ‘+’
    • In the expression: 5 + "hoge"
      In an equation for ‘it’: it = 5 + "hoge"
      
Prelude> 5 == "hello"

<interactive>:21:1: error:
    • No instance for (Num [Char]) arising from the literal ‘5’
    • In the first argument of ‘(==)’, namely ‘5’
      In the expression: 5 == "hello"
      In an equation for ‘it’: it = 5 == "hello"
Prelude> 5 + 4.0
9.0
  • 5が5.0に

Calling Functions

  • infix function
  • prefix function
    • だいたいこれ
Prelude> succ 8
9
  • 関数の適用は最高優先度
Prelude> succ 9 + max 5 4 + 1
16
Prelude> (succ 9) + (max 5 4) + 1
16
Prelude> succ 9 * 10
100
Prelude> succ (9 * 10)
91
  • 2値関数をinfixでcallする
Prelude> div 92 10
9
Prelude> 92 `div` 10
9
  • もともとinfixのやつをprefixでcallする
Prelude> (*) 9 10
90

Baby's First Functions

  • 関数定義

baby.hs

doubleMe x = x + x
  • 読み込んで使用 :l babyコマンド
Prelude> :l baby
[1 of 1] Compiling Main             ( baby.hs, interpreted )
Ok, one module loaded.
*Main> doubleMe 9
18
*Main> doubleMe 8.3
16.6
  • 関数の宣言順に制限はない

baby.hs

doubleUs x y = doubleMe x + doubleMe y

doubleMe x = x + x
  • 保存後、:l babyで再読込
*Main> :l baby
[1 of 1] Compiling Main             ( baby.hs, interpreted )
Ok, one module loaded.
*Main> doubleUs 4 9
26
  • if
    • であり、文ではない
doubleSmallNumber x = if x > 100
                         then x
                         else x * 2
*Main> :l baby
[1 of 1] Compiling Main             ( baby.hs, interpreted )
Ok, one module loaded.
*Main> doubleSmallNumber 99
198
*Main> doubleSmallNumber 100
200
*Main> doubleSmallNumber 101
101
*Main> 
  • else必須
    • Haskellプログラムは関数の集まり
      • 他の関数から利用される
    • 何かしら値を返さないといけない
    • ので、手続き型と異なり、else必須
  • 関数名には'を使える
    • 特別な意味はない

baby.hs

doubleSmallNumber' x = (if x > 100 then x else x * 2) + 1
Prelude> :l baby.hs
[1 of 1] Compiling Main             ( baby.hs, interpreted )
Ok, one module loaded.
*Main> doubleSmallNumber' 1
3

An Intro to Lists

  • homogeneous data structure
    • 全要素同じ型であること

Concatenation

Prelude> let lostNumbers = [4,8,15,16,23,42]
Prelude> lostNumbers
[4,8,15,16,23,42]
  • let...ghci上で名前を定義するのにつかう
  • 文字列は文字のリスト
Prelude> ['a','b']
"ab"
  • ++: リストとリストの結合
Prelude> "hello" ++ " " ++ "world"
"hello world"
  • :: 要素とリストの結合
    • cons
Prelude> 'A':" SMALL CAT"
"A SMALL CAT"
Prelude> 1:2:3:[]
[1,2,3]

Accessing List Elements

  • !!で要素アクセス
Prelude> "hello world"!!0
'h'
Prelude> "hello world"!!6
'w'
Prelude> "hello world"!!-1

<interactive>:13:14: error:
    • Variable not in scope: (!!-) :: [Char] -> Integer -> t
    • Perhaps you meant ‘!!’ (imported from Prelude)
Prelude> "hello world"!!100
*** Exception: Prelude.!!: index too large
Prelude> 
  • マイナスで右からとかはない
  • 範囲外は例外

Lists Inside Lists

Prelude> [[],[],[]]
[[],[],[]]
Prelude> [[1,2,3],[1,2,3,4,5]]
[[1,2,3],[1,2,3,4,5]]
Prelude> [[1,2,3],[1.2]]
[[1.0,2.0,3.0],[1.2]]
Prelude> [[1,2,3],"hello world"]

<interactive>:19:3: error:
    • No instance for (Num Char) arising from the literal ‘1’
    • In the expression: 1
      In the expression: [1, 2, 3]
      In the expression: [[1, 2, 3], "hello world"]
  • 素数違いは同じ型
  • 要素の型違いは異なる型
    • Int を Floatにするとかはいい感じにやってくれる
    • いい感じに変換できない場合は例外

Comparing Lists

Prelude> [3,2,1] > [2,1,0]
True
Prelude> [3,2,1] > [2,10,100]
True
Prelude> [3,4,2] < [3,4,3]
True
Prelude> [3,4,2] > [2,4]
True
Prelude> [3,4,2] == [3,4,2]
True
Prelude> [1] > []
True
  • 頭から再帰的に比較
    • headが等価ならtailを比較
    • 空でないリスト > 空のリストが成立する

More List Operations

Prelude> head [1,2,3,4,5]
1
Prelude> tail [1,2,3,4,5]
[2,3,4,5]
Prelude> init [1,2,3,4,5]
[1,2,3,4]
Prelude> last [1,2,3,4,5]
5
  • head:tail
  • init++[last]
  • いずれも[]に対して適用すると死ぬ
  • 空でないことのチェック
Prelude> map length [[1,2,3,4,5], []]
[5,0]
Prelude> map null [[1,2,3,4,5], []]
[False,True]
  • reverse
    • 反転
Prelude> reverse [1,2,3,4,5]
[5,4,3,2,1]
  • take
    • リストの頭から何個かとる
    • 無限リストでもとれる
Prelude> take 3 [1..]
[1,2,3]
  • drop
    • リストの頭から何個か切り落とす
    • (1,2,3,)4,5,6,...
Prelude> take 3 (drop 3 [1..])
[4,5,6]
  • maximum, minumum
    • リストの中で最大・最小
    • 二項関数max,minreduceしたかんじ
Prelude> maximum [1,9,2,3,4]
9
Prelude> minimum [8,4,2,1,5,6]
1
  • sum, product
    • ∑とかΠとか
Prelude> sum [1,2,3,4]
10
Prelude> product [1,2,3,4]
24
  • 空のリストを渡すと単位元が返る
Prelude> sum []
0
Prelude> product []
1
  • elem
    • 要素をもつか確認
Prelude> 1 `elem` [1,2,3]
True
Prelude> elem 1 [1,2,3]
True
  • ふつうinfixで呼び出される
    • よみやすい
Prelude> 0 `elem` [1,2,3]
False

Texas Ranges

  • range
Prelude> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
Prelude> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
  • 等差数列を表現可能
    • [a,b..c]
    • a,bで公差を導出
Prelude> [2,4..20]
[2,4,6,8,10,12,14,16,18,20]
Prelude> [3,6..20]
[3,6,9,12,15,18]
Prelude> [10..1]
[]
Prelude> [10,9..1]
[10,9,8,7,6,5,4,3,2,1]
  • 公差0だと無限リストになるっぽい
take 3 [1,1..1]
[1,1,1]
  • 初項13、公差13の24要素のリストをつくる例
    • 無限リスト使え
Prelude> [13,26..24*13]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
Prelude> take 24 [13,26..]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
  • lazy evaluation
  • 無限リストつくるやつ
    • cycle
      • リストを受けとり、それを結合して無限リスト作る
    • repeat
      • 受け取った要素の無限リスト作る
Prelude> take 10 (cycle "hoge")
"hogehogeho"
Prelude> take 10 (repeat 5)
[5,5,5,5,5,5,5,5,5,5]
  • 後者は後も書ける
Prelude> replicate 10 5
[5,5,5,5,5,5,5,5,5,5]
Prelude> [0.1,0.3..1]
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]

I'm a list Comprehension

  • リストを...
    • filter
    • transform
    • combine
  • 数学由来のやつ
{2・x|x ∈ N, x <= 10}
Prelude> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
  • これは駄目
    • 無限リスト全なめするから処理が返ってこない
Prelude> [x*2 | x <- [1..], x <= 10]
[2,4,6,8,10,12,14,16,18,20
  • 剰余群
Prelude> [x | x <- [50..100], x `mod` 7 == 3]
[52,59,66,73,80,87,94]
  • 関数にする
boomBangs xs = [if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
*Main> boomBangs [7..13]
["BOOM!","BOOM!","BANG!","BANG!"]
  • combination
    • 書いた順番に多重ループ回すような感じの出力になる
Prelude> [x+y | x <- [1,2,3], y <- [10,100,1000]]
[11,101,1001,12,102,1002,13,103,1003]
  • 自作length関数をつくる
    • 使わない名前は_をつけるのが通例
Prelude> let length' xs = sum [1 | _ <- xs]
Prelude> length' [1,2,3]
3
  • ネストもできる
    • かくのめんどくさい

Tuples

  • heterogeneous data structure
    • 各要素異なる型でもよい
Prelude> (1, 3)
(1,3)
Prelude> (3, 'a', "hello")
(3,'a',"hello")

Using Tuples

  • 素数や型が異なるタプルは異なる型
    • なので単一のリストには入れられない
    • cf. リストは要素数が異なっても同じ型
Prelude> [(1,2), (1,2,3)]

<interactive>:23:9: error:
    • Couldn't match expected type ‘(a, b)’
                  with actual type ‘(Integer, Integer, Integer)’
    • In the expression: (1, 2, 3)
      In the expression: [(1, 2), (1, 2, 3)]
      In an equation for ‘it’: it = [(1, 2), (1, 2, 3)]
    • Relevant bindings include
        it :: [(a, b)] (bound at <interactive>:23:1)

Prelude> [(1, 2), ('a', 'b')]

<interactive>:24:3: error:
    • No instance for (Num Char) arising from the literal ‘1’
    • In the expression: 1
      In the expression: (1, 2)
      In the expression: [(1, 2), ('a', 'b')]

Using Pairs

  • fst, snd
    • pairのfirst/secondを取得する関数
    • triple等には使えない
Prelude> fst (1,2)
1
Prelude> snd (1,2)
2
  • zip
    • 配列をふたつ受取りpairの配列をつくるやつ
Prelude> zip [0..] ["foo", "bar", "baz"]
[(0,"foo"),(1,"bar"),(2,"baz")]

Finding the Right Triangle

[(a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
[(6,8,10)]
  • 順序集合にするためにc <- [1..10], b <- [1..c], a <- [1..b]にしてる
    • 非順序集合にするならc <- [1..10], b <- [1..10], a <- [1..10]
      • 計算コスト上がる
Prelude> [(a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10], a^2 + b^2 == c^2, a+b+c==24]
[(8,6,10),(6,8,10)]