我想在不导入 Data.Bool 的情况下仅使用 prelude 内置函数来构建下面的函数。我想将 bool 函数替换为其他函数,因此我不必导入 Data.Bool 并且函数打印与以下函数相同的输出。我该怎么做才能返回相同的输出?
increment :: [Bool] -> [Bool]
increment x = case x of
[] -> [True]
(y : ys) -> not y : bool id increment y ys
我想在不导入 Data.Bool 的情况下仅使用 prelude 内置函数来构建下面的函数。我想将 bool 函数替换为其他函数,因此我不必导入 Data.Bool 并且函数打印与以下函数相同的输出。我该怎么做才能返回相同的输出?
increment :: [Bool] -> [Bool]
increment x = case x of
[] -> [True]
(y : ys) -> not y : bool id increment y ys
bool
from Data.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
。对于任何给定的输入,您需要考虑所有四个概念上不同的处理步骤,以了解函数在做什么,最后,您可能仍然不确定哪些步骤是由输入的哪些方面触发的。
现在,理想情况下,GHC-O2
会在内部将这两个版本编译成完全相同的代码。几乎可以。但是,事实证明,由于一个明显的优化错误,原始代码最终的效率比这个重写的版本略低,因为它不必要地检查y == True
了两次。