我查看了有关缩进的问题,这些问题无济于事。我的缩进看起来也正确,但是根据编译器,它不是。 正确的缩进是什么,规则是什么?
readFile filename = do
inputFile <- openFile filename ReadMode
readLines inputFile
hClose inputFile
readLines inputFile =
do endof <- hIsEOF inputFile
| endof = return()
| otherwise = do
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
使用所有空格并且不使用制表符。 错误: “在输入'|'上解析错误 | endof = return()“
答案 0 :(得分:4)
您可以为此重组代码,例如
readLines :: Handle -> IO ()
readLines inputFile = g =<< hIsEOF inputFile
where -- hIsEOF :: Handle -> IO Bool
g endof
| endof = return ()
| otherwise = do
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
保护| ...
属于函数定义或大小写表达式。它们不能单独出现在do
块中。
g =<< hIsEOF inputFile
是一种较短的书写方式
readLines inputFile = do { endof <- hIsEOF inputFile
; g endof
}
where
g endof
| endof = .....
但是更简单的选择是首先在if ... then ... else ...
块中使用do
:
readLines inputFile =
do { endof <- hIsEOF inputFile
; if endof
then return()
else do { inpStr <- hGetLine inputFile
; print inpStr
; readLines inputFile
}}
另一个人正在使用LambdaCase内联g
定义:
readLines :: Handle -> IO ()
readLines inputFile = hIsEOF inputFile >>=
(\ case { True -> return ()
; _ -> do
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile })
并且案例子句可以有警卫(尽管这里我们不需要警卫)。
答案 1 :(得分:1)
正如Will Ness在回答中解释的那样,缩进在这里不是您的问题-问题是您试图在| …
块中的语句之后使用防护(do
),但是守卫只能出现在(1)函数方程式的模式和主体之间:
function param1 param2
| guard1 = body1
| guard2 = body2
…
和(2)case
个表达式:
case expr of
pattern1
| guard1 -> body1
| guard2 -> body2
pattern2
| guard3 -> body3
…
因此,您可能需要一个if
表达式。至于缩进规则,您的代码已正确缩进,但您并不需要使用的空白:基本规则是:
某些关键词,例如do
,let
,where
和of
开始布局图块
在这些块中,所有内容都必须缩进到该块第一行的第一列之外
如果表达式包含多行,则第一行之后的行必须缩进
因此,始终有效的经验法则是在每个此类关键字之后简单地添加换行符并缩进一些空格(例如2或4):
readFile filename = do -- newline+indent to begin block
inputFile <- openFile filename ReadMode
readLines inputFile
hClose inputFile
readLines inputFile = do -- newline+indent to begin block
endof <- hIsEOF inputFile
if endof -- indent to continue if expression
then return ()
else do -- newline+indent to begin block
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
另一种样式是在与layout关键字相同的行上开始一个块;那么一切都需要与该行具有相同的对齐方式:
readFile filename = do inputFile <- openFile filename ReadMode
readLines inputFile
hClose inputFile
readLines inputFile = do endof <- hIsEOF inputFile
if endof
then return ()
else do inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
我宁愿避免这种样式,因为它会导致缩进,这也取决于之前代码的宽度。
这两种样式都使用显式定界符分解为以下代码,您还可以编写自己的定界符,以更好地了解布局的工作原理:
readFile filename = do {
inputFile <- openFile filename ReadMode;
readLines inputFile;
hClose inputFile;
};
readLines inputFile = do {
endof <- hIsEOF inputFile;
if endof
then return ()
else do {
inpStr <- hGetLine inputFile;
print inpStr;
readLines inputFile;
};
};
经常使人绊倒的一件事是let
还引入了一个布局块,用于在同一块中定义多个绑定。因此,如果您编写一个带有长定义的let
语句或let
…in
…表达式,则需要将其写成对齐:
let example1 = some very long definition
that we need to wrap across lines
example2 = another binding for illustration
-- ^ everything must be aligned past this column
in example1 . example2
或者使用与其他所有样式相同的换行符+缩进样式:
let
example1 = some very long definition
that we need to wrap across lines
example2 = another binding for illustration
in example1 . example2
最后,为了方便起见,可以使用if x then return () else y
中的unless x y
或when (not x) y
将unless
或when
写入Control.Monad
:
import Control.Monad (unless)
…
endof <- hIsEOF inputFile
unless endof $ do
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile
此外,您可能会在启用$
扩展名的代码中看到do
之前省略了case
(以及其他\
和BlockArguments
之类的块关键字) :
{-# LANGUAGE BlockArguments #-}
import Control.Monad (unless)
…
endof <- hIsEOF inputFile
unless endof do -- no need for $
inpStr <- hGetLine inputFile
print inpStr
readLines inputFile