【问题标题】:Why foldl1 fails applying the (==) operator?为什么 foldl1 无法应用 (==) 运算符?
【发布时间】:2011-02-06 00:08:44
【问题描述】:

来自前奏

foldl1:取前2项 列表并将函数应用于 他们,然后用 这个结果和第三个论点和 以此类推。

为什么不能这样写?

foldl1 (==) [6, 6, 6]
foldl1 (\x y -> x == y) [6, 6, 6]

【问题讨论】:

  • 只是一种预感,但在前两个上应用相等运算符会产生一个布尔值。我怀疑将布尔值与整数进行比较是否有意义。

标签: haskell functional-programming higher-order-functions fold


【解决方案1】:

如果你想检查一个列表的所有元素是否相等,一个快速的解决方案是

allEqual [] = True --(edit: this case is not necessary as pointed out by sepp2k)
allEqual xs = all (== head xs) xs

我敢肯定,有人会写出更优雅的方式来做到这一点,但这是可行的。

编辑:感谢 sepp2k 的建议。

【讨论】:

  • 不需要第一种情况,因为and [] 无论如何都是真的。 and . map 也是 all
  • sepp2k:我在没有先看到您的评论的情况下意识到这一点后进行了更新。我想我们在想同样的事情。然而,意识到不需要第一种情况是很聪明的。我在想head 在这种情况下会失败
  • 如果它被评估的话。但是如果xs 为空,则all 永远不会调用该函数,因此head 永远不需要评估。
  • @sepp2k:是的,在对您的评论感到困惑一段时间后,我意识到了这一点。看来我还没有完全让懒惰的想法接管......
【解决方案2】:

编辑:Antal 指出我的推理不正确。这是评论的相关部分,给出了真正的推理(我觉得逐字逐句很糟糕,但这个答案被接受了,所以我不能删除它):

这不起作用的原因是foldl1 的类型是(a -> a -> a) -> [a] -> a,但(==) 的类型是Num a => a -> a -> Bool。由于Bool不是Num(==)类型不匹配a -> a -> a,所以foldl1的申请被拒绝。如果它被接受了,你最终会遇到这样的情况:你试图做True == 6,但类型系统一开始就永远不会让你走那么远。

原答案(后推理不正确):

== 将占用两个 Ints 并返回一个 Bool。第一次迭代后,您的示例列表变为[True, 6]。然后它会尝试将 True6 进行比较,但失败了。

【讨论】:

  • 你的第一句话是正确的,但是在 Haskell 中 True 没有被强制转换为 1!
  • @marcog:谢谢,这很有意义。羞于我没有考虑它! :)
  • @drvitek 谢谢。我的实际haskell不强。
  • marcog:不用担心 - 如果 OP 曾经用另一种语言重新创建此功能并想知道为什么,例如,此代码会在 [6,6,1] 上给出不正确的结果,这是一个很好的说明。我不想追查那个错误......
  • 这仍然不太正确。列表[True,6] 不能存在于 Haskell 中,因为它的类型错误;即使可以,这也会使事情在运行时失败,而不是编译时。这不起作用的原因是foldl1 的类型是(a -> a -> a) -> [a] -> a,但(==) 的类型是Num a => a -> a -> Bool。由于Bool不是Num(==)类型不匹配a -> a -> a,所以foldl1的申请被拒绝。如果它接受,你最终会遇到这样一种情况:你试图做True == 6,但类型系统一开始就不会让你走那么远。
【解决方案3】:

这是另一个版本:

allEqual xs = and $ zipWith (==) xs (tail xs)

【讨论】:

  • 受您的回答启发,还有这个:allEqual xs = tail xs == init xs
  • @Dan:您可能想使用 drop 1 而不是 tail,所以这也适用于空列表。
  • 无济于事,因为在这种情况下,调用 init 时会失败。
【解决方案4】:

如果你想使用折叠,我建议这样修改:

allEqual xs = foldr (\x acc -> x == head xs && acc) True xs

这与已经建议的all 方法非常相似。请注意,这种方法和all 方法都可以在无限列表上工作(只要答案是False)。

对于非常长的有限列表,在极少数情况下,您可能会通过严格的左折叠获得更好的性能:

allEqual xs = foldl' (\acc x -> acc && x == head xs) True xs

但是正确的折叠通常更适合这个问题,因为(在这个实现中)它只执行等于length $ takeWhile (== head xs) xs 的折叠步骤。左折叠每次都会执行length xs折叠步骤。

【讨论】:

  • 您可以写allEqual xs = foldl' (flip $ (&&).(== head xs)) True xs 以获得更好的混淆。
猜你喜欢
  • 1970-01-01
  • 2018-05-08
  • 1970-01-01
  • 2018-01-01
  • 1970-01-01
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多