2010-02-03 22 views
6

實現我發現:如何ABS,註冊等在F#

abs -10 
abs -10L 

都工作。所以我想知道F#如何實現這一點,並在源代碼中搜索:

type AbsDynamicImplTable<'T>() = 
    let AbsDynamic x   = AbsDynamicImplTable<_>.Result x 

    [<CompiledName("Abs")>] 
    let inline abs (x: ^T) : ^T = 
     AbsDynamic x 
     when ^T : ^T = absImpl x 

我對這些感到困惑。

正如我所知道的一個函數,如abs,我們必須將輸入與0進行比較,並且對於不同類型有不同的0。

謝謝。

+0

您不必將X與0進行比較以找到其絕對值 - 您可以將其與-X進行比較。 – Simon 2010-02-03 15:04:11

+1

@西蒙看起來不錯。但是,你的方法不正確。考慮x = -2147483648。 – 2010-02-03 15:17:22

+0

是的,好點 - 雖然我懷疑你會得到相同的異常,試圖評估-X在這種情況下,因爲abs()應該拋出。 – Simon 2010-02-03 15:56:27

回答

13

要一些解釋添加到發佈的ChaosPandion,與F#功能該問題的代碼一樣abs的是,他們可以與任何數值類型的工作。沒有辦法用F#/ .NET泛型來表達它 - 唯一支持的約束是該類型參數實現了某個接口或者具有構造函數,但是對於數字類型沒有限制。因此,F#還支持靜態約束(類型參數是^a,而不是通常的'a),並且這些在編譯時使用內聯進行處理(這也解釋了爲什麼該函數必須是inline)。您可以通過使用內置函數從LanguagePrimitives它包含需要一些限制許多有用的功能給你寫靜態約束自己的功能:

> 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 

注意約束推斷 - DivideByInt要求類型有DivideByInt成員,所以我們的功能需要相同的東西(如果它也有這個成員,它會和你自己的類型一起工作,這非常有用!)。

除此之外,的abs的實現使用的是隻允許在F#庫兩個額外的花樣 - 它指定不同的代碼(以內聯時,可以使用),用於不同類型的(使用when ^a:int = ....)和回退的情況下,這使用Abs成員,因此它可以與任何明確列出的類型或與Abs成員的類型一起使用。另一個訣竅是retype函數「更改類型」,但不包含任何代碼 - 唯一的目的是進行代碼類型檢查,但這可能非常不安全 - 因此僅在F#庫中使用。

+0

+1 - 非常棒。 – ChaosPandion 2010-02-03 15:42:28

8

其實這阿布斯表將調用此:

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,但我想我的答案的第一個版本是正確的。 – 2010-02-03 15:11:43