如何修改这个 Haskell 函数,以便我不必导入 Data.Bool 而只使用前奏函数?



我想只使用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的boolif语句做的事情完全相同,因此它可以是实现它的一种方法:

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两次。

最新更新