我正在尝试通过代码的出现来学习 haskell(我知道,现在已经很晚了),但我遇到了我无法理解的缩进问题,尽管我检查了haskell.org或wikibooks上我认为相关的部分。
具体来说,在缩进我的内部辅助函数时,如果我缩进 4 个或更少的空格(不计算主块的 4 个空格缩进),haskell 将发送解析错误,但如果缩进至少 5 个额外的空格,它将起作用。所以我需要将 let ... 至少缩进 9 个空格。但是,仅将主块缩进 4 个空格而不是 5 个空格似乎可以正常工作,我不明白其中的区别。
因此这是可行的:
solvePart1 :: String -> IO ()
solvePart1 input = do
let parsedInput = map (map read . words) . lines $ input :: [[Int]]
let validateReport report =
let differences = zipWith (-) report (tail report)
in all (\d -> d >= 1 && d <= 3) differences || all (\d -> d <= -1 && d >= -3) differences
let areReportsValid = map validateReport parsedInput
let validReportsCount = sum $ map fromEnum areReportsValid
putStrLn $ "Day 2, Part 1: " ++ show validReportsCount
但事实并非如此:
solvePart1 :: String -> IO ()
solvePart1 input = do
let parsedInput = map (map read . words) . lines $ input :: [[Int]]
let validateReport report =
let differences = zipWith (-) report (tail report)
in all (\d -> d >= 1 && d <= 3) differences || all (\d -> d <= -1 && d >= -3) differences
let areReportsValid = map validateReport parsedInput
let validReportsCount = sum $ map fromEnum areReportsValid
putStrLn $ "Day 2, Part 1: " ++ show validReportsCount
该问题与 do 与 let ... in 语法无关,因为将内部函数重写为 do 块不会改变该问题:仍然需要 5 个空格。
(我使用 vscode 作为编辑器,并使用堆栈进行构建,以防万一)
在某种程度上,它等同于:
相对:
该项目需要位于定义范围的标识符开头右侧的一列。因此,
let
本身不算数,事实上,例如,同样也不起作用:let
如果比 更靠右一列,则此方法有效x
:话虽如此,通常你不会在块中使用很多
let
sdo
,而是使用没有块的函数do
,例如:然后使用以下命令解决:
这是 Haskell 新手经常犯的一个错误。
let
关键字是一个布局标记,如do
、where
和of
(如case
...of
),这意味着当它后面没有左括号时,它将开始一个块。关键字后面的第一个元素决定了块的左边距。如果您愿意,您可以随时在布局关键字后放置换行符和缩进,这样您就不需要对齐任何内容了。当您有多个let
绑定时,您可以将它们合并为一个let
块。尝试在程序中使用明确的括号和分号,它会让您熟悉布局规则将它们插入到哪里。