复制可被5整除的行



又是我。我试着写一个程序,它把能被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'

问题就在这里:

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)

这个if缺少一个else子句。if在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)

我想建议一种不同的做事方式,主要是将纯代码与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,nHaskellnjestnfantastyczny"
Hej,
Haskell
jest
fantastyczny
*Main> putStrLn (przechowacLinie 2 "Hej,nHaskellnjestnfantastyczny")
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很方便。

相关内容

  • 没有找到相关文章

最新更新