0

我想在不导入 Data.Bool 的情况下仅使用 prelude 内置函数来构建下面的函数。我想将 bool 函数替换为其他函数,因此我不必导入 Data.Bool 并且函数打印与以下函数相同的输出。我该怎么做才能返回相同的输出?

increment :: [Bool] -> [Bool]
increment x = case x of
  [] -> [True]
  (y : ys) -> not y : bool id increment y ys
4

2 回答 2

5

boolfrom Data.Bool 的作用与if语句完全相同,因此它可以作为实现它的一种方式:

    bool x y b = if b then y else x
于 2020-12-10T16:16:47.307 回答
1

@dfeuer 在评论中建议您应该丢弃此代码,因为它很恶心,而是尝试自己编写。如果您是首先编写代码的人并且看不出它为什么令人作呕,那么这可能会让您感到痛苦,所以请允许我详细说明。

事实上,“恶心”这个词太强了。但是,代码过于复杂且难以理解。一个更直接的实现使用函数参数上的模式匹配来完成所有处理:

increment :: [Bool] -> [Bool]
increment [] = [True]
increment (False : rest) = True  : rest
increment (True  : rest) = False : increment rest

对于大多数人来说,这段代码更容易阅读,因为所有的决策逻辑都处于相同的“级别”并且以相同的方式实现——通过检查定义左侧的三个模式,您可以确切地看到如何三、互斥案件一目了然。

相比之下,原始代码要求读者考虑针对空与非空列表的模式匹配、“非”计算对第一个布尔值的影响、bool基于相同布尔值的调用以及函数的应用id或布尔列表其余部分的递归increment。对于任何给定的输入,您需要考虑所有四个概念上不同的处理步骤,以了解函数在做什么,最后,您可能仍然不确定哪些步骤是由输入的哪些方面触发的。

现在,理想情况下,GHC-O2会在内部将这两个版本编译成完全相同的代码。几乎可以。但是,事实证明,由于一个明显的优化错误,原始代码最终的效率比这个重写的版本略低,因为它不必要地检查y == True了两次。

于 2020-12-11T00:36:14.350 回答