这是我的代码:
boolTrueList :: [Bool] -> Bool
boolTrueList xs
| length (filterFalse xs) > 0 = False
| otherwise = True
where
filterFalse = filter (==False)
这是完美的工作,但是我想用 foldr/foldl 重写同样的东西,但我被卡住了。
我的想法是折叠一个列表,直到我找到一个错误的值,然后停止。有什么提示吗?
这是我的代码:
boolTrueList :: [Bool] -> Bool
boolTrueList xs
| length (filterFalse xs) > 0 = False
| otherwise = True
where
filterFalse = filter (==False)
这是完美的工作,但是我想用 foldr/foldl 重写同样的东西,但我被卡住了。
我的想法是折叠一个列表,直到我找到一个错误的值,然后停止。有什么提示吗?
我的想法是折叠一个列表,直到我找到一个错误的值,然后停止。有什么提示吗?
如果你想提前停止,你必须使用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
有一种思考折叠的方法,我发现它非常有用和直观(我可能在某处读过这个)。请注意,这可能不会导致最有效的解决方案,但我的关注点很明确。
不要将 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
使用高阶函数来避免迭代是不够的,我们应该避免迭代的心态。考虑开放式表达式,而不是“在这一点上,这个值会传播”。通常情况下,迭代只是生成开放式表达式的低级方式,并且由于我们的语言迫使我们这样做,我们的思维方式经常停留在它上面,即使我们有允许我们思考表达式的语言等级。