2013-09-21 186 views
1

我正在編寫一個程序,在該程序中我必須驗證obj類型的某些輸入爲數字類型。基本上,我在找寫功能F#數值類型鑄造

validateint : obj -> int option 
validatefloat : obj -> float option 
... and so on 

這本身是沒有問題的,比如,例如我有一樣的東西:

let validateint x = 
    match x with 
     | y when y.GetType() = typeof<int>   -> y :?> int     |> Some 
     | y when y.GetType() = typeof<float>  -> y :?> float  |> int |> Some 
     | y when y.GetType() = typeof<System.Int16> -> y :?> System.Int16 |> int |> Some 
     /// ...more numeric cases 
     | _           -> None 

但是,編寫類似的代碼validateint後我以爲分解出一個函數,它的轉換函數作爲輸入,像:

let validatenumeric cast x = 
    match x with 
     | y when y.GetType() = typeof<int>   -> y :?> int   |> cast |> Some 
     | y when y.GetType() = typeof<float>  -> y :?> float  |> cast |> Some 
     | y when y.GetType() = typeof<System.Int16> -> y :?> System.Int16 |> cast |> Some 
     /// ...more numeric cases 
     | _           -> None 

然後定義

let validateint = validatenumeric int 
let validatefloat = validatenumeric float 
... 

但是,這不起作用,因爲F#基本上推斷出類型轉換爲int - >'a,然後第二個和更高版本的匹配個案輸入錯誤。我想我可以通過爲每個數字類型添加一個額外的數據類型來避免這種情況,但是這感覺像一個醜陋的黑客。有沒有更優雅的解決方案?

回答

4

我覺得你並不需要重新實現的功能自己。 .NET中的System.Convert類型已提供嘗試將任何輸入轉換爲指定類型的操作,如果無法完成轉換,則會失敗。

let validateNumeric<'T> input = 
    try Some(System.Convert.ChangeType(input, typeof<'T>) :?> 'T) 
    with _ -> None 

下面是一個簡單的F#交互式輸出:

> validateNumeric<int> (box 1.1);; 
val it : int option = Some 1 
> validateNumeric<float> "1.1";; 
val it : float option = Some 1.1 
> validateNumeric<float> "xxx";; 
val it : float option = None 

一件不幸的事情是,有沒有一種方法,不會拋出異常 - 如果你需要成千上萬的轉換值,這可能對你來說太慢了。

+0

謝謝。作爲一個後續問題,寫這個函數時沒有類型註釋,這在F#中是不可能的,或者在函數語言中是不可能的。我試圖理解爲什麼這個函數會失敗(無論是在原始形式還是當例如在^ a:(靜態成員op_Explicit:^ a - >'b))時添加註釋^ a - >'b)。 – Bram

+0

@Bram在F#中,不能直接將泛型函數作爲參數傳遞(以便您可以在不同類型中調用它)。這在其他一些語言中是可能的 - 您也可以在F#中實現這一點,但是您必須將它作爲由對象表達式實現的接口傳遞(因此它會變得有點難看)。 –

2

你需要使用一個通用的類型,試試這個

let validate<'a, 'b> (cast: 'a -> 'b) (x: 'a) = 
    try 
     cast x |> Some 
    with _ -> None 

let a = validate int "123" 
let b = validate int 1.23 
let c = validate float "abc" 
let d = validate<obj, decimal> Convert.ToDecimal (null :> obj) 
let e = validate<string, DateTime> Convert.ToDateTime "2013-9-21" 
+0

謝謝,這和以前的答案一樣好,但是因爲我只能選擇一個,所以我選擇了第一個。 – Bram

+0

不介意:) – kwingho

0
open System 
let validateint =function 
    |(x:obj) when x=null->None 
    | :? int as i->Some(i) 
    | :? float as f->Some(f|>int) 
    | :? string as s->Int32.TryParse(s)|>function |true,x->Some(x)|_->None 
    //| :? ... 
    |_->None