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
必須
-
-
関数名には
'
を使える- 特別な意味はない
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
-
要素数や型が異なるタプルは異なる型
- なので単一のリストには入れられない
- 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
-
条件
- 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)]