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