2010-02-03 9 views
6

ich gefunden:wie abs sind, melden usw. implementiert in F #

abs -10 
abs -10L 

beide arbeiten. Also habe ich mich gefragt, wie F # dies implementiert hat und eine Suche im Quellcode durchgeführt hat:

Und ich bin verwirrt mit diesen.

Wie ich in einer Funktion wie abs weiß, müssen wir die Eingabe mit 0 vergleichen, und es gibt verschiedene 0s für verschiedene Typen.

Danke.

+0

Sie müssen nicht X mit 0 vergleichen, um seinen absoluten Wert zu finden - Sie können es mit -X vergleichen. – Simon

+1

@Simon scheint ein guter Punkt. ABER, deine Methode ist nicht korrekt. Betrachte x = -2147483648. –

+0

ja, guter Punkt - obwohl ich vermute, dass Sie die gleiche Ausnahme bekommen würde, versuchen zu bewerten -X in diesem Fall als abs() sollte sowieso werfen. – Simon

Antwort

13

Um eine Erklärung zu dem von ChaosPandion geschrieben Code hinzufügen, das Problem mit F # Funktionen wie abs ist, dass sie mit beliebigen numerischen Typs arbeiten können. Es gibt keine Möglichkeit, dies auszudrücken, nur mit F # /. NET-Generics - die einzigen unterstützten Einschränkungen sind, dass Typ Parameter implementiert eine bestimmte Schnittstelle oder hat einen Konstruktor, aber es gibt keine Einschränkung für numerische Typen.

Also, F # unterstützt auch statische Einschränkungen (der Typ-Parameter ist ^a anstelle der üblichen 'a) und diese werden bei der Kompilierung verarbeitet inlining verwenden (dies erklärt auch, warum die Funktion inline sein). Sie können Sie eigene Funktion mit statischen Einschränkungen schreiben von integrierten Funktionen von LanguagePrimitives verwendet, die viele nützliche Funktionen, die einige Einschränkungen erfordern:

> let inline half (num: ^a) : ^a = 
    LanguagePrimitives.DivideByInt< (^a) > num 2 
    ;; 
val inline half : ^a -> ^a 
    when ^a : (static member DivideByInt : ^a * int -> ^a) 

> half 42.0;; 
val it : float = 21.0 

> half 42.0f;; 
val it : float32 = 21.0f 

Beachten Sie, dass Einschränkungen abgeleitet werden - DivideByInt erfordert, dass der Typ DivideByInt Mitglied hat, so Unsere Funktion erfordert das gleiche (und es funktioniert mit Ihrem eigenen Typ, wenn es auch dieses Mitglied hat, was sehr nützlich ist!).

Zusätzlich dazu, die Umsetzung der abs verwendet zwei weitere Tricks, die nur in der F # Bibliothek erlaubt sind - es gibt verschiedenen Code (zu verwenden, wenn inlining) für verschiedene Arten (mit when ^a:int = ....) und einem Rückfall Fall der verwendet Abs Mitglied, so dass es mit jedem explizit aufgeführten Typ oder einem Typ mit Abs Mitglied funktioniert. Ein weiterer Trick ist die -Funktion, die "den Typ ändert", aber keinen Code enthält - der einzige Zweck ist die Überprüfung des Codetyps, aber dies könnte sehr unsicher sein - daher wird dies nur in F # -Bibliotheken verwendet.

+0

+1 - Fantastisch. – ChaosPandion

8

Eigentlich dass Abs Tabelle wird dieser Aufruf:

let inline abs_impl (x: ^a) : ^a = 
    (^a: (static member Abs : ^a -> ^a) (x)) 
    when ^a : int32  = let x : int32  = retype x in if x >= 0 then x else -x 
    when ^a : float  = let x : float  = retype x in if x >= 0.0 then x else -x 
    when ^a : float32  = let x : float32 = retype x in if x >= 0.0f then x else -x 
    when ^a : int64  = let x : int64  = retype x in if x >= 0L then x else -x 
    when ^a : nativeint = let x : nativeint = retype x in if x >= 0n then x else -x 
    when ^a : int16  = let x : int16  = retype x in if x >= 0s then x else -x 
    when ^a : sbyte  = let x : sbyte  = retype x in if x >= 0y then x else -x 
    when ^a : decimal  = System.Math.Abs(retype x : decimal) 
+0

+1 für die eigentliche Implementierung, aber ich denke, die erste Version meiner Antwort war richtig. –