如何在不重命名变量的情况下累积更改?

时间:2019-06-24 01:43:04

标签: haskell

我有一个字符串列表,errors。我进行了一些检查,如果有任何失败,我会在errors后面附加一条消息。像这样:

let errors = []
let errors' = errors ++ if (check1 fails) then ["check1 failed"] else []
let errors'' = errors' ++  if (check2 fails) then ["check2 failed"] else []

当然,还有一种更惯用的方法来累积对errors的更改,而不必每次都创建新变量。我是否需要针对可变变量展开Data.IORef?似乎有点杀手。

如果我只是删除撇号,则编译器将返回错误,因为它陷入了无限循环。

3 个答案:

答案 0 :(得分:7)

您可以将条件和消息分组在一起

checksTodo = [(check1 fails, "check1 failed"), (check2 fails, "check2 failed")]

errors = map snd (filter fst checksTodo)

如果您愿意使用列表理解语法,可以改用更易读的方式编写它:

errors = [ msg | (cond, msg) <- checksTodo, cond ]
  

如果我只是删除撇号,则编译器将返回错误,因为它陷入了无限循环。

之所以发生这种情况,是因为默认情况下,Haskell中的let绑定(不同于大多数语言)是递归的。这意味着如果你说

let errors = errors ++ if (check1 fails) then ["check1 failed"] else []

编译器会将其视为递归定义。当您尝试在运行时评估errors时,由于需要errors来计算errors,因此会陷入无限循环。

答案 1 :(得分:3)

theindigamer所说的话,再加上错误检查的惯用方式通常是让您的检查器返回一个Either:如果出现问题,请产生一个带有错误消息的Left,否则为Right和结果。

由于在这种情况下,您的检查未产生实际结果,因此可以将结果设为单位类型();因此,您可以将支票转换为Either,如下所示:

check1Either = if check1 fails then Left "check1 failed" else Right ()

然后,只需运行检查并使用Left中的lefts函数使用Data.Either过滤元素:

import Data.Either

errors = lefts [check1Either, check2Either]

(您可能会问,如果没有结果可以用Right来填充,为什么不使用Maybe?您可以从{ {1}};只是catMaybes通常被解释为计算失败,而Data.Maybe通常意味着成功-与此处发生的情况相反-而习惯上讲Nothing是通常解释为错误)

答案 2 :(得分:3)

另一种选择:

let errors = 
       [ "check1 failed" | check1 fails ] ++
       [ "check2 failed" | check2 fails ] ++
       ...