すごいH本 Ch1 Starting Out
- Starting Out
- Calling Functions
- Baby's First Functions
- An Intro to Lists
- Texas Ranges
- I'm a list Comprehension
- Tuples
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
5 * 5
の*
とか- 演算子は関数
- 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
必須
- Haskellプログラムは関数の集まり
- 関数名には
'
を使える- 特別な意味はない
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"
- Lisp感ある
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
,min
でreduce
したかんじ
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}
- Haskellではこう
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
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
- 条件
- 3辺整数
- 3辺10以下
- 周長24
- 直角三角形である = ピタゴラスの定理
[(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)]