复制可被5整除的行

时间:2013-01-13 05:06:43

标签: haskell if-statement rewrite eof parse-error

再次是我:)。我尝试编写一个程序,将可以被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

我认为它应该有效...但我得到一个,奇怪的错误:

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

这对我来说毫无意义。我一直在其他程序中使用相同的表达式,它就像一个伤害。我试图删除这些行并用不同的缩进写出它们(我记得我以前遇到过一些白色空格问题)。但我仍然得到同样的错误:(。


- 修改

好的,我有第一个错误......它只是else do而不是else。但是这里出现了另一个问题:

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

2 个答案:

答案 0 :(得分: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的{​​{1}}条款。 if始终是Haskell中的表达式,因此它必须始终评估为某些内容。如果您想表达“如果条件为真,请执行此if操作,否则不执行任何操作”,您可以使用IO

return ()

标准库甚至包含表达这个想法的函数if (mod licznik liczba) == 0 then hPutStrLn wyjscie linia else return ()

when

如果你愿意,你可以用同样的方式重写外部when (mod licznik liczba == 0) $ hPutStrLn wyjscie linia 表达式,并得到这样的结果:

if

答案 1 :(得分: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)

FilePathString的类型同义词,可以使类型签名更清晰。)

我可以用它来编写你的函数:

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非常方便。