2

我查看了有关缩进的问题,但没有任何帮助。我的缩进看起来也正确,但根据编译器它不是。什么是正确的缩进,规则是什么?

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

使用所有空格且不使用制表符。错误:“输入'|'上的解析错误 | 结束 = 返回()“

4

2 回答 2

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

守卫 , | ...属于函数定义或 case 表达式。他们不能自己出现在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 })

并且 case 子句可以有守卫(尽管在这里我们不需要它们)。

于 2019-04-15T11:43:30.513 回答
1

正如 Will Ness 在他的回答中解释的那样,缩进不是你的问题 - 问题是你试图使用防护(| …</code>) after a statement in a do block, but guards can only ever appear between patterns and bodies in (1) function equations:

function param1 param2
  | guard1 = body1
  | guard2 = body2
  …

And (2) case expressions:

case expr of
  pattern1
    | guard1 -> body1
    | guard2 -> body2
  pattern2
    | guard3 -> body3
  …

So you probably want an if expression instead. As for the rules of indentation, your code is indented correctly, but you don’t need as much whitespace as you’re using: the basic rules are:

  • Certain keywords such as do, let, where, and of begin layout blocks

  • Within those blocks, everything must be indented past the first column of the first line in the block

  • If an expression wraps across multiple lines, the lines following the first must be indented

So a rule of thumb that always works is to simply add a newline and indent by some number of spaces (e.g. 2 or 4) after every such keyword:

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

The alternative style is to start a block on the same line as the layout keyword; then everything needs to have the same alignment as that line:

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

I prefer to avoid this style because it leads to deep indentation, which also depends on the width of the code before it.

Both of these styles are desugared to the following code with explicit delimiters, which you can also write yourself to get a better feel for how layout works:

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;
    };
};

One thing that often trips people up is that let also introduces a layout block for defining multiple bindings in the same block. So if you write a let statement or let…<code>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 ywhen (not x) y使用unlessor whenfrom Control.Monad

import Control.Monad (unless)

…
  endof <- hIsEOF inputFile
  unless endof $ do
    inpStr <- hGetLine inputFile
    print inpStr
    readLines inputFile

此外,您可能会在启用扩展的代码中看到$省略的 before do(以及其他块关键字,如caseand ):\BlockArguments

{-# LANGUAGE BlockArguments #-}
import Control.Monad (unless)

…
  endof <- hIsEOF inputFile
  unless endof do -- no need for $
    inpStr <- hGetLine inputFile
    print inpStr
    readLines inputFile
于 2019-04-16T07:46:22.270 回答