Haskellはあなたのアプローチをスケッチするユニークな方法を提供します。あなたが知っていることから始めましょう
module Main where
type Rating = (String, Int)
type Film = (String, String, Int, [Rating])
main :: IO ()
main = do
films <- readFilms "ratings.dat"
print films
このプログラムをghciにロードしようとすると、
Films.hs:8:12:範囲外: `readFilms '
それは何であるかを知る必要がreadFilms
あるので、動き続けるのにちょうど十分なコードを追加します。
readFilms = undefined
これは、データに関連する何かを実行する必要がある関数ですFilm
。このコードを(:reload
コマンドまたは:r
略して)リロードして、
Films.hs:9:3:
制約内のあいまいな型変数`a0':
(a0を表示)`print'の使用から生じる
..。
タイプprint
は
プレリュード>:t print
print :: Show a => a-> IO()
言い換えるprint
と、非公式に、それ自体を表示する方法(つまり、その内容を文字列に変換する方法)を知っている単一の引数を取り、実行時にその文字列を出力するI/Oアクションを作成します。それは多かれ少なかれあなたがprint
働くことを期待する方法です:
プレリュード>プリント3
3
プレリュード>「こんにちは」を印刷
"こんにちは"
print
ファイルからデータを取得したいことはわかってFilm
いますが、ghcは私たちの心を読み取ることができません。しかし、タイプヒントを追加した後
readFilms :: FilePath -> Film
readFilms = undefined
新しいエラーが発生します。
Films.hs:8:12:
期待されるタイプ`IOt0'と一致しませんでした
実際の型は`(String、String、Int、[Rating]) '
予想されるタイプ:IO t0
実際のタイプ:フィルム
`readFilms'の呼び出しのリターンタイプ
'do'式のstmt:films <-readFilms "ratings.dat"
このエラーは、コンパイラがストーリーについて混乱していることを示しています。あなたはreadFilms
それを返す必要があると言いましたFilm
が、あなたがそれを呼んだようにmain
、コンピュータは最初にいくつかのI / Oを実行し、次にデータを返す必要がありFilm
ます。
Haskellでは、これは純粋な文字列、たとえば"JamieB"
、と副作用、たとえばStackOverflowのユーザー名の入力を求められた後にキーボードから入力を読み取ることの違いです。
これで、次のようにスケッチできることがわかりreadFilms
ました。
readFilms :: FilePath -> IO Film
readFilms = undefined
そしてコードがコンパイルされます!(ただし、まだ実行できません。)
別のレイヤーを掘り下げるには、単一の映画の名前が唯一のデータであると偽ってratings.dat
、タイプチェッカーを満足させるために他のすべての場所にプレースホルダーを配置します。
readFilms :: FilePath -> IO Film
readFilms path = do
alldata <- readFile path
return (alldata, "", 0, [])
main
このバージョンはコンパイルされ、ghciプロンプトで入力して実行することもできます。
dave4420の答えには、使用する他の関数に関する優れたヒントがあります。上記の方法は、個々のピースが機能するジグソーパズルを組み立てる方法と考えてください。プログラムを正しくするには、すべてのタイプが一致している必要があります。上記のように少し手を加えることで、最終的な作業プログラムに進むことができます。タイプチェッカーは、スケッチに誤りがあるかどうかを通知します。
理解すべきこと:
- 入力のブロブ全体を個々の行にどのように変換しますか?
- あなたのプログラムが調べているラインがタイトル、ディレクターなどであるかどうかをどのように判断しますか?
String
ファイル(a )の年を、Int
の定義に協力するためにどのように変換しますFilm
か?
- 空白行または空行をスキップするにはどうすればよいですか?
- データのリストをどのように
readFilms
蓄積して返すのFilm
ですか?