2009年1月25日日曜日

Haskell の read 関数で、文字列から代数的データ型へ変換 - 導出インスタンスを使って

1. 文字列から代数的データ型へ変換したい

Haskell の print 関数で日本語を出力するために、以下のコードを参考にした。

上記には、

「文字列から、代数的データ型へ変換するための、Read クラスの導出インスタンスの使い方」

についても、わかりやすく書かれていた。忘れないうちに、練習しておこう。o(^^)o

 

2. Show クラスの使い方はわかってきた

ところで、Read の反対 Show クラスについては、少しわかるようになった。

しかし、Read についてはチンプカンプンだった。 (+_+)

 

3. Read クラスについて

文字列を、特定の型へ変換する例を、最初に書いたのは以下の記事。

この記事を書いたときは、まだ、

「返り値の型を指定する関数」

について理解してなかった。そういう書き方をするものだ、と疑問を持たずに写経。

後になって、腑に落ちなかったので、少し調べた。

 

read 関数の基本

基本となる要点を復習しておく。

例えば、文字列 “100” を読み込んで、数値 200 と足し合わせる場合、

*Main> (read "100" :: Int) + 200
300

read 関数の返り値の型を Int と指定する必要がある。

read の関数の型は、

read :: Read a => String –> a

(Prelude より)

read 関数の、型変数 a は Read クラスのインスタンスだという制約がある。

Int 型は Read クラスのインスタンスとなので、返り値を Int で型付けてもエラーとならない。

090125-002

上記では、返り値を Int と型を指定した。

別の書き方として、read 関数の型を指定することもできる。

*Main> (read :: String -> Int) "100" + 200
300

 

型推論が行われる場合、型を明示的に書く必要はない

ただし、この場合 + 200 と文脈があるので型推論が行われ、

*Main> read "100" + 200
300

のように返り値の型を明示的に指定しなくても大丈夫。

 

実装について

ちなみに実装は、GHC.Read モジュールの中で instance Read Char と定義されていた。

 

Read, Show のドキュメントを読んでも理解できなかった

しかし、これだけでは The Haskell 98 Language Report

を読んでも相変わらず理解できず… (@_@;)

 

4. Read クラスの導出インスタンスを使う

そこで最初に参考にしたコードを真似て、Read の導出インスタンスを使う方法を試してみる。データは、前回と同じく Person 型の値とする。

以下の内容で UTF-8 で保存。(person.txt)

Person {name = "太郎", age = 20}
Person {name = "次郎", age = 30}

 

フィールドラベルで値を構築

その前に、「フィールドラベルを使って値を構築できるんだっけ?」と疑問を感じたので、調べたら、

ラベルの付いたフィールドをもつ構築子は構成要素が位置ではなく名前で 指定される値を構成するのに使用できる。宣言リストで用いるブレースとは 違い、これらはレイアウトの支配を受けない。

(3.15.2 フィールドラベルを用いた構築 より)

知らなかった。 ^^;

 

導出インスタンスを使って定義

一行ごとに、文字列から Person 型へと変換を行うことにする。person.hs の記述は、以下の通り。

import qualified System.IO.UTF8 as U

data Person = Person {name :: String,
                      age  :: Int} deriving (Read)

instance Show Person where
    show (Person name age) = name ++ " " ++ show age

main = U.getContents >>= U.print . map (read :: String -> Person) . lines

コマンドラインより、

runghc person.hs < person.txt > result.txt

結果は、

[太郎 20,次郎 30]

次は自前で実装する方法を調べよう~。 CCC

追記(2011.11.26) : コンマ区切りのデータを、一度タプルに変換した後、特定の型へ変換する方法は、以下を参照。