Transcript
Page 1: Haskell超入門 Part.1
Page 2: Haskell超入門 Part.1

Nabe

Twitter: @nabe256

Page 3: Haskell超入門 Part.1

Haskell

Page 4: Haskell超入門 Part.1
Page 5: Haskell超入門 Part.1

Haskell は高階関数や静的多相型付け、定義可能な演算子、例外処理といった多くの言語で採用されている現代的な機能に加え、パターンマッチングやカリー化、リスト内包表記、ガードといった多くの特徴的な機能を持っている。また、遅延評価や再帰的な関数や代数的データ型もサポートしているほか、独自の概念として圏論のアイデアを利用し参照透過性を壊すことなく副作用のある操作(例えば 代入、入出力、配列など)を実現するモナドを含む。

(by Wikipedia)

Page 6: Haskell超入門 Part.1

一言で言うと

関数型言語

です。

Page 7: Haskell超入門 Part.1

ちょっとだけ詳しく言うと

純粋関数型

プログラミング言語

です。

Page 8: Haskell超入門 Part.1

手続き型言語

› 記述された命令を逐次実行していく。

› 一般的に用いられる言語。

関数型言語

› すべての計算は関数の評価で行われる。

› 変数が無く、定数しか無い。

› 手続き型でのループは再帰で実現する他、

様々な機能が別の形で実現されている。

Page 9: Haskell超入門 Part.1

その他の種類について興味のある方は

「プログラミングパラダイム」という

キーワードで調べると良いでしょう。

Page 10: Haskell超入門 Part.1

基本的に副作用が無いため、

原因不明のバグが出ることが少ない。

どういう物が必要かを記述していけば

書けてしまうので、短時間での開発が可能。

Page 11: Haskell超入門 Part.1

クイックソートの例

qsort [] = []

qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger

where

smaller = [a | a <- xs, a <= x]

larger = [b | b <- xs, b > x]

非常に簡潔に記述出来る。

Page 12: Haskell超入門 Part.1

代表的なもの

› 再帰関数(自分自身を呼び出す関数)

› λ式(ラムダ式、無名関数)

› 無限リスト(長さが無限のデータ)

› 遅延評価 (必要な時まで式を評価しない)

› 高階関数(関数の引数または返り値が関数)

› 純粋 (基本的に副作用が無い)

› モナド(副作用を扱う仕組み)

他多数!

Page 13: Haskell超入門 Part.1

Haskellが使用されているプロジェクト › Pugs

HaskellによるPerl6の実装

› darcs 分散バージョン管理システム

› Whitespace タブ・空白・改行のみで記述する言語

› Monadius グラディウス風のゲーム

› Mighttpd (発音:Mighty) HTTPサーバ

Page 14: Haskell超入門 Part.1

基本的な文法をいくつか覚える。

簡単なプログラムを書けるように

なれれば素敵。

Page 15: Haskell超入門 Part.1

GHC (Glasgow Haskell Compiler)

Haskellにおける事実上の標準コンパイラ。

実行が高速で、ライブラリ多数。

Hugs

軽量なコンパイラ、基礎を学ぶには十分。

導入も簡単だが開発は停止している模様。

Haskell Platform

Haskell処理系のGHCに加えて

各種ツールやライブラリが揃っている。

Page 16: Haskell超入門 Part.1

UNIXであれば

GHCか、可能であればHaskell Platformを。

Windowsであれば

Haskell Platformが最適。

高度な事をするのであれば、

UNIXならGCC

WindowsならMinGW

などが必要。

Page 17: Haskell超入門 Part.1

今回はGHCに付属する

› コンパイラのGHC

› インタプリタのGHCI

を使用します。

環境がある方は挑戦してみましょう。

Page 18: Haskell超入門 Part.1

まずはインタプリタから

実行してみます。

Page 19: Haskell超入門 Part.1

Win/UNIXならghci、MinGWならghcii.shを起動します。

$ ghci

GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help

Loading package ghc-prim ... linking ... done.

Loading package integer-gmp ... linking ... done.

Loading package base ... linking ... done.

Loading package ffi-1.0 ... linking ... done.

Prelude>

Prelude(標準ライブラリ)が出てくれば準備完了です。

:quit または :q で終了します。 Prelude> :q

Leaving GHCi.

$

Page 20: Haskell超入門 Part.1

いくつか試してみましょう

> 256

256

> 1+2*3

7

> 7/2

3.5

> 2^256

Page 21: Haskell超入門 Part.1

結合順位もしっかりあります

> 8/4/2

1.0

> 2^3^2

512

> (2^3)^2

64

Page 22: Haskell超入門 Part.1

:typeで型が見られます。(:t と略せる)

> :type 256

256 :: Num a => a

> :t 1+2

1+2 :: Num a => a

> :t 3.5

3.5 :: Fractional a => a

> :t 7/2

Page 23: Haskell超入門 Part.1

関数は :info で見ると色々情報が出ます。

(:i と略せる)

> :info (+)

class (Eq a, Show a) => Num a where

(+) :: a -> a -> a

...

-- Defined in GHC.Num

infixl 6 +

Page 24: Haskell超入門 Part.1

Haskellでは型が非常に重要な要素です。

型が分かればHaskellを理解する

良い手がかりになります。

型の詳細は後ほど。

Page 25: Haskell超入門 Part.1

文字関係です。

> „a‟

„a‟

> “Hello”

Hello

Page 26: Haskell超入門 Part.1

リストを使ってみます。

> [1,2,3]

[1,2,3]

> head [1,2,3]

1

> tail [1,2,3]

[2,3]

Page 27: Haskell超入門 Part.1

リストは色々な操作が可能です。

> [1,2,3,4,5] !! 2

3

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

[1,2,3]

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

[4,5]

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

[5,4,3,2,1]

Page 28: Haskell超入門 Part.1

長さも変更可能です。

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

5

> length (tail [1,2,3,4,5])

4

> [1,2,3] ++ [4,5]

[1,2,3,4,5]

Page 29: Haskell超入門 Part.1

: 演算子(cons)

> 1:2

[1,2]

> 1:[2,3]

[1,2,3]

Page 30: Haskell超入門 Part.1

同じ型式でないといけません。

以下はエラーが発生します。

> [1,„a‟]

> [1,“Hello”]

Page 31: Haskell超入門 Part.1

ちなみに、文字と文字列の関係

> [„a‟,„b‟,„c‟]

“abc”

文字列は文字のリスト、ということです。

Page 32: Haskell超入門 Part.1

タプルを使ってみます。

> (1,2)

(1,2)

> (1,2,3)

(1,2,3)

Page 33: Haskell超入門 Part.1

長さを変えられない代わりに、

違う型でも使えます。

> (1,‟a‟)

(1,‟a‟)

> (1,”Hello”)

(1,”Hello”)

> (1,'a',"hello",[1,2,3])

(1,'a',"hello",[1,2,3])

Page 34: Haskell超入門 Part.1

タプルを使った関数。

> fst (1,”hello”)

1

> snd (1,”hello”)

”hello”

Page 35: Haskell超入門 Part.1

Prelude> putStrLn “Hello, World!”

Hello, World!

Hello, World! と出力してくれる

おなじみのサンプルです。

Page 36: Haskell超入門 Part.1

putStrLn “Hello, World!”

関数を適用する場合は

空白文字を使います。

Page 37: Haskell超入門 Part.1

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

[1,2,3]

2引数の関数です。

リストから指定した数の要素を

取り出します。

2つ以上の引数に対して関数適用する場合も空白文字を使います。

Page 38: Haskell超入門 Part.1

Prelude> take 3 (tail [1,2,3,4,5])

[2,3,4]

引数に関数を渡す場合は括弧を使います。

Page 39: Haskell超入門 Part.1

関数を定義して実行してみましょう。

Page 40: Haskell超入門 Part.1

$ ghci

Prelude> let main = putStrLn “Hello, World!”

等式の左辺が関数名、

右辺は関数の内容になります。

let 関数名=関数の内容

という形式です。

Page 41: Haskell超入門 Part.1

先ほど定義したmainを使用してみます。

Prelude> main

Hello, World!

実行出来ました。

Page 42: Haskell超入門 Part.1

先ほどはインタプリタで実行しました。

ならば今度は

コンパイルして実行してみましょう。

Page 43: Haskell超入門 Part.1

以下の行をhello.hsというファイルに 保存します。

main = putStrLn “Hello, World!”

関数名=関数の内容という形式です。 先程と同様に等式の左辺が関数名、 右辺は関数の内容になります。

こちらが一般的な書き方です。 先ほどのletはインタプリタ用と考えてください。

Page 44: Haskell超入門 Part.1

hello.hsをコマンドラインから コンパイルして実行してみます。

$ ghc --make hello.hs

$ ./hello

Hello, World!

$

実行できました。

Page 45: Haskell超入門 Part.1

実際にHaskellを扱うに当たって

コードを記述して実行する方法は

いくつかあります。

先程の例

› インタプリタ上で記述して実行

› ソースをコンパイルして実行

Page 46: Haskell超入門 Part.1

実際にHaskellを扱うに当たって

コードを記述して実行する方法は

いくつかあります。

その他

› ソースをインタプリタに渡して実行

› ソースをインタプリタから読み込んで実行

Page 47: Haskell超入門 Part.1

ソースをインタプリタに渡して実行

$ ghci hello.hs

Ok, modules loaded: Main.

Prelude Main> main

Hello, World!

Prelude Main> :q

$

Page 48: Haskell超入門 Part.1

ソースをインタプリタから読み込んで実行

$ ghci

Prelude> :load hello.hs

Ok, modules loaded: Main.

Prelude Main> main

Hello, World!

Prelude Main> :q

$

Page 49: Haskell超入門 Part.1

コンパイルして実行する場合

› 高速に実行できる

› 実行するにはmainを定義する必要がある

インタプリタで実行する場合

› 便利な機能が沢山あるので

学習する時に非常に便利

› mainを定義しなくても良い

Page 50: Haskell超入門 Part.1

:reloadで再読み込みが出来る

Page 51: Haskell超入門 Part.1

先程のhello.hsを読み込む。

> :load hello.hs

> main

Hello, World!

Page 52: Haskell超入門 Part.1

GHCIを起動したまま、

別ウィンドウでhello.hsを編集する。

$ cat hello.hs

main = putStrLn “Hello, World!”

$ #編集

$ cat hello.hs

main = putStrLn “Hello, Haskell!”

Page 53: Haskell超入門 Part.1

GHCIのウィンドウに戻り、

再読み込み。

> :load hello.hs

> main

Hello, World!

> :reload

Page 54: Haskell超入門 Part.1

GHCIのウィンドウに戻り、

再読み込み。そして実行。

> :load hello.hs

> main

Hello, World!

> :reload

> main

Hello, Haskell!

Page 55: Haskell超入門 Part.1

インタプリタならではのやり方でした。

Page 56: Haskell超入門 Part.1

あとは :type や :info など。

調べるのに便利な機能が色々あります。

Page 57: Haskell超入門 Part.1

Haskellは型が重要。

型さえ覚えれば

第一の突破口クリア。

Page 58: Haskell超入門 Part.1

1引数の関数を見てみる

headを見てみる

> head [1,2,3]

1

> :t head

head :: [a] -> a

Page 59: Haskell超入門 Part.1

head :: [a] -> a

Page 60: Haskell超入門 Part.1

head :: [a] -> a

関数名

Page 61: Haskell超入門 Part.1

head :: [a] -> a

これ以降は型を表す

Page 62: Haskell超入門 Part.1

head :: [a] -> a

Page 63: Haskell超入門 Part.1

head :: [a] -> a

a型

任意の型

Page 64: Haskell超入門 Part.1

head :: [a] -> a

[a]型

任意のリスト型

Page 65: Haskell超入門 Part.1

head :: [a] -> a

[a] -> a型

[a]型からa型へと変換する型

Page 66: Haskell超入門 Part.1

head :: [a] -> a

[a] -> a型

[a]型からa型へと変換する関数

Page 67: Haskell超入門 Part.1

head :: [a] -> a

[a] -> a型

任意のリスト型から任意の型へと

変換する関数

Page 68: Haskell超入門 Part.1

a型を具体的な型に置き換えるため、

先程の例

> head [1,2,3]

1

を当てはめてみる。

Page 69: Haskell超入門 Part.1

head :: [a] -> a

[a] -> a型

任意のリスト型から任意の型へと

変換する関数

Page 70: Haskell超入門 Part.1

head :: [Num] -> Num

[Num] -> Num型

数値のリスト型から数値型へと

変換する関数

Page 71: Haskell超入門 Part.1

head :: [Num] -> Num

headは[Num] -> Num型

数値のリスト型から数値型へと

変換する関数

Page 72: Haskell超入門 Part.1

head :: [Num] -> Num

headは[Num] -> Num型

数値のリスト型から数値型へと

変換する関数

本当にそうなのか確認。

Page 73: Haskell超入門 Part.1

> head [1,2,3]

1

Page 74: Haskell超入門 Part.1

> head [1,2,3]

数値のリスト型

1

数値型

Page 75: Haskell超入門 Part.1

> head [1,2,3]

数値のリスト型

1

数値型

数値のリスト型から

数値型へと正しく変換されている。

Page 76: Haskell超入門 Part.1

1引数の関数を見てみる(2)

sumを見てみる

> sum [1,2,3,4,5]

15

> :t sum

sum :: Num a => [a] -> a

Page 77: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

Page 78: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

関数名

Page 79: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

sum関数の型

Page 80: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

[a]型をa型に変換する関数

Page 81: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

aを数値型とするクラス

Page 82: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

数値のリスト型を

数値型に変換する関数

Page 83: Haskell超入門 Part.1

型の読み方

sum :: Num a => [a] -> a

数値のリスト型を

数値型に変換する関数

本当にそうなのか確認

Page 84: Haskell超入門 Part.1

> sum [1,2,3,4,5]

15

Page 85: Haskell超入門 Part.1

> sum [1,2,3,4,5]

数値のリスト型

15

数値型

数値のリスト型から数値型へ

正しく変換されている

Page 86: Haskell超入門 Part.1

2引数の関数を見てみる(1)

takeを見てみる

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

[1,2,3]

> :t take

take :: Int -> [a] -> [a]

Page 87: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

Page 88: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

関数名

Page 89: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

Page 90: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

右結合なので

このように解釈する

Page 91: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

[a]型を受け取り

[a]型を返す関数

Page 92: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

関数A

Page 93: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

Int型を受け取り

関数Aを返す関数

Page 94: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

Int型を受け取り

関数A

を返す関数

Page 95: Haskell超入門 Part.1

take :: Int -> ([a] -> [a])

Int型を受け取り

([a]型を受け取り

[a]型を返す関数)

を返す関数

Page 96: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

Int型を受け取り

[a]型を受け取り

[a]型を返す関数

を返す関数

Page 97: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

Int型を受け取り

[a]型を受け取り

[a]型を返す関数

を返す関数

言い換えると

Page 98: Haskell超入門 Part.1

take :: Int -> [a] -> [a]

Int型と[a]型を受け取り

[a]型を返す関数

という事です

Page 99: Haskell超入門 Part.1

非常に分かりづらいので

順を追って解読してみる。

Page 100: Haskell超入門 Part.1

takeにInt型の値(2)を渡してみる

take :: Int -> [a] -> [a]

Int型を受け取り

[a]型を受け取り

[a]型を返す関数

を返す関数

Page 101: Haskell超入門 Part.1

takeにInt型の値(2)を渡してみる

(take 2) :: Int -> [a] -> [a]

Int型を受け取り

[a]型を受け取り

[a]型を返す関数

を返す関数

Page 102: Haskell超入門 Part.1

takeにInt型の値(2)を渡してみる

(take 2) :: Int -> [a] -> [a]

Int型を受け取り

[a]型を受け取り

[a]型を返す関数

を返す関数

Page 103: Haskell超入門 Part.1

takeにInt型の値(2)を渡してみる

(take 2) :: [a] -> [a]

[a]型を受け取り

[a]型を返す関数

Page 104: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2) :: [a] -> [a]

[a]型を受け取り

[a]型を返す関数

Page 105: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) :: [a] -> [a]

[a]型を受け取り

[a]型を返す関数

Page 106: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) :: [a] -> [a]

[a]型を受け取り

[a]型を返す関数

Page 107: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) :: [a]

[a]型を返す関数

Page 108: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) = [4,5]

[a]型を返す関数

Page 109: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) = [4,5]

[4,5]を返す関数

Page 110: Haskell超入門 Part.1

(take 2)に[a]型の値([4,5,6])を渡してみる

(take 2 [4,5,6]) = [4,5]

結果 = [4,5]

本当にそうなのか確認

Page 111: Haskell超入門 Part.1

> take 2 [4,5,6]

[4,5]

Page 112: Haskell超入門 Part.1

> take 2 [4,5,6]

Int型

[4,5]

Page 113: Haskell超入門 Part.1

> take 2 [4,5,6]

Int型 [a]型

[4,5]

Page 114: Haskell超入門 Part.1

> take 2 [4,5,6]

Int型 [a]型

[4,5]

[a]型

Page 115: Haskell超入門 Part.1

> take 2 [4,5,6]

Int型 [a]型

[4,5]

[a]型

どうやら本当になったようです。

Page 116: Haskell超入門 Part.1

実際にGHCIでも確認してみましょう。

Page 117: Haskell超入門 Part.1

> :t take

take :: Int -> [a] -> [a]

> :t take 2 take 2 :: [a] -> [a]

> :t take 2 [4,5,6]

take 2 [4,5,6] :: Num a => [a]

> take 2 [4,5,6]

[4,5]

Page 118: Haskell超入門 Part.1

確かにそのように動作していました。

Page 119: Haskell超入門 Part.1

少々かみ砕きすぎたでしょうか。

余計難しくなったかも知れません。

一言で説明しても順を追って説明しても

難解な概念ですが、何度かソースを書いて

試して見ると、急にすっと分かるように

なります。

Page 120: Haskell超入門 Part.1

型を意識して作る必要があります。

型を考えるという事は

入力の形と出力の形を考える事、

そして関数の入出力と処理内容を

切り分ける事へと繋がります。

型の概念は難しい部分がありますが、

型が分かるようになると

難しい事を考えなくても

プログラムが書けるようになります。

Page 121: Haskell超入門 Part.1

全体的に駆け足で説明する事に

なってしまいました。

Haskellが分かるようになると

いつの間にか面白いと思うようになります。

ありがとうございました。


Top Related