【问题标题】:Copying lines divisible by 5复制可被 5 整除的行
【发布时间】:2013-01-13 05:06:42
【问题描述】:

又是我:)。我尝试编写一个程序,它将数字可被 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'

【问题讨论】:

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


    【解决方案1】:

    问题出在这里:

    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)
    

    您缺少此 ifelse 子句。 if 在 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)
    

    【讨论】:

    • FWIW,when在标准库中的位置在Control.Monad模块中:import Control.Monad (when)
    • @luqui:哦,麻烦了。不是在前奏曲里吗?我的.ghci 文件再次让我误入歧途。
    【解决方案2】:

    我想建议一种不同的做事方式,主要是把纯代码从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 很方便。

    【讨论】:

      猜你喜欢
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-09
      • 1970-01-01
      • 1970-01-01
      • 2019-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多