1

再び私です:)。5で割り切れる数の行を別のファイルにコピーするプログラムを書こうとしています。コードは次のとおりです(ポーランド語の名前は申し訳ありません)。

import IO

przepiszConHelper :: Handle -> Handle -> Integer -> Integer -> IO ()
przepiszConHelper wejscie wyjscie liczba licznik = do
    eof <- hIsEOF wejscie
    if eof then return ()
        else
            linia <- hGetLine wejscie
            if (mod licznik liczba) == 0 then
                hPutStrLn wyjscie linia
            przepiszConHelper wejscie wyjscie liczba (licznik + 1)

przepiszCon :: String -> String -> Integer -> IO ()
przepiszCon wejscie wyjscie liczba = do
    wej <- openFile wejscie ReadMode
    wyj <- openFile wyjscie WriteMode
    przepiszConHelper wej wyj liczba 0
    hClose wej
    hClose wyj

main = przepiszCoN "wejscie.txt" "wyjscie.txt" 5

私はそれがうまくいくはずだと思います...しかし私は1つの奇妙なエラーを受け取ります:

przepisz.hs:6:9:
    Parse error in pattern: if eof then return () else linia

それは私には意味がありません。私は別のプログラムで同じ表現を使用していましたが、それは害のように機能しました。これらの行を削除して、異なるインデントで書き込もうとしました(以前、空白に問題があったことを覚えています)。しかし、それでも同じエラーが発生します:(。


- 編集

OK、最初のエラーがあります...それはのelse do代わりですelse。しかし、ここに別の問題があります。

przepisz.hs:11:25: parse error on input `przepiszConHelper'
4

2 に答える 2

3

問題はここにあります:

if eof then return ()
    else
        linia <- hGetLine wejscie

実際、問題は空白ではありません。問題は、doブロックが部分式内で拡張されることを期待しているように見えることですが、そうではありません。else句は、独自のdoブロックを定義する必要があります。

if eof then return ()
    else do
        linia <- hGetLine wejscie

ただし、その後に別のエラーがあります。

if (mod licznik liczba) == 0 then
    hPutStrLn wyjscie linia
przepiszConHelper wejscie wyjscie liczba (licznik + 1)

elseこの句がありませんififは常にHaskellの式なので、常に何かに評価される必要があります。IO「条件が真の場合はこのアクションを実行し、それ以外の場合は何も実行しない」を表現する場合は、次を使用できますreturn ()

if (mod licznik liczba) == 0 
    then hPutStrLn wyjscie linia
    else return ()

標準ライブラリには、whenこのアイデアを表現する関数も含まれています。

when (mod licznik liczba == 0) $ hPutStrLn wyjscie linia

if必要に応じて、同じ方法で外部式を書き直して、次のようにすることができます。

przepiszConHelper :: Handle -> Handle -> Integer -> Integer -> IO ()
przepiszConHelper wejscie wyjscie liczba licznik = do
    eof <- hIsEOF wejscie
    when (not eof) $ do 
        linia <- hGetLine wejscie
        when (mod licznik liczba == 0) $ hPutStrLn wyjscie linia
        przepiszConHelper wejscie wyjscie liczba (licznik + 1)
于 2013-01-13T05:20:13.507 に答える
3

主に純粋なコードをIOから分離し、より標準的な関数を使用するという、別の方法を提案したいと思います。また、私はあなたが行ったよりも小さな関数を書こうとすることがよくあり、それぞれをシンプルで保守しやすいものにしています。

まず、リストのn番目の要素を保持しましょう。これを行うには、数値[1..]を使用して圧縮し、数値が。で割り切れる値のみを保持しnます。

przechowac :: Int -> [a] -> [a]
przechowac n listy = [a| (a,i) <- zip listy [1..], i `mod` n == 0]

たとえば、

*Main> przechowac 3 [1..10]
[3,6,9]

次に、文字列の行でそれを使用している間、純粋なままにしましょう:

przechowacLinie :: Int -> String -> String
przechowacLinie n = unlines . przechowac n . lines

*Main> putStrLn "Hej,\nHaskell\njest\nfantastyczny"
Hej,
Haskell
jest
fantastyczny
*Main> putStrLn (przechowacLinie 2 "Hej,\nHaskell\njest\nfantastyczny")
Haskell
fantastyczny

それでは、IOでラップしましょう。関数をファイルに適用する関数を作成し、それを出力ファイルに保存します。

zastosowacFunkcje :: (String -> String) -> FilePath -> FilePath -> IO ()
zastosowacFunkcje f wejscie wyjscie = do
   wej <- readFile wejscie
   writeFile wyjscie (f wej)

FilePathこれは型の同義語でありString、型の署名をより明確にします。)

私はそれを使ってあなたの関数を書くことができます:

przepiszCon :: Int -> FilePath -> FilePath -> IO ()
przepiszCon liczba = zastosowacFunkcje (przechowacLinie liczba)

実際、最近は次のような機能の使用をやめzastosowacFunkcje、今では

przepiszCon :: Int -> FilePath -> FilePath -> IO ()
przepiszCon n wejscie wyjscie = fmap (przechowacLinie n) (readFile wejscie)
                                                     >>= writeFile wyjscie

fmapとても便利だと思うからです。

于 2013-01-14T01:19:17.643 に答える