我有一个字符串列表,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
?似乎有点杀手。
如果我只是删除撇号,则编译器将返回错误,因为它陷入了无限循环。
答案 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 ] ++
...