我想只使用prelude内置函数构建下面的函数,而不导入Data.Bool。我想将Bool函数替换为其他函数,这样我就不必导入Data。Bool和函数打印与下面函数相同的输出。我如何才能做到这一点,使它返回相同的输出?
increment :: [Bool] -> [Bool]
increment x = case x of
[] -> [True]
(y : ys) -> not y : bool id increment y ys
Data.Bool的bool
与if
语句做的事情完全相同,因此它可以是实现它的一种方法:
bool x y b = if b then y else x
@dfeuer在一条评论中建议,你应该扔掉这段代码,因为它很恶心,而应该自己写。如果你是一开始写代码的人,却不明白为什么它很恶心,这可能会让你感到痛苦,所以请允许我详细说明。
事实上;恶心的";太强势了。然而,代码过于复杂,难以理解。一个更直接的实现使用对函数参数的模式匹配来完成所有处理:
increment :: [Bool] -> [Bool]
increment [] = [True]
increment (False : rest) = True : rest
increment (True : rest) = False : increment rest
这段代码对大多数人来说更容易阅读,因为所有的决策逻辑都是相同的"电平";并以相同的方式实现——通过检查定义左侧的三个模式,您可以一目了然地看到这三个互斥的情况是如何处理的。
相反,原始代码要求读者考虑与空与非空列表的模式匹配;而不是";对第一个布尔值的计算,基于相同布尔值的bool
调用,以及对布尔值列表的其余部分应用函数id
或递归increment
。对于任何给定的输入,您需要考虑所有四个概念上不同的处理步骤,以了解函数在做什么,最后,您可能仍然不确定输入的哪些方面触发了哪些步骤。
现在,理想情况下,带有-O2
的GHC会在内部将这两个版本编译为完全相同的代码。它几乎做到了。但是,事实证明,由于一个明显的优化错误,原始代码的效率最终比这个重写版本略低,因为它不必要地检查y == True
两次。