Dea*_*unt 18

第一个更快

 (n >= 3) && (n <= 99)
Run Code Online (Sandbox Code Playgroud)

它正在进行3次操作

 n >= 3
 n <= 99
 and
Run Code Online (Sandbox Code Playgroud)

elem在数组中查找项目时,最多(99-3)*2操作也是如此.

index = 0
isFound = false
array[] = { 3, 4, 5, 6, ... 98, 99 }

while isFound == false
   isFound = (n == array[index++])
Run Code Online (Sandbox Code Playgroud)

  • 你会期待什么"黑魔法"?即使在引擎盖下,编译器识别[3..99]是一个范围并执行范围检查它仍然只与(n> = 3)&&(n <= 99)相同 (4认同)
  • 我猜你可以通过使用重写规则a la"elem/enumFromTo"forall x lo hi来获得GHC以优化这种特殊情况.elem x(enumFromTo lo hi)=(x> = lo && x <= hi)`.我从来没有用重写规则做过任何事情(太像黑魔法了;-),所以请用一大堆盐吧! (2认同)
  • 好吧,`elem`无法知道它所使用的列表:特别是它来自像[3..99]`这样的表达式的信息在运行时消失了.想想如何计算'elem n(3:4:50000:[6..99]`并且你会看到elem必须经历所有元素.至于你的第一个评论中的问题:看到的来源Data.List:http://www.haskell.org/ghc/docs/latest/html/libraries/base/src/GHC-List.html#elem用于定义(它不涉及数组) (2认同)

Flo*_*iel 11

(n> = 3)&&(n <= 99)更快,因为它只涉及两个平凡的比较.如果编译器/解释器没有做任何真正的黑魔法优化,它必须构造列表([3..99]),因为懒惰的评估不能使用(通常"拉"下一个值,直到你完成,在这种情况下会有O(n/2)的复杂度.


Mtn*_*ark 8

这两个表达并不意味着相同的事情.一个微妙的区别是一个依赖于Ord另一个Enum:

> :t \n -> (n >= 3) && (n <= 99)
\n -> (n >= 3) && (n <= 99) :: (Num a, Ord a) => a -> Bool

> :t \n -> n `elem` [3..99]
\n -> n `elem` [3..99] :: (Num a, Enum a) => a -> Bool
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果n是3.14159,那么第一个测试将通过,但第二个测试不会:

> (pi >= 3) && (pi <= 99)
True

> pi `elem` [3..99]
False
Run Code Online (Sandbox Code Playgroud)

此外,尽管四个前奏Num实例(Int,Integer,FloatDouble)都是的所有实例OrdEnum,可以想像一个数值类型,它是一个实例Ord,但不是Enum.在这种情况下,第二次测试甚至不合法.

因此,一般情况下,编译器不能将第二个与第一个一样快,除非它知道给定类型,Ord并且该范围内的所有有序值也在创建的列表枚举中enumFromTo.对于Float并且Double这不是真的,因为Int并且Integer编译器无法导出它,编译器和库程序员必须手动编写它并确保它在所有情况下都保持.