【问题标题】:nubBy is not working as expectednubBy 没有按预期工作
【发布时间】:2016-02-05 12:52:41
【问题描述】:

下面的函数应该生成素数,但它不适用于 GHC 7.10.2。其他人看到了吗?

GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
Prelude> import Data.List
Prelude Data.List> print . take 100 . nubBy (\x y -> x `rem` y == 0) $ [2..]
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101]

奇怪的是,它似乎在这个网站上运行良好:

rextester.com/LWZCQ71376

【问题讨论】:

  • 我认为 nubBy 需要等价关系。
  • 这就是文档所说的,而这里传递的函数不是,这将导致未定义的行为。在实践中,它可以例如已被重写,使得参数以相反的顺序应用(对于等价函数是安全的,而不是像这样的任意函数)
  • 它在这里工作得很好:rextester.com/LWZCQ71376
  • 它适用于 ghc 7.6.3。也许nubBy 被修改和“优化”以依赖等价关系属性?

标签: haskell


【解决方案1】:

base-4.7.x 和 base-4.8.0.0 之间的变化是 elem_by 的定义,这就是 nubBy 的定义。

在 base-4.7 elem_by 中有这个子句:

elem_by eq y (x:xs)     =  y `eq` x || elem_by eq y xs

在 base-4.8 中改为:

elem_by eq y (x:xs)     =  x `eq` y || elem_by eq y xs

此更改的历史记录在这些 TRAC 问题中:

请注意,nubBy 的 Haskell Report Prelude 版本是:

nubBy eq (x:xs)         =  x : nubBy eq (filter (\ y -> not (eq x y)) xs)

这与 base-4.7 实现不一致,因此这也解释了这种变化。

【讨论】:

  • eq 函数应该是可交换的,看来这种变化可能是我的问题的原因。
  • 不!您的 eq 函数不可交换! rem 4 2 /= rem 2 4。一个将等于 0,而另一个将是 2。
  • 我认为对称这个词更好用。
  • @ThomasM.DuBuisson 我说的是 elem_by 的变化。这种变化对我来说应该不是问题,因为如果x `eq` y,那么y `eq` x 对于所有Int。所以 elem_by 的这种变化不会是我的问题的原因。
  • @VansonSamuel 我们在互相交谈吗?在您的使用中,eq = (\x y -> x `rem` y == 0)elem_by 的更改意味着您的使用没有问题(x `rem` y == 0) == (y `rem` x == 0) 必须持有的属性。我在等待你证明这样的财产存在;-)。
【解决方案2】:

在新的基础上,参数的顺序似乎已经颠倒了。编辑:我称之为错误,但另一个答案指出旧行为是错误的顺序。

通过观察可以看到顺序已经翻转:

> print . take 5 . nubBy (\x y -> trace (show (x,y)) $ x `rem` y == 0) $ [2..]
[2(2,3)
,3(3,4)
(2,4)
,4(4,5)
(3,5)
(2,5)

当然rem 2 4 不等于 0(它等于 2),所以它产生 4

请注意,当您翻转 lambda 中的参数顺序时,您会得到想要的结果:

> print . take 5 . nubBy (\x y -> trace (show (x,y)) $ y `rem` x == 0) $ [2..]
[2(2,3)
,3(3,4)
(2,4)
(3,5)
(2,5)
....

编辑:由于讨论表明关系应该是平等的并且无论顺序如何都可以运行(我现在懒得看报告)请注意,您可以先比较参数并以任何一种方式获得稳定的行为:

print . take 100 . nubBy (\x y -> rem (max x y) (min x y) == 0) $ [2..]

【讨论】:

  • 你会称这是 GHC 基础中的错误吗?
  • 当然可以。首先,将“bug”定义为与 Haskell 语言规范相反的行为。其次,注意这种行为是相反的。第三,喊“bug”。
  • (\x y -> rem (max x y) (min x y) == 0) 可能是对称的,但仍然不是等价关系。
  • 是的,我只是小心翼翼地将这种行为称为“稳定”。