0

这是我的代码:

boolTrueList :: [Bool] -> Bool
boolTrueList xs
  | length (filterFalse xs) > 0 = False
  | otherwise = True
  where
    filterFalse = filter (==False)

这是完美的工作,但是我想用 foldr/foldl 重写同样的东西,但我被卡住了。

我的想法是折叠一个列表,直到我找到一个错误的值,然后停止。有什么提示吗?

4

2 回答 2

12

我的想法是折叠一个列表,直到我找到一个错误的值,然后停止。有什么提示吗?

如果你想提前停止,你必须使用foldr. foldl总是必须遍历整个列表(因此在无限列表上根本不起作用)。

所以你要

foldr combine default list

default是一个空列表的结果,它会在True这里。现在,我们想如何组合?

False遇到 a 时,我们想立即返回 False,所以

combine False _ = False

当值为 时True,我们必须继续,所以

combine True more = more

换句话说,combine = (&&),所以

boolTrueList = foldr (&&) True

或者,甚至更短

boolTrueList = and
于 2012-12-06T13:42:20.397 回答
5

有一种思考折叠的方法,我发现它非常有用和直观(我可能在某处读过这个)。请注意,这可能不会导致最有效的解决方案,但我的关注点很明确。

不要将 fold 视为从列表中构建值的函数,而应将其视为在列表元素之间插入运算符的一种方式。当你这样做时,折叠就成为将列表转换为表达式的一种方式。例如,以下内容:

foldr op I [A,..,Z]

将产生以下扩展:

A op .. op Z op I 

如果你将这种想法应用到你的问题上,你需要做的就是问问自己应该做什么,我要确保你只有在 A .. Z 为真时才得到真?作为提示,I 通常是关于 op 的标识元素(不会改变表达式的值,例如 0 表示加法)。

好吧,我们从逻辑上知道 and (&&) 只有当它的所有操作数都为真时才为真。同样,True 是关于 && 的标识元素。话虽如此,您的扩展将是:

A && .. && Z && True

所以你的折叠将是:

foldr (&&) True [A,..,Z]

这意味着您的功能将是:

boolTrueList x = foldr (&&) True x

或者以更无点的风格:

boolTrueList = foldr (&&) True

使用高阶函数来避免迭代是不够的,我们应该避免迭代的心态。考虑开放式表达式,而不是“在这一点上,这个值会传播”。通常情况下,迭代只是生成开放式表达式的低级方式,并且由于我们的语言迫使我们这样做,我们的思维方式经常停留在它上面,即使我们有允许我们思考表达式的语言等级。

于 2012-12-06T14:22:21.220 回答