quotRem和divMod之间的区别何时有用?

gro*_*rom 42 haskell division

来自haskell报告:

如果y不为零,则quot,rem,div和mod类方法满足这些定律:

(x `quot` y)*y + (x `rem` y) == x
(x `div`  y)*y + (x `mod` y) == x
Run Code Online (Sandbox Code Playgroud)

quot是整数除法被截断为零,而结果div 被截断为负无穷大.

例如:

Prelude> (-12) `quot` 5
-2
Prelude> (-12) `div` 5
-3
Run Code Online (Sandbox Code Playgroud)

结果如何截断的区别在哪里?

Shr*_*saR 36

许多语言都有一个"mod"或"%"运算符,它在除法后给出余数,截断为0; 例如C,C++和Java,可能还有C#,会说:

(-11)/5 = -2
(-11)%5 = -1
5*((-11)/5) + (-11)%5 = 5*(-2) + (-1) = -11.
Run Code Online (Sandbox Code Playgroud)

Haskell的quotrem旨在模仿这种行为.我可以想象在一些人为的情况下,可能需要与某些C程序的输出兼容.

Haskell的divmod,随后Python的/和%,跟随数学家(至少数理论家)的约定,始终截断下来师(不计入0 -接近负无穷大),这样的形式始终是负数.因此在Python中,

(-11)/5 = -3
(-11)%5 = 4
5*((-11)/5) + (-11)%5 = 5*(-3) + 4 = -11.
Run Code Online (Sandbox Code Playgroud)

Haskell divmod遵循这种行为.

  • "所以余数总是非负的"技术上,"mod"的符号遵循第二个操作数的符号. (5认同)
  • @luqui:不,这不能解释.你总是可以得到x = q*y + r,其中r是非负的; 例如,如果`divMod 11(-5)=(-2,1)`(而不是(-3,-4)),你仍然有"11 =( - 2)*( - 5)+ 1".所以你的条件不强制`mod`的符号跟随第二个操作数.顺便说一下,`x = q*y + r`的属性也总是对于quotRem是真的,并且总是有无数对(q,r)使得`x = q*y + r`(恰好两个这些对有| r | <q,除非r = 0给出解,只有一对). (3认同)
  • 实际上,虽然C99和C++ 11确实指定了截断到0的定义,但是早期的版本使其保留了实现定义,因此"与C的兼容性"是其他语言或程序不太可能(或至少是坏...)的原因.截断到0. (2认同)

luq*_*qui 25

这不完全是你问题的答案,但在x86上的GHC中,Int上的quotRem将编译为单个机器指令,而divMod则做了相当多的工作.因此,如果您处于速度关键部分并且只处理正数,那么可以使用quotRem.

  • 在包括 x86 在内的许多体系结构上,当除以非常数时,使用向零截断除法比向下取负无穷除法稍快,但是当除以许多常量值(尤其是 2 的幂)时,使用向零截断除法零要快得多(例如,一条指令与 3 条指令相比)。我认为对速度敏感的代码往往比慢速代码有更多的“快速”划分。 (3认同)
  • 为了解决SPOJ素数,使用rem而不是mod使我的测试文件运行在4.758s而不是5.533s.这意味着在32位Ubuntu,Haskell Platform 2011下,更快版本的速度提高了16%. (2认同)

nam*_*min 6

一个简单的例子就是测试整数是偶数还是奇数.

let buggyOdd x = x `rem` 2 == 1
buggyOdd 1 // True
buggyOdd (-1) // False (wrong!)

let odd x = x `mod` 2 == 1
odd 1 // True
odd (-1) // True
Run Code Online (Sandbox Code Playgroud)

当然,请注意,您可以通过以这种方式定义odd来避免考虑这些问题:

let odd x = x `rem` 2 /= 0
odd 1 // True
odd (-1) // True
Run Code Online (Sandbox Code Playgroud)

一般来说,只要记住,因为y > 0,x mod y总是返回一些东西,>= 0同时x rem y返回0或与x相同的符号.