Start Haskell2 に行ってきました
すごいHaskellたのしく学ぼう!の読書会としてStart Haskell2に行ってきました。
- 作者: Miran Lipovača,田中英行,村主崇行
- 出版社/メーカー: オーム社
- 発売日: 2012/05/23
- メディア: 単行本(ソフトカバー)
- 購入: 25人 クリック: 580回
- この商品を含むブログ (73件) を見る
0. Introduction
http://www.slideshare.net/YasuyukiOgawa/ss-13433189
Haskellとは?
Haskellには型クラスがある
(+) :: Int -> Int -> Int と (+) :: Float -> Float -> Float に対して、
同じ+記号を使いたいけど、型はどうしたらよいかという問題に対して、型クラスが提案された。
この場合は、IntやFloatのように、足し算できる型の集合をNumクラスとする。
Javaで言えばインタフェースのような概念だが、違いは追々分かっていくらしい(`・ω・´)。
Numクラスを用いた(+)の定義は次の通り。
(+) :: Num a => a -> a -> a
環境とか
GHC: Glasgow Haskell Compiler (G is not GNU)
Haskellは仕様、GHCは実装の1つ。
Haskell Platform : GHCとよく使われるライブラリをセットにしたもの。
各OS用に合わせたGHCはHaskell Platformを入れる事で使う事ができる。
ちなみに、単にGHCを入れて動作を動かそうとDLページに行くと、公式にSTOPがかかる(参考)。
他の環境として、Hugs*1があるが、今はメンテされていないので忘れていいらしい(´・ω・`)。
他の拡張環境もGHCをベースとしたものが多い。
また、Haskell' (Haskell Prime)は理想に走りすぎたので忘れていいらしい(´・ω・`)。
1.はじめの第一歩
VMwareに入れていた仮想環境が壊れていたので実施できず……(´・ω・`)
他の言語とちょっと違うHaskellの書き方について。
-- 真偽値の先頭は大文字。 True False -- 関数に渡す引数はスペース区切りで(,)じゃないよ。 min 1 2 -- min(1, 2) ;; NG -- 負数の場合は()を付ける。(構文解析の制約上) min (-1) (-2) -- コンパイルエラー(文字列と0なんてそもそも比較できないよね!) "" == 0 -- これはTrueになる(共通の型クラス、理由は後述) 1 == 1.0 -- 関数を中置で使う場合はバッククォート(`)を利用する。 10 `div` 2 -- div 10 2 と同じ -- Haskellの文字列は文字のリスト "hoge" == ['h', 'o', 'g', 'e'] "hoge" == 'h' : 'o' : 'g' : 'e' : [] -- リスト操作関数色々 head [1,2,3] -- 1 tail [1,2,3] -- [2,3] take 2 [1,2,3] -- [1,2] elem 3 [1,2,3] -- True elem 4 [1,2,3] -- False -- リスト内容表記 [ x | x <- [1 .. 10], x `mod` 2 == 0] -- [2,4,6,8,10] [ x*2 | x <- [1 .. 10], x `mod` 2 == 0 ] -- [4,8,12,16,20] -- bit演算 import Data.Bits 15 .&. 9 -- 9 15 .|. 9 -- 15 15 `xor` 9 -- 6
演習問題
http://wiki.haskell.jp/Workshop/StartHaskell/LYHGG/exercise/1
解決のヒント
次の違いがある。(厳密な型は異なるが、今回の問題の範囲ではこの理解で十分)
- (/) :: Float -> Float -> Float
- div :: Int -> Int -> Int
レンジ(範囲指定生成)で減算を行う場合は2個目を指定する。
- ['z', 'y' ... 'a']
問題6: 1から10までの全ての整数で割り切れる最少の正の整数を求めよ。
-- ここまでの知識のみの解法1 let res6 = head [x | x <- [1 .. ], x `mod` 2 == 0, x `mod` 3 == 0, x `mod` 4 == 0, x `mod` 5 == 0, x `mod` 6 == 0, x `mod` 7 == 0, x `mod` 8 == 0, x `mod` 9 == 0, x `mod` 10 == 0] -- ここまでの知識のみの解法2 let res6 = head [x | x <- [1 .. ], length [c | c <- [1..10], mod x c == 0] == 10] -- all関数を利用したもの。解法1をスマートに書くとこうなる。 let res6 = head [x | x <- [1 .. ], all (\y -> x `mod` y == 0) [1..10]]
問題7: ピタゴラス数の組(a,b,c)(ただし、a
let res7 = head [ (a, b, c) | a <- [1..1000], b <- [a+1..1000], c <- [b+1..1000], a+b+c == 1000, a*a+b*b == c*c]
ただし、a,bが決まればcも決まるので(==1000制約から)、それを利用して2重ループ扱いとするのがよい。
あとは、上記の場合は制約a+b+c == 1000条件に当てはまらないものを先に書いた方が効率が良い。
head [(a,b,1000-a-b) | a <- [1..998], b <- [a+1..999], a^2+b^2 == (1000-a-b)^2 ]
2.型を信じろ
http://www.slideshare.net/skoji/haskell2-13424552
型を調べたり、たくさんある組み込みの型の紹介。
ここでのa, bは何の型でもよい。Generics的だがもっと強力!
- 多相型・型クラス制約。 (==) :: (Eq a) => a -> a -> Bool
aは何でもよいが、Eq型クラスのインスタンスである必要があるという制約がつく。
型クラスの例
- Eq型クラス:等価性判定ができる型のクラス
- Ord型クラス:順序付けができる型のクラス
- Show型クラス:文字列として表現できる型のクラス。show関数で文字列に変換可能。
- Read型クラス:読み取り、文字列に変換できる型のクラス
- Enum型クラス*2:順番に並んだ型のクラス。より具体的にはsuccで次の値が取得できる型のクラス。
- Bounded型クラス:上限と下限を持つ型のクラス。maxBound/minBound関数で上下限がそれぞれ取得できる。
- Num型クラス:数のように振る舞う型のクラス*3
- Floating型クラス:浮動小数点の型のクラス
- Integral型クラス:整数全体の型のクラス
型注釈
実際の型があいまいで一意に推論できない場合などに型を指定できる。
(5 :: Int) のように、値の後ろに":: 型名"を記述することで利用できる。
Haskellでは自由に2項演算子を自由に定義できる(=Haskellでは2引数関数は2項演算子と等価)
型クラスによって、複数の型で同様の記号・関数名を利用できる。
コンパイラによるリテラルの解釈
Haskellでは、2 + 2.5の式はコンパイルエラーにならない。
これは一見、Int + Floatの足し算を行うようにみえるが、これは以下の型クラスの仕組みにより解決される。
(ただ、いまいち理解が錯綜したので、間違えている可能性は高いです。)
2とだけ直接プログラム上に記述した場合、それはNum型クラスのインスタンスである。
重要なのは、2と書いた段階でIntに型が確定するわけではないことである。
2も2.5も、それを書くだけではNum型クラスのインスタンスとみなされる。
そのため、この時点で2 + 2.5はNum aを返す式として解釈され、それ以上の評価は行われない。
(のが、GHCのデフォルトの挙動っぽい。そして、この挙動をある程度自由に変化させられるらしい?)
一方で、これを表示する場合には、具体的な計算結果を評価する必要がある。
この時に初めて、具体的に式の結果が最終的にはどの型になるか(型クラスから型になるか)を決定する必要がある。
(補足:型クラスは具体的な型ではないため、このままでは結果となる値が属する最終的な型が決定できない。)
また、(+) :: (Num a) => a -> a -> a であり、式を解析する時点で初めてNum型の変数が(具体的に)どの型であるかの推論が行われる。
そのため、この式を解決するためには、aはFloat(あるいは、2.5を解決できる)Num型クラスのインスタンスである事が求められる。
ここで1からIntで解釈を行おうとすると、2.5が解決できないため、2.5が解決できるFractional aが適用され、
結果として、(+) :: Fractional a => a -> a -> a を今回の場合には適用することを決定する。
この後、Fractional型クラスのインスタンス変換時にHaskellのdefaultingルールによりDoubleに変換される。
それゆえ、2 + 2.5は解決でき、結果として2.0*4 + 2.5と(結果的に)等価となる。
また、この仕組みにより、1 == 1.0も同様にTrueとなる(リテラル1がFloat型と解釈された結果と1.0 :: Doubleを比較するため)。
演習問題
http://wiki.haskell.jp/Workshop/StartHaskell/LYHGG/exercise/2
型クラスが入る場合の記述
-- 関数名 :: (型クラス制約) => [関数定義] -- show関数 :: Show型クラスのインスタンスを受け取りStringを返す関数 show :: Show a => a -> String
すごいHaskellのテストたのしく学ぼう(LT)
スライド: http://www.slideshare.net/ShokoSasaki/haskell-13433770
GitHub: https://github.com/shokos/Haskell_test_tut
Cabalで「cabal install HUnit」を実施することでHUnitを入手してから。
こんな感じで、「assert関数、メッセージ、期待値(テストが成功となる値)、テスト内容」のリストを作成する。
-- リンク先(https://github.com/shokos/Haskell_test_tut)より引用 --assertの配列 caliculateTests :: [Test] caliculateTests = map TestCase [ assertEqual "plus 1 2" 3 (plus 1 2) ,assertEqual "minus 5 3" 2 (minus 5 3) ]
Haskellを勉強するために (LT)
- Hoogle
もはやサンプルと関数名と型で何がやりたいかが大体分かるレベル。
- Project Eular/Codeforces/At Coder
実際にHaskellを使って問題を解こう!
- パーサコンビネータを書こう!
google: 48時間でschemeを書こう で検索をして実際に
- Webフレームワーク: Yesodを使おう!
すごいYesodたのしく学ぼう! (LT)
(注:Haskell Day 2012 のスライドと同じ)
http://www.slideshare.net/ssuser6c06ba/20120527yesod
コーディングしている分には、他の言語でWebサイトを書くのとあまり変わらない。
コンパイル時の型チェック(アプリ内リソースに対してリンク切れが無い)とか、各セキュリティ対策がデフォルトで効いているとか。
リンク
Togetterまとめ - 第1回 スタートHaskell2 - http://togetter.com/li/326480
最後に
一回目から非常に熱い議論(特にNum型に対する)が行われましたこともあり、今後どのようになっていくかが楽しみです。
自分の中での3度目のStartをしたので、IOに包まれて汚れた経験者になって帰るまで是非続けたいと思います。