Haskell勉強会メモ #1

「すごいHaskell たのしく学ぼう!」を読む会@渋谷某所

↓↓ 第一回(2015/2/6)の学習範囲 ↓↓

  • イントロダクション
    • で、Haskellって何なの?
    • Haskellの世界に飛び込むのに必要なもの
  • 第一章 はじめの第一歩
    • 1.1 関数呼び出し
    • 1.2 赤ちゃんの最初の関数
    • 1.3 リスト入門
    • 1.4 レンジでチン!
    • 1.5 リスト内包表記
    • 1.6 タプル

イントロダクション

Haskellはおもしろい。以上! (introduction)

「副作用を持たない」とは

1.同じ条件を与えれば、必ず同じ結果が得られる
2.他のいかなる機能の結果にも影響を与えない
 (入力によって出力は変わるけど、その他のものは何も変わらない)

→この性質を「参照透過性」と呼ぶ。


第一章 はじめの一歩

もしあなたが、イントロダクションなんて読まないというとんでもない考えの持ち主なら、今すぐ1つ前の章に戻って読んできてください。この本の使い方や、GHCに関数をロードする方法が書いてあります。(p.1)

ghci> True && False
False
ghci> True && False
True
ghci> False || True
True
ghci> not False
True
ghci> not (True && True)
False

ここで、True Falseが大文字なのがポイント
=> 大文字でスタートするものは、どこかで定義されている

Prelude> :i True
data Bool = ... | True   -- Defined in ‘GHC.Types’

だから、関数名は小文字で始める
(なにかを自分で定義するなら大文字はじめにする)

succ

=> 順序を持つデータ型に対して使える関数。引数で与えられたモノの「次の順番」のモノを返す

Prelude> succ 9
10
Prelude> succ 'A'
'B'

min, max

Prelude> min 9 10
9

複数の引数の間にカンマがない!

(succ 9 ) + (succ 4)
=> (+) (succ 9) (succ 4)

↑は同値!

(+) succ 9 (succ 4)

→エラー。「succ」にも()をつけないと、(+)の引数が”succ” と 9 であると解釈されてしまう。

バッククオート(`)

Prelude> div 92 8
11
Prelude> 92 `div` 8
11

バッククオートで関数を囲むことで中置表記にできる

:tと:i

  • :t => 型を表示
  • :i => いろいろ表示

:iで(+)を見てみよう

Prelude> :i (+)
class Num a where
  (+) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Num’
infixl 6 +

はじめの'a', 2つ目の'a'が引数、最後の'a'が戻り値。

infixr5 ++ => 優先順位。大きいほうが強い => 参考サイト

ちなみにこんなのも(a where)

class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
  -- Defined in ‘GHC.Num’

ifについて

doubleSmallNumber x = if x > 100
                        then x
                        else x * 2

必ずelseが必要
=>関数として、呼び出されたら必ず何かを返す必要がある。

  • 式;受け取った値を評価して返す
  • 文;実行の結果、何かの状態を変化させる

1.3 リスト

文字列はリスト!

javaとかなら文字列とlistは別、それぞれにメソッドとか定義されている。
haskellでは、文字列=文字のリスト、String =「文字のリストのエイリアス

‘A’ : “ Small cat” => “A Small cat”
[1,2,3,4] ++ [5] => [1,2,3,4,5]

Haskell では[1,2,3] は 1:2:3: の単なる構文糖衣です => は空のリストです。その先頭に 3 を追加すると [3] になります。そこにさらに 2 を先頭に追加すると、 [2,3] になります。(p.8)

Prelude> "Steve Buscemi" !! 6
'B'

リスト操作の関数

head

ghci> head [5,4,3,2,1]
5

tail

ghci> tail [5,4,3,2,1]
[4,3,2,1]

last

ghci> last [5,4,3,2,1]
1

init

ghci> init [5,4,3,2,1]
[5,4,3,2]

length

ghci> length [5,4,3,2,1]
5

null

ghci> null [1,2,3]
False
ghci> null []
True

reverse

ghci> reverse [5,4,3,2,1]
[1,2,3,4,5]

take x

ghci> take 3 [5,4,3,2,1]
[5,4,3]

drop x

ghci> drop 3 [5,4,3,2,1]
[2,1]

maximum, minimum

ghci> maximum [1,3,5,7,9]
9
ghci> minimum [3,5,7,9,1]
1

sum, product

ghci> sum [2,4,5]
11
ghci> product [2,4,7,9]
504

elem

ghci> 4 `elem` [3,4,5,6]
True
ghci> 10 `elem` [3,4,5,6]
False

[1..100] !! i (iをインクリ)みたいのは効率悪い

  • リストは先頭から評価される
    => head, tailに比べるとlast, initは効率が悪い

  • lengthも、要素をはじめから走査していくので、数の多さにによっては実行速度に響く

  • nullは一つ目の要素を評価するので、すぐ実行結果が出る

レンジでチン

レンジ(range)

ghci> [1..20]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
ghci> ['A'..'Z']
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ghci> [2,4..20]
  • 遅延評価のお陰で無限を扱える
    =>実行時に評価されるので定義の時点では無限になってもok!
ghci> take 5 [2,4..]
[2,4,6,8,10]
  • cycle => リストを受け取って繰り返す
  • repeat=> 要素を受け取って繰り返す

リスト内包表記

ghci> [ x * 2 | x <- [1..10] ]
[2,4,6,8,10,12,14,16,18,20]

 参考