前回紹介したヒストグラムを作るためのモジュール Histogram.hs は [Int] 型のリストを扱うものだが、[Double] 型のリストを使って実数も扱えるようにしてみた。ヒストグラムの計算など [Int] 型のリストと [Double] 型のリストに共通する操作を histogram と histogramR のように別々の関数にせず、同じ histogram という関数にしたかったので色々と調べてみたが、結局、型クラス制約 (type class constraint) を使うのが良いというのがわかった。
型クラス制約とは何かということを説明するより、実際のソースをみた方が分かりやすいので、実数の計算ができるようになった Histogram.hs を次に示す。 module Histogram where toint :: [String] -> [Int] toint = map (read :: String->Int) . (filter (/="")) toreal :: [String] -> [Double] toreal = map (read :: String->Double) . (filter (/="")) fromFile :: String -> IO [Int] fromFile fname = readFile fname >>= return . toint . lines fromFileR :: String -> IO [Double] fromFileR fname = readFile fname >>= return . toreal . lines toFile :: (Num a, Show a) => String -> [a] -> IO () toFile fname = (writeFile fname) . unlines . (map show) histogramM :: (Num a, Ord a) => [a] -> [a] -> IO [Int] histogramM bin = return . (histogram bin) count :: (Num a,Ord a) => (a, a) -> [a] -> Int count (lo, hi) = length . filter (\x -> (lo<=x) && (x<hi)) bins :: (Num a) => [a] -> [(a, a)] bins lst = zip lst (tail lst) histogram :: (Num a, Ord a) => [a] -> [a] -> [Int] histogram bin lst = map ((flip count) lst) $ bins bin このプログラムは ghci から load して使う。 Prelude> :l Histogram.hs [1 of 1] Compiling Histogram ( Histogram.hs, interpreted ) Ok, modules loaded: Histogram. *Histogram> ヒストグラムの作り方はたとえば、data.txt というファイルのデータを [Int] 型のリストとして読み込んで、[1..6] の範囲のヒストグラムを作るには次のようにする。 *Histogram> fromFile "data.txt" [1,2,3,4,5] *Histogram> fromFile "data.txt" >>= histogramM [1..6] [1,1,1,1,1] さらに、改訂版の Histogram モジュールではファイルのデータを実数型のリストとして読み込み、実数のリストでヒストグラムを作ることができる。 *Histogram> fromFileR "data.txt" [1.0,2.0,3.0,4.0,5.0] *Histogram> fromFileR "data.txt" >>= histogramM [0.5,1..5.5] [0,1,0,1,0,1,0,1,0,1] また、計算結果をファイルに書き出すには toFile 関数を使う。 *Histogram> fromFileR "data.txt" >>= histogramM [0.5,1..5.5] >>= toFile "out.txt" *Histogram> fromFile "out.txt" [0,1,0,1,0,1,0,1,0,1] toFile 関数は [Int] 型のリストの書き出しだけでなく、[Double] 型の実数のリストも書き出せる。 *Histogram> fromFile "data.txt" >>= toFile "out.txt" *Histogram> fromFileR "out.txt" [1.0,2.0,3.0,4.0,5.0] ところで、上のソースのどの部分が型クラス制約なのかというと、たとえば bins 関数の型シグネチャーの bins :: (Num a) => [a] -> [(a, a)] がそれだ、(Num a) => は型変数 a が Num クラスのインスタンスであることを指定している。しかし、このような型の指定の仕方をすると、Num クラスのインスタンスの Int 型や Double 型のデータのどちらにも bin 関数が使える。 *Histogram> bins [1..5] [(1,2),(2,3),(3,4),(4,5)] *Histogram> bins [1,1.5..3] [(1.0,1.5),(1.5,2.0),(2.0,2.5),(2.5,3.0)] (Num a) => というのは何か暗号的で理解できにくく見えるが、使ってみるとこんな便利なものはないことがわかった。ポイントは自分の定義したい関数にどんなデータ型を使いたいか、またそれらのクラスのどんな関数を使いたいかということだ。たとえば上のソースの count という関数の型シグネチャーは次のようになる。 count :: (Num a,Ord a) => (a, a) -> [a] -> Int count (lo, hi) = length . filter (\x -> (lo<=x) && (x<hi)) count は数値型 (Num 型) のデータを扱いたいので Num a という型クラス制約を利用する。また <= や < という比較演算子を使うためには Ord a という型クラス制約が必要だ。使ってみると、簡単な理屈だ。しかし、このおかげで整数型でも実数型でも同じ count という関数で処理することができる。 型クラス制約 type class constraint はおすすめだ。
by tnomura9
| 2015-10-08 23:03
| Haskell
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||