Haskell超入門 Part.1

  • 1,909 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,909
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
16
Comments
0
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1.  Nabe Twitter: @nabe256
  • 2. Haskell
  • 3.  Haskell は高階関数や静的多相型付け、定義可 能な演算子、例外処理といった多くの言語で採 用されている現代的な機能に加え、パターン マッチングやカリー化、リスト内包表記、ガー ドといった多くの特徴的な機能を持っている。 また、遅延評価や再帰的な関数や代数的データ 型もサポートしているほか、独自の概念として 圏論のアイデアを利用し参照透過性を壊すこと なく副作用のある操作(例えば 代入、入出力、 配列など)を実現するモナドを含む。 (by Wikipedia)
  • 4.  一言で言うと 関数型言語 です。
  • 5.  ちょっとだけ詳しく言うと 純粋関数型 プログラミング言語 です。
  • 6.  手続き型言語 › 記述された命令を逐次実行していく。 › 一般的に用いられる言語。 関数型言語 › すべての計算は関数の評価で行われる。 › 変数が無く、定数しか無い。 › 手続き型でのループは再帰で実現する他、 様々な機能が別の形で実現されている。
  • 7.  その他の種類について興味のある方は 「プログラミングパラダイム」という キーワードで調べると良いでしょう。
  • 8.  基本的に副作用が無いため、 原因不明のバグが出ることが少ない。 どういう物が必要かを記述していけば 書けてしまうので、短時間での開発が可能。
  • 9.  クイックソートの例qsort [] = []qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger where smaller = [a | a <- xs, a <= x] larger = [b | b <- xs, b > x] 非常に簡潔に記述出来る。
  • 10.  代表的なもの › 再帰関数(自分自身を呼び出す関数) › λ式(ラムダ式、無名関数) › 無限リスト(長さが無限のデータ) › 遅延評価 (必要な時まで式を評価しない) › 高階関数(関数の引数または返り値が関数) › 純粋 (基本的に副作用が無い) › モナド(副作用を扱う仕組み) 他多数!
  • 11.  Haskellが使用されているプロジェクト › Pugs HaskellによるPerl6の実装 › darcs 分散バージョン管理システム › Whitespace タブ・空白・改行のみで記述する言語 › Monadius グラディウス風のゲーム › Mighttpd (発音:Mighty) HTTPサーバ
  • 12.  基本的な文法をいくつか覚える。 簡単なプログラムを書けるように なれれば素敵。
  • 13.  GHC (Glasgow Haskell Compiler) Haskellにおける事実上の標準コンパイラ。 実行が高速で、ライブラリ多数。 Hugs 軽量なコンパイラ、基礎を学ぶには十分。 導入も簡単だが開発は停止している模様。 Haskell Platform Haskell処理系のGHCに加えて 各種ツールやライブラリが揃っている。
  • 14.  UNIXであれば GHCか、可能であればHaskell Platformを。 Windowsであれば Haskell Platformが最適。 高度な事をするのであれば、 UNIXならGCC WindowsならMinGW などが必要。
  • 15.  今回はGHCに付属する › コンパイラのGHC › インタプリタのGHCI を使用します。 環境がある方は挑戦してみましょう。
  • 16.  まずはインタプリタから 実行してみます。
  • 17.  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. $
  • 18.  いくつか試してみましょう> 256256> 1+2*37> 7/23.5> 2^256
  • 19.  結合順位もしっかりあります> 8/4/21.0> 2^3^2512> (2^3)^264
  • 20.  :typeで型が見られます。(:t と略せる)> :type 256256 :: Num a => a> :t 1+21+2 :: Num a => a> :t 3.53.5 :: Fractional a => a> :t 7/2
  • 21.  関数は :info で見ると色々情報が出ます。 (:i と略せる)> :info (+)class (Eq a, Show a) => Num a where (+) :: a -> a -> a ... -- Defined in GHC.Numinfixl 6 +
  • 22.  Haskellでは型が非常に重要な要素です。 型が分かればHaskellを理解する 良い手がかりになります。 型の詳細は後ほど。
  • 23.  文字関係です。> „a‟„a‟> “Hello”Hello
  • 24.  リストを使ってみます。> [1,2,3][1,2,3]> head [1,2,3]1> tail [1,2,3][2,3]
  • 25.  リストは色々な操作が可能です。> [1,2,3,4,5] !! 23> 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]
  • 26.  長さも変更可能です。> 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]
  • 27.  : 演算子(cons)> 1:2[1,2]> 1:[2,3][1,2,3]
  • 28.  同じ型式でないといけません。 以下はエラーが発生します。> [1,„a‟]> [1,“Hello”]
  • 29.  ちなみに、文字と文字列の関係> [„a‟,„b‟,„c‟]“abc” 文字列は文字のリスト、ということです。
  • 30.  タプルを使ってみます。> (1,2)(1,2)> (1,2,3)(1,2,3)
  • 31.  長さを変えられない代わりに、 違う型でも使えます。> (1,‟a‟)(1,‟a‟)> (1,”Hello”)(1,”Hello”)> (1,a,"hello",[1,2,3])(1,a,"hello",[1,2,3])
  • 32.  タプルを使った関数。> fst (1,”hello”)1> snd (1,”hello”)”hello”
  • 33. Prelude> putStrLn “Hello, World!”Hello, World! Hello, World! と出力してくれる おなじみのサンプルです。
  • 34. putStrLn “Hello, World!” 関数を適用する場合は 空白文字を使います。
  • 35. Prelude> take 3 [1,2,3,4,5][1,2,3] 2引数の関数です。 リストから指定した数の要素を 取り出します。 2つ以上の引数に対して関数適用する場合も 空白文字を使います。
  • 36. Prelude> take 3 (tail [1,2,3,4,5])[2,3,4] 引数に関数を渡す場合は括弧を使います。
  • 37.  関数を定義して実行してみましょう。
  • 38. $ ghciPrelude> let main = putStrLn “Hello, World!” 等式の左辺が関数名、 右辺は関数の内容になります。 let 関数名=関数の内容 という形式です。
  • 39.  先ほど定義したmainを使用してみます。Prelude> mainHello, World! 実行出来ました。
  • 40.  先ほどはインタプリタで実行しました。 ならば今度は コンパイルして実行してみましょう。
  • 41.  以下の行をhello.hsというファイルに 保存します。main = putStrLn “Hello, World!” 関数名=関数の内容という形式です。 先程と同様に等式の左辺が関数名、 右辺は関数の内容になります。 こちらが一般的な書き方です。 先ほどのletはインタプリタ用と考えてください。
  • 42.  hello.hsをコマンドラインから コンパイルして実行してみます。$ ghc --make hello.hs$ ./helloHello, World!$ 実行できました。
  • 43.  実際にHaskellを扱うに当たって コードを記述して実行する方法は いくつかあります。 先程の例 › インタプリタ上で記述して実行 › ソースをコンパイルして実行
  • 44.  実際にHaskellを扱うに当たって コードを記述して実行する方法は いくつかあります。 その他 › ソースをインタプリタに渡して実行 › ソースをインタプリタから読み込んで実行
  • 45.  ソースをインタプリタに渡して実行$ ghci hello.hsOk, modules loaded: Main.Prelude Main> mainHello, World!Prelude Main> :q$
  • 46.  ソースをインタプリタから読み込んで実行$ ghciPrelude> :load hello.hsOk, modules loaded: Main.Prelude Main> mainHello, World!Prelude Main> :q$
  • 47.  コンパイルして実行する場合 › 高速に実行できる › 実行するにはmainを定義する必要がある インタプリタで実行する場合 › 便利な機能が沢山あるので 学習する時に非常に便利 › mainを定義しなくても良い
  • 48.  :reloadで再読み込みが出来る
  • 49.  先程のhello.hsを読み込む。> :load hello.hs> mainHello, World!
  • 50.  GHCIを起動したまま、 別ウィンドウでhello.hsを編集する。$ cat hello.hsmain = putStrLn “Hello, World!”$ #編集$ cat hello.hsmain = putStrLn “Hello, Haskell!”
  • 51.  GHCIのウィンドウに戻り、 再読み込み。> :load hello.hs> mainHello, World!> :reload
  • 52.  GHCIのウィンドウに戻り、 再読み込み。そして実行。> :load hello.hs> mainHello, World!> :reload> mainHello, Haskell!
  • 53.  インタプリタならではのやり方でした。
  • 54.  あとは :type や :info など。 調べるのに便利な機能が色々あります。
  • 55.  Haskellは型が重要。 型さえ覚えれば 第一の突破口クリア。
  • 56.  1引数の関数を見てみる headを見てみる> head [1,2,3]1> :t headhead :: [a] -> a
  • 57. head :: [a] -> a
  • 58. head :: [a] -> a関数名
  • 59. head :: [a] -> a これ以降は型を表す
  • 60. head :: [a] -> a 型
  • 61. head :: [a] -> a a型 任意の型
  • 62. head :: [a] -> a [a]型 任意のリスト型
  • 63. head :: [a] -> a [a] -> a型 [a]型からa型へと変換する型
  • 64. head :: [a] -> a [a] -> a型 [a]型からa型へと変換する関数
  • 65. head :: [a] -> a [a] -> a型 任意のリスト型から任意の型へと 変換する関数
  • 66. a型を具体的な型に置き換えるため、先程の例> head [1,2,3]1を当てはめてみる。
  • 67. head :: [a] -> a [a] -> a型 任意のリスト型から任意の型へと 変換する関数
  • 68. head :: [Num] -> Num [Num] -> Num型 数値のリスト型から数値型へと 変換する関数
  • 69. head :: [Num] -> Numheadは[Num] -> Num型数値のリスト型から数値型へと変換する関数
  • 70. head :: [Num] -> Numheadは[Num] -> Num型数値のリスト型から数値型へと変換する関数本当にそうなのか確認。
  • 71. > head [1,2,3]1
  • 72. > head [1,2,3] 数値のリスト型1数値型
  • 73. > head [1,2,3] 数値のリスト型1数値型数値のリスト型から数値型へと正しく変換されている。
  • 74.  1引数の関数を見てみる(2) sumを見てみる> sum [1,2,3,4,5]15> :t sumsum :: Num a => [a] -> a
  • 75.  型の読み方sum :: Num a => [a] -> a
  • 76.  型の読み方sum :: Num a => [a] -> a関数名
  • 77.  型の読み方sum :: Num a => [a] -> a sum関数の型
  • 78.  型の読み方sum :: Num a => [a] -> a [a]型をa型に変換する関数
  • 79.  型の読み方sum :: Num a => [a] -> a aを数値型とするクラス
  • 80.  型の読み方sum :: Num a => [a] -> a 数値のリスト型を 数値型に変換する関数
  • 81.  型の読み方sum :: Num a => [a] -> a 数値のリスト型を 数値型に変換する関数本当にそうなのか確認
  • 82. > sum [1,2,3,4,5]15
  • 83. > sum [1,2,3,4,5] 数値のリスト型15数値型数値のリスト型から数値型へ正しく変換されている
  • 84.  2引数の関数を見てみる(1) takeを見てみる> take 3 [1,2,3,4,5][1,2,3]> :t taketake :: Int -> [a] -> [a]
  • 85. take :: Int -> [a] -> [a]
  • 86. take :: Int -> [a] -> [a]関数名
  • 87. take :: Int -> [a] -> [a] 型
  • 88. take :: Int -> ([a] -> [a]) 右結合なので このように解釈する
  • 89. take :: Int -> ([a] -> [a]) [a]型を受け取り [a]型を返す関数
  • 90. take :: Int -> ([a] -> [a]) 関数A
  • 91. take :: Int -> ([a] -> [a]) Int型を受け取り 関数Aを返す関数
  • 92. take :: Int -> ([a] -> [a]) Int型を受け取り 関数A を返す関数
  • 93. take :: Int -> ([a] -> [a]) Int型を受け取り ([a]型を受け取り [a]型を返す関数) を返す関数
  • 94. take :: Int -> [a] -> [a] Int型を受け取り [a]型を受け取り [a]型を返す関数 を返す関数
  • 95. take :: Int -> [a] -> [a] Int型を受け取り [a]型を受け取り [a]型を返す関数 を返す関数言い換えると
  • 96. take :: Int -> [a] -> [a] Int型と[a]型を受け取り [a]型を返す関数という事です
  • 97.  非常に分かりづらいので 順を追って解読してみる。
  • 98.  takeにInt型の値(2)を渡してみるtake :: Int -> [a] -> [a] Int型を受け取り [a]型を受け取り [a]型を返す関数 を返す関数
  • 99.  takeにInt型の値(2)を渡してみる(take 2) :: Int -> [a] -> [a] Int型を受け取り [a]型を受け取り [a]型を返す関数 を返す関数
  • 100.  takeにInt型の値(2)を渡してみる(take 2) :: Int -> [a] -> [a] Int型を受け取り [a]型を受け取り [a]型を返す関数 を返す関数
  • 101.  takeにInt型の値(2)を渡してみる(take 2) :: [a] -> [a] [a]型を受け取り [a]型を返す関数
  • 102.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2) :: [a] -> [a] [a]型を受け取り [a]型を返す関数
  • 103.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) :: [a] -> [a] [a]型を受け取り [a]型を返す関数
  • 104.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) :: [a] -> [a] [a]型を受け取り [a]型を返す関数
  • 105.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) :: [a] [a]型を返す関数
  • 106.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) = [4,5] [a]型を返す関数
  • 107.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) = [4,5] [4,5]を返す関数
  • 108.  (take 2)に[a]型の値([4,5,6])を渡してみる(take 2 [4,5,6]) = [4,5]結果 = [4,5]本当にそうなのか確認
  • 109. > take 2 [4,5,6][4,5]
  • 110. > take 2 [4,5,6] Int型[4,5]
  • 111. > take 2 [4,5,6] Int型 [a]型[4,5]
  • 112. > take 2 [4,5,6] Int型 [a]型[4,5][a]型
  • 113. > take 2 [4,5,6] Int型 [a]型[4,5][a]型どうやら本当になったようです。
  • 114.  実際にGHCIでも確認してみましょう。
  • 115. > :t taketake :: Int -> [a] -> [a]> :t take 2take 2 :: [a] -> [a]> :t take 2 [4,5,6]take 2 [4,5,6] :: Num a => [a]> take 2 [4,5,6][4,5]
  • 116.  確かにそのように動作していました。
  • 117.  少々かみ砕きすぎたでしょうか。 余計難しくなったかも知れません。 一言で説明しても順を追って説明しても 難解な概念ですが、何度かソースを書いて 試して見ると、急にすっと分かるように なります。
  • 118.  型を意識して作る必要があります。 型を考えるという事は 入力の形と出力の形を考える事、 そして関数の入出力と処理内容を 切り分ける事へと繋がります。 型の概念は難しい部分がありますが、 型が分かるようになると 難しい事を考えなくても プログラムが書けるようになります。
  • 119.  全体的に駆け足で説明する事に なってしまいました。 Haskellが分かるようになると いつの間にか面白いと思うようになります。 ありがとうございました。