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

我认为它应该可以工作......但我得到一个奇怪的错误:

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'
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)

您缺少elsethis 的子句ifif始终是 Haskell 中的表达式,因此它必须始终计算为something。如果您想表达“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)

(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很方便。

于 2013-01-14T01:19:17.643 回答