2008-12-04 10 views
35

Vom Haskell Report:Wann ist der Unterschied zwischen quotRem und divMod nützlich?

The quot, rem, div und mod Klasse Methoden diese Gesetze erfüllen, wenn y ungleich Null:

(x `quot` y)*y + (x `rem` y) == x 
(x `div` y)*y + (x `mod` y) == x 

quot ganzzahlige Division abgeschnitten Richtung Null, während das Ergebnis von div in Richtung negativer Unendlichkeit abgeschnitten ist.

Zum Beispiel:

Prelude> (-12) `quot` 5 
-2 
Prelude> (-12) `div` 5 
-3 

Was sind einige Beispiele dafür, wo der Unterschied zwischen dem, wie das Ergebnis abgeschnitten Angelegenheiten?

Antwort

35

Viele Sprachen haben einen "Mod" oder "%" Operator, der den Rest nach der Division mit Trunkierung in Richtung 0 gibt; zum Beispiel C, C++ und Java, und wahrscheinlich C#, würde sagen:

(-11)/5 = -2 
(-11)%5 = -1 
5*((-11)/5) + (-11)%5 = 5*(-2) + (-1) = -11. 

Haskells quot und rem sollen dieses Verhalten nachzuahmen. Ich kann mir vorstellen, dass die Kompatibilität mit der Ausgabe eines C-Programms in einer konstruierten Situation wünschenswert sein könnte.

Haskells div und mod und anschließend Python/und%, folgen der Konvention des Mathematiker (zumindest zahlen Theoretiker) in immer unten Teilung Kürzen (nicht gegen 0 - in Richtung negativ unendlich), so dass der Rest immer nicht negativ. So ist in Python,

(-11)/5 = -3 
(-11)%5 = 4 
5*((-11)/5) + (-11)%5 = 5*(-3) + 4 = -11. 

Haskells div und mod dieses Verhalten folgen.

+5

"so dass der Rest immer nichtnegativ ist" Technisch folgt das Vorzeichen von 'mod' dem Vorzeichen des zweiten Operanden. – newacct

+0

Huh, du hast Recht. Ich verstehe diese Design-Entscheidung nicht ... – ShreevatsaR

+0

es ist die Eigenschaft zu erhalten, die '(q, r) = divMod x y 'wenn und nur wenn' x = q * y + r'. Führen Sie ein Beispiel, es ist klug, wie es funktioniert. – luqui

6

Ein einfaches Beispiel, wo es wichtig wäre zu testen, ob eine ganze Zahl gerade oder ungerade ist.

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 

Hinweis, natürlich könnte man über diese Probleme vermeiden denkt nur um auf diese Weise ungerade definieren:

let odd x = x `rem` 2 /= 0 
odd 1 // True 
odd (-1) // True 

Im Allgemeinen nur daran erinnern, für y > 0, x mod y immer etwas >= 0 während zurückkehren x rem y gibt 0 oder etwas mit dem gleichen Vorzeichen wie x zurück.

23

Dies ist nicht genau eine Antwort auf Ihre Frage, aber in GHC auf x86, quotRem on Int kompiliert sich auf eine einzige Maschine Anweisung, während DivMod ein bisschen mehr Arbeit. Wenn Sie sich also in einem geschwindigkeitskritischen Bereich befinden und nur an positiven Zahlen arbeiten, ist QuotRem der richtige Weg.

+2

Um die SPOJ-Primzahlen zu lösen, verwendet rem statt mod meine Testdatei in 4.758 statt in 5.533s. Dies bedeutet, dass die schnellere Version unter 32-Bit Ubuntu, Haskell Platform 2011 16% schneller ist. –

+0

@TimPerry, ich denke nicht, dass das folgt. Was, wenn du in deinem ganzen Programm einen "Mod" gemacht hast und diese Verbesserung gesehen hast? – luqui

+0

Ich erklärte, dass, wenn ich Anrufe in meinem Primes-Code von Mod zu Rem änderte und ich eine Beschleunigung von 20% sah. Es ist kein theoretischer Kommentar. Es war eine Beschreibung eines Ereignisses. Ich änderte nur eine Sache (wenn auch mehrere Plätze) und ich sah eine Beschleunigung von 20%. Es scheint eine 20% ige Beschleunigung * DID * zu folgen. –

Verwandte Themen