2012-03-17 6 views
2

私はHaskellを学んでいるだけで、単にファイルを読み込んで、lines関数を使って行のリストを作成するコードを書こうとしています。ファイルI/Oが出力を生成しないときの問題

import System.IO 
import Control.Monad 

main = do 
     let list = [] 
     handle <- openFile "data.txt" ReadMode 
     contents <- hGetContents handle 
     let myLines = lines contents 
      list = listLines myLines 
     print list 
     hClose handle 

listLines :: [String] -> [String] 
listLines = map read 
:ここ
this is line one 
another line 
and the final line 

私はリストにこのデータを読み込み、画面にそれを印刷するために使用しようとしていますコードです:たとえば、私は次の行が含まれてい data.txtと呼ばれるファイルを持っています

結果のコードはコンパイルされますが、出力は生成されません。

私のコードに何が間違っているのか理解できる人はいますか?ありがとう。

+2

'listLines'は何をすると思いますか? – Vitus

+0

ところで、 'String→String'バージョンの' read'は、Haskell文字列フォーマット( '' Hello \\ "world \\" ''のような生の文字列)を期待し、それを '' Hello "world" ''に変換します。あなたがファイルから得たものを印刷したいだけなら、 'list = listLines myLines'を完全に削除してください。 – Vitus

+0

'listLines'は文字列のリストを取り、文字列のリストを返すと期待しています。うーん、私は生の文字列が私のコードと関係があるのか​​どうか本当に理解していないのですか?もっと説明できますか? – turtle

答えて

8

お気づきのように、エラーメッセージにはreadの問題があるので、それに焦点を当てましょう。

私がコメントで述べたように、readは、あるデータ型の値の文字列表現を取り、それを解析してその値を返します。いくつかの例:

read "3.14" :: Double ≡ 3.14 :: Double 
read "'a'"  :: Char ≡ 'a'  :: Char 
read "[1,2,3]" :: [Int] ≡ [1,2,3] :: [Int] 

一部nonexamples:

read "[1,2," :: [Int] ≡ error "*** Exception: Prelude.read: no parse" 
read "abc" :: Int ≡ error "*** Exception: Prelude.read: no parse" 

あなたがreadStringバージョン(すなわちread :: String → String)を使用しようとするとどうなりますか?

ハスケルのStringの表現(例:GHCiでStringを返すものを評価する場合)は、" ... "の引用符で囲まれた一連の文字で構成されます。もちろん、の表示を(改行のような)いくつかの特殊文字にしたい場合、そこにエスケープされたバージョン(この場合は\n)を入れなければなりません。

readは値の文字列表現を期待していると書いていますか?あなたの場合、readと正確にはの文字列形式を想定しています。当然、最初にしようとするのはオープニングの見積もりと一致させることです。最初の行"で始まるでないので、readはプログラムに文句を付けてクラッシュします。

read "hello" :: Stringは、read "1" :: [Int]が失敗するのと同じ方法で失敗します。 1だけでは、Intのリストとして解析できません。readは、括弧[を開始して文字列を開始すると想定しています。


また逆である、showの聞いたことがあるかもしれない(しかし非常に緩い意味で)readへ。目安として、readの値をxに設定すると、readの文字列表現はshow xのようになります。


あなたはあなたのコードがうまく動作し、次の入力生み出す

"this is line one" 
"another line" 
"and the final line" 

次へファイルの内容を変更した場合:あなたがしたくない場合は

["this is line one","another line","and the final line"] 

.txtファイルを変更するには、list = listLines myLinesを削除し、print myLinesを削除してください。ただし、プログラムを実行すると、再度

["this is line one","another line","and the final line"] 

が返されます。だから何が問題なの?

print = putStrLn ∘ showshowのデフォルトの動作それは何か(一部aため、すなわち[a]; Charを除いては、特別な治療を受ける)のリストをINGのshowに来る文字列[ firstElement , secondElement ... lastElement ]を生成することです。ご覧のとおり、[ ... ]を避けたい場合は、[String]をまとめてマージする必要があります。

linesの逆数であるunlinesという素晴らしい機能があります。また、printは最初にshowと呼ばれますが、この場合は望んでいません。だから私たちはputStrLnを使用しています。最終バージョン:我々はまた、不要なlines ~ unlinesだけputStrLn contentsを取り除くことができ

main = do 
    handle <- openFile "data.txt" ReadMode 
    contents <- hGetContents handle 
    let myLines = lines contents 
    putStrLn (unlines myLines) 
    hClose handle 

+2

ああ、あなたが楽しみにしていることがあるので、これはワンライナーとして書くことができます: 'readFile" data.txt ">> = putStrLn'。 – Vitus

2

hGetContents handleは、ハンドルの内容を文字列に読み込みます。この文字列を行のリストに変換するためには、実際には改行で分割する必要があります。あなたはreadに対処する必要はありません - とにかく文字列がほしいだけです。だから、lines :: String -> [String]が必要です。さらに、あなたのコードは、それを読み込む前にlistを「初期化」することによって、あなたが「必然的に考えている」ことを示しているようです。

main = do 
    handle <- openFile "data.txt" ReadMode 
    contents <- hGetContents handle 
    let list = lines contents 
    print list 
    hClose handle 

あなたのプログラムを簡単にする方法はいくつかあります。実際に他のもののためにファイルへのハンドルが必要ない場合は、単にreadFile :: FilePath -> IO Stringを呼び出すだけで、ファイル内のすべてのテキストを簡単に取得できます。あなたが実際の名前にバインドされたlistを必要としない場合、あなたはここで何が起こるかおさらいするに

main = do 
    contents <- readFile "data.txt" 
    print (lines contents) 

にあなたのプログラムを削減するだけでなくその取り除くことができます:「data.txtを」が読み込まれ、その全体コンテンツはcontents :: Stringとして利用可能です。この文字列を各行に1つずつ、つまり正確にはlines contentsの文字列のリストに変換します。

+1

これは彼が彼のコードで持っている問題に答えることができないことを覚えておいてください。 – Vitus

+0

ああ、申し訳ありませんが、私は間違ったはずです。うまくいけば、彼はそれから有用なものをまだ抽出することができるので、私はそれをぶら下げておきます。 (編集:うん、私はそれが誤解について読むことを参照してください。ああ...それは共通の礼儀である場合は、私は答えを削除します) – gspr

+2

それを行う必要はありません。そこにはかなり有益なことがあります。ハスケラーは素敵な人たちです。そのためにあなたを落胆させません(私が願っています:)。 – Vitus

関連する問題