Skip to content

Instantly share code, notes, and snippets.

@akiyoshi83
Last active August 29, 2015 14:06
Show Gist options
  • Save akiyoshi83/144af2bd438bfb85d8a1 to your computer and use it in GitHub Desktop.
Save akiyoshi83/144af2bd438bfb85d8a1 to your computer and use it in GitHub Desktop.
写経:『すごいHaskellたのしく学ぼう!』
doubleMe x = x + x
-- old
--doubleUs x y = x * 2 + y * 2
-- new
doubleUs x y = doubleMe x + doubleMe y
-- if式はelse節が必須
doubleSmallNumber x = if x > 100
then x
else x * 2
-- if式は値を返す"式"。
doubleSmallNumber' x = (if x > 100 then x else x * 2) +1
-- "'"は関数名として有効な文字。
-- 関数名の先頭は大文字で始められない。
-- 関数が引数をひとつも受け取らない場合定義とか名前と呼ぶ。
-- 定義、名前は不変なので文字列"It's a-me, Conan O'Brien!"と交換可能。
conanO'Brien = "It's a-me, Conan O'Brien!"
{-
Chapter 1 はじめの第一歩
-}
{-
新しめのGHCiでは:{と}:で囲むことで複数行にわたって式を記述できる。
またGHCi7.2.1以降では:set +mとすることで複数行にわたって式を記述できる。
-}
Prelude> 2 + 15
17
Prelude> 49 * 100
4900
Prelude> 1892 - 1472
420
Prelude> 5 / 2
2.5
Prelude> (50 * 100) - 4999
1
Prelude> 50 * 100 - 4999
1
Prelude> 50 * (100 - 4999)
-244950
Prelude> True && False
False
Prelude> True && True
True
Prelude> False || 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
Prelude> 5 + "llama"
<interactive>:26:3:
No instance for (Num [Char])
arising from a use of `+'
Possible fix: add an instance declaration for (Num [Char])
In the expression: 5 + "llama"
In an equation for `it': it = 5 + "llama"
{-
Chapter 1.1 関数呼び出し
-}
Prelude> succ 8
9
Prelude> min 9 10
9
Prelude> min 3.4 3.2
3.2
Prelude> max 100 101
101
-- 関数呼び出しは演算子の中で最も優先度が高い。
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
-- バッククォートで中置関数の記法で呼び出す
Prelude> div 92 10
9
Prelude> 92 `div` 10
9
{-
Chapter 1.2 赤ちゃんの最初の関数
-}
$ cat baby.hs
doubleMe x = x + x
Prelude> :l baby.hs
[1 of 1] Compiling Main ( baby.hs, interpreted )
Ok, modules loaded: Main.
*Main> doubleMe 9
18
*Main> doubleMe 8.3
16.6
$ cat baby.hs
doubleMe x = x + x
doubleUs x y = x * 2 + y * 2
Prelude> :l baby
[1 of 1] Compiling Main ( baby.hs, interpreted )
Ok, modules loaded: Main.
*Main> doubleUs 4 9
26
*Main> doubleUs 2.3 34.2
73.0
*Main> doubleUs 28 88 + doubleMe 123
478
-- doubleUsは以下の実装もOK(というか望ましい)。
doubleUs x y = doubleMe x + doubleMe y
-- if式
-- if式はelse節が必須。
doubleSmallNumber x = if x > 100
then x
else x * 2
-- if式は値を返す"式"。
doubleSmallNumber' x = (if x > 100 then x else x * 2) +1
-- "'"は関数名として有効な文字。
-- 関数名の先頭は大文字で始められない。
-- 関数が引数をひとつも受け取らない場合定義とか名前と呼ぶ。
-- 定義、名前は不変なので文字列"It's a-me, Conan O'Brien!"と交換可能。
conanO'Brien = "It's a-me, Conan O'Brien!"
{-
Chapter 1.3 リスト入門
-}
-- GHCiの中での名前の定義はletを使う。スクリプトの中ではletは不要。
Prelude> let lostNumbers = [4,8,15,16,23,42]
Prelude> lostNumbers
[4,8,15,16,23,42]
-- 連結
Prelude> [1,2,3,4] ++ [9,10,11,12]
[1,2,3,4,9,10,11,12]
Prelude> "hello" ++ " " ++ "world"
"hello world"
-- Haskellでの文字列は文字のリスト
Prelude> ['w', 'o'] ++ ['o', 't']
"woot"
-- 連結"++"を行うと1つ目のリストを最後まで走査してしまう。
-- 対して先頭に追加するのは軽い処理で":"(cons演算子)で行う。
Prelude> 'A':" SMALL CAT"
"A SMALL CAT"
Prelude> 5:[1,2,3,4,5]
[5,1,2,3,4,5]
-- 連結"++"は第1引数と第2引数がリストである必要がある。
-- 対してcons":"は第1引数がリストの中身の型で第2引数がリストとなる。
Prelude> [1,2,3,4] ++ [5]
[1,2,3,4,5]
-- 実は[1,2,3]は以下の式の糖衣構文。
Prelude> 1:2:3:[]
[1,2,3]
-- 以下の3つはそれぞれ異なる。
Prelude> []
[]
Prelude> [[]]
[[]]
Prelude> [[],[],[]]
[[],[],[]]
-- リスト要素へのアクセス
Prelude> "Steve Buscemi" !! 6
'B'
Prelude> [9.4,33.2,66.2,11.2,23.25] !! 1
33.2
-- 範囲外へのアクセスはエラー。
Prelude> "Steve Buscemi" !! 100
*** Exception: Prelude.(!!): index too large
-- 負数はそもそも書けないっぽい。
Prelude> "Steve Buscemi" !! -1
<interactive>:27:1:
Precedence parsing error
cannot mix `!!' [infixl 9] and prefix `-' [infixl 6] in the same infix expression
-- リストの中のリスト
-- 各リストは長さは違ってよいが、同じ型のリストである必要がある。
Prelude> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
Prelude> b
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
Prelude> b ++ [[1,1,1,1]]
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]
Prelude> [6,6,6]:b
[[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
Prelude> b !! 2
[1,2,2,3,4]
‐‐ リストの比較
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> head [5,4,3,2,1]
5
Prelude> tail [5,4,3,2,1]
[4,3,2,1]
Prelude> last [5,4,3,2,1]
1
Prelude> init [5,4,3,2,1]
[5,4,3,2]
-- 空のリストにこれらの操作はできない。
-- コンパイル時には検出できない。
Prelude> head []
*** Exception: Prelude.head: empty list
-- 長さのチェック。
Prelude> length [5,4,3,2,1]
5
-- 空のリストかどうかのチェック。
Prelude> null [1,2,3]
False
Prelude> null []
True
-- 反転。
Prelude> reverse [5,4,3,2,1]
[1,2,3,4,5]
-- 先頭から取得。
Prelude> take 3 [5,4,3,2,1]
[5,4,3]
Prelude> take 1 [3,9,3]
[3]
Prelude> take 5 [1,2]
[1,2]
Prelude> take 0 [6,6,6]
[]
-- 先頭から削除。
Prelude> drop 3 [8,4,2,1,5,6]
[1,5,6]
Prelude> drop 0 [1,2,3,4]
[1,2,3,4]
Prelude> drop 100 [1,2,3,4]
[]
-- 最大。
Prelude> maximum [1,9,2,3,4]
9
-- 最小。
Prelude> minimum [8,4,2,1,5,6]
1
-- 和。
Prelude> sum [5,2,1,6,3,2,5,7]
31
-- 積。
Prelude> product [6,2,1,2]
24
Prelude> product [1,2,5,6,7,9,2,0]
0
-- 要素の有無をチェック。
Prelude> 4 `elem` [3,4,5,6]
True
Prelude> 10 `elem` [3,4,5,6]
False
{-
Chapter 1.4 レンジでチン
-}
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"
Prelude> ['K'..'Z']
"KLMNOPQRSTUVWXYZ"
-- カンマで区切って2つ目の数値を指定することでステップを指定することができる。
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> [20,19..1]
[20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
-- レンジでは空リストからはじめて上限値に達するまで値を列挙する動作なので、以下は最初から上限以上なので空リストになる。
Prelude> [20..1]
[]
-- 13の倍数の最初の24個を取る例。
Prelude> [13,26..13*24]
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]
-- 上限を指定しないと無限リストになる。
-- Haskellは遅延評価なので先ほどの例は以下のように書ける。
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]
-- cycleは引数にリストを取り、その要素を無限に繰り返すリストを返す。
Prelude> take 10 (cycle [1,2,3])
[1,2,3,1,2,3,1,2,3,1]
Prelude> take 12 (cycle "LOL ")
"LOL LOL LOL "
-- repeatは引数に要素を取り、それを無限に繰り返すリストを返す。
Prelude> take 10 (repeat 5)
[5,5,5,5,5,5,5,5,5,5]
-- replicate は繰り返す回数と繰り返す要素を引数に取る。
Prelude> replicate 3 10
[10,10,10]
-- レンジで浮動小数点数を使うと制度がくるうことがあるので注意。
Prelude> [0.1,0.3..1]
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
Prelude> [0.1..1]
[0.1,1.1]
{-
Chapter 1.5 リスト内包表記
-}
‐‐ リストから<-で要素をxに束縛して|より前の式で出力する。
Prelude> [x*2 | x <- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
-- 上記は以下と同値。
Prelude> take 10 [2,4..]
[2,4,6,8,10,12,14,16,18,20]
-- 述語のある例。
-- カンマの後に述語を追記できる。述語はフィルタとも言う。
Prelude> [x*2 | x <- [1..10], x*2 >= 12]
[12,14,16,18,20]
Prelude> [x | x <- [50..100], x `mod` 7 == 3]
[52,59,66,73,80,87,94]
-- 出力部をもっと複雑にすることもできる。
-- また内包表記を関数に記述することもできる。
Prelude> let boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
Prelude> boomBangs [7..13]
["BOOM!","BOOM!","BANG!","BANG!"]
-- 述語はカンマ区切りで複数記述できる。
Prelude> [x | x <- [10..20], x /= 13, x /= 15, x /= 19]
[10,11,12,14,16,17,18,20]
-- 述語だけでなくリストも複数書ける。
-- 述語との判別は<-かな?
Prelude> [x+y | x <- [1,2,3], y <- [10,100,1000]]
Prelude> [x*y | x <- [2,5,10], y <- [8,10,11]]
[16,20,22,40,50,55,80,100,110]
Prelude> [x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]
[11,101,1001,12,102,1002,13,103,1003]
-- 名詞と形容詞のリストから言葉を作る。
Prelude> let nouns = ["hobo","frog","pope"]
Prelude> let adjectives = ["lazy","grouchy","scheming"]
Prelude> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns]
["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog","grouchy pope","scheming hobo","scheming frog","scheming pope"]
-- リスト内包表記を使ったlengthの再実装。
Prelude> let length' xs = sum [1 | _ <- xs]
Prelude> length' [1,2,3]
3
-- リスト内包表記で文字列操作。
Prelude> let removeNonUppercase st = [c | c <- st, c `elem` ['A'..'Z']]
Prelude> removeNonUppercase "Hahaha! Ahaha!"
"HA"
Prelude> removeNonUppercase "IdontLIKEFROGS"
"ILIKEFROGS"
-- リスト内包表記の入れ子。
-- GHCiでなければ複数行に分けて書くべき。
Prelude> let xss = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
Prelude> [ [ x | x <- xs, even x ] | xs <-xss]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
{-
Chapter 1.6 タプル
-}
-- ペア。
Prelude> (1, 3)
(1,3)
-- トリプル。
Prelude> (3, 'a', "hello")
(3,'a',"hello")
-- もっと。
Prelude> (50, 50.4, "hello", 'b')
(50,50.4,"hello",'b')
-- ペアは数が違うと異なるのでペアとトリプルは同じリストに入れられない。
Prelude> [(1,2), (8,11,5), (4,5)]
<interactive>:146:9:
Couldn't match expected type `(t0, t1)'
with actual type `(t2, t3, t4)'
In the expression: (8, 11, 5)
In the expression: [(1, 2), (8, 11, 5), (4, 5)]
In an equation for `it': it = [(1, 2), (8, 11, 5), (4, 5)]
-- ペアを使う。
Prelude> fst (8, 11)
8
Prelude> fst ("Wow", False)
"Wow"
Prelude> snd (8, 11)
11
Prelude> snd ("Wow", False)
False
-- zipでペアのリストを作る。
Prelude> zip [1,2,3,4,5] [5,5,5,5,5]
[(1,5),(2,5),(3,5),(4,5),(5,5)]
Prelude> zip [1..5] ["one", "two", "three", "four", "five"]
[(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
-- 長さは短い方に合わせる。
Prelude> zip [5,3,2,6,2,7,2,5,4,6,6] ["im", "a", "turtle"]
[(5,"im"),(3,"a"),(2,"turtle")]
-- よって無限リストと有限リストをzip可能。
Prelude> zip [1..] ["apple", "orange", "cherry", "mango"]
[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
-- 直角二等辺三角形を見つける
-- 第一段階
Prelude> let triples = [ (a,b,c) | c <- [1..10], a <- [1..10], b <- [1..10] ]
-- 第二段階
-- c辺が一番長いのとa辺とb辺を交換したものは同一の三角形とみなせるため以下のように範囲を縮小できる。
Prelude> let rightTriangles = [ (a,b,c) | c <- [1..10], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2 ]
-- 第三段階
Prelude> let rightTriangles' = [ (a,b,c) | c <- [1..10], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2, a+b+c == 24 ]
-- 答え
Prelude> rightTriangles'
[(8,6,10)]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment