スタートHaskell2 #3 に参加してきました
Long long ago... そう、それは先週の日曜日の話でした……
第3回スタートHaskell2に参加してきました。
くわしく
- 第6章
- 資料はこちらにあるようです。 http://www.slideshare.net/aomoriringo/haskell-6
- import Foo () は何の役に立つのか。何もインポートされないじゃないですか。
- 何もインポートされない訳ではなく、「インスタンス宣言だけ」がインポートされる。そういう使い方をしたいときにどうぞ
- Haskell のChar はUnicode で処理される
- つまり1文字4Byte。C のように1Byte ではない。
- 比較的リッチなデータ構造なので低速。
- C言語ライクなChar を使いたい場合は、Data.Word8 を使うと良い。
- やっぱり(!)foldl はいらない子
- 基本的に、正格なfoldl' 使えば良い。
- とは言うものの、foldl' に遅延評価を要求するような関数を渡すとオーバーフローしてしまう
- なので遅延評価しない書き方で関数を渡してやる必要がある
- Map.map について
- Map: 要素が重複しない辞書的なもののこと
- map: 写像する
- 同じスペルであれど、両者は全くの別モノなので理解しておくと良いかも。
- import qualified Data.Map as Map
- リストライクなモジュールをインポートする時にはこのような書き方をする
- なぜか: 関数名が重複してしまうから。
- 型名は被らないようにつくられている(はず)だから、まずは型だけをインポートして、その後にqualified なりas なりすると良い。
- 第7章
- 資料はこちらにあるようです。 http://www.slideshare.net/a-hisame/start-haskell7
- なるべくエクスポートリストは書いた方が良い
- エクスポートリストを書くと、どれをエクスポートしてどれをエクスポートしないかをコンパイラに伝えられる
- その結果、GHC が最適化してくれるから高速になる
- 抽象データ型としてエクスポートすると?
- 型の名前は公開するけど、データコンストラクタは公開しない(直接触らせない)
- パターンマッチに引っかからなくなる
- 内部実装をこっそり変えても影響が出にくくなるからハッピー
- 全てのソースは"module" から始めるべき。そっちの方がお行儀が良い
- ファイル名とモジュール名は一致していなければならない
- 但し、モジュール名Main だけはファイル名とモジュール名が一致していなくても良い
- 言語仕様的に、"module" が省略された時はモジュール名が強制的にMain になる
- 各ファイル(モジュール)でたくさんimport を書くのは面倒臭い。import.hs みたいなファイルで必要なものを一括インポートして、各ファイルでimport.hs をインポートすれば楽じゃない?
- そのやりかたはOK
- けれども、1つのファイルに大量のimport 文が存在しているのは何かあやしい。分割等した方が良いんじゃ?
- パッケージ単位で「エクスポートしたい、したくない」のようなアクセス制限は出来るのか
- パッケージ内に何個かモジュールがあって、それらをエクスポートするかしないかの記述はCabal 5 に書く
- レコード構文のフィールドを秘匿し、ヘルパ関数を提供する事により値を生成する事は可能か
- できる
- また、差分だけを記述して別の型(たとえばPerson型)を提供することもできる
- その場合、デフォルトのPerson型を提供しておく必要がある
- 値コンストラクタと型コンストラクタの違い
- Either 型で、Left が失敗でRight が成功と決められているのはなぜか
- Either 型はBool のなれの果て
- Bool --> Maybe --> Either というふうに出世している
- Bool : False | True
- Maybe : Nothing | Maybe a
- Either: Left a | Right a
- というように、これらの系譜は全て左側が失敗している
- データを宣言する時に、値の中身まで制約することは可能か? (例えば非負の値のみ受理する、のような)
- 基本的に無理。依存型になってしまうから。
- Haskell でInvalid な値(制約条件を満たさない値)が渡されたときはどうするの?
- Data.Map にはその昔バグがあった。
- Delete()時にバランスが取れなくなって低速になる事が。10年くらい放置されていた
- データコンストラクタが複数ある時に、レコード構文を利用する場合は制約がある
- 引数の個数は異なっても良いが、型・フィールドラベルは同じ順序でなければならない
- Haskell の正規表現ライブラリ
- 再起の鳥瞰図
- 資料はこちらにあるようです。 http://mew.org/~kazu/material/2012-recursion.pdf
- 末尾再起
- 場合分けの末端で自分自身を呼び出す
- Haskellだと'='のすぐ右が自分自身
- 一般的な再起
- 場合分けの末端で他の関数を呼び出す
- 末尾再起とは、すなわちループである
- ループで記述可能なものは末尾再起に書き換え可能である。逆も然り。
- 末尾再起はスタックを消費しない
- 正格評価でも末尾再起呼び出しはジャンプに変換する事が出来る。
- コンパイラがサポートしていれば、スタックは消費されない
- 正格評価ではなく、遅延評価の場合はいつでもジャンプに変換する事ができる
- ループで記述可能なコードなのに、一般的な再起で記述されている場合
- 引数を増やしてやる事によって末尾再起に書き換える事ができる
- 末尾再起では書けない一般的な再起 ==> 末尾再起よりも強力(なはず)
- 再起の考え方
- 「ひとつ前のステップができているとすれば、次はどうするか」
- Haskell における力の強さ
- 一般的な再起、末尾再起 : 強い
- 畳み込み : やや強い
- 単純な高階関数(map, filter, etc...): 弱い
- なるべく力の弱いものを使う
- 力の強いものは何でも出来てしまうがゆえに危険
- MapReduce でやっつけましょう
演習問題
こちらにあるようです https://github.com/yuzutechnology/Community-StartHaskell2011/tree/master/exercises/recursion今回は色々と多いので省略と言うことで。(気が向いたら上げるかもわかりませんが)