2013-04-25 153 views
2

我只是想知道如何縮短這些代碼,我懷疑這是過於冗餘如何縮短OCaml代碼?

let get ename doc = 
    try Some (StringMap.find ename doc) with Not_found -> None;; 

let get_double ename doc = 
    let element = get ename doc in 
    match element with 
    | None -> None 
    | Some (Double v) -> Some v 
    | _ -> raise Wrong_bson_type;; 

let get_string ename doc = 
    let element = get ename doc in 
    match element with 
    | None -> None 
    | Some (String v) -> Some v 
    | _ -> raise Wrong_bson_type;; 

let get_doc ename doc = 
    let element = get ename doc in 
    match element with 
    | None -> None 
    | Some (Document v) -> Some v 
    | _ -> raise Wrong_bson_type;; 

所以,基本上,我有不同類型的值,我把所有這些類型的值到地圖中。

上面的代碼是爲了獲得相應的值類型的地圖。我所做的是爲每種類型,我有一個得到。要獲得一種價值,我必須看到a)。無論是否存在; B)。無論這種類型是否確實如果不是,都會引發例外。

但上面的代碼似乎是多餘的,你可以看到。每種類型的get之間唯一的區別就是類型本身。

我該如何縮短這段代碼?

回答

2

你可以這樣做:

let get_generic extract ename doc = 
    let element = get ename doc in 
    match element with 
    | None -> None 
    | Some v -> Some (extract v) 

let get_double = get_generic (function Double v -> v | _ -> raise Wrong_bson_type) 
let get_string = get_generic (function String v -> v | _ -> raise Wrong_bson_type) 
let get_doc = get_generic (function Document v -> v | _ -> raise Wrong_bson_type) 

編輯: 要刪除多餘的raise Wrong_bson_type(但它是醜陋的):

let get_generic extract ename doc = try 
    let element = get ename doc in 
    match element with 
    | None -> None 
    | Some v -> Some (extract v) 
with Match_failure _ -> raise Wrong_bson_type 

let get_double = get_generic (fun (Double v) -> v) 
let get_string = get_generic (fun (String v) -> v) 
let get_doc = get_generic (fun (Document v)-> v) 
+0

可以,我們甚至去除多餘'提高Wrong_bson_type' – 2013-04-25 13:38:18

+0

也在做,我們需要()爲雙V?喜歡的樂趣(Double v) - > ...' – 2013-04-25 13:41:21

+0

爲什麼它很醜? – 2013-04-25 13:42:15

2

您可以使用GADT做到這一點:

如果你定義了這樣一個類型expr

type _ expr = 
    | Document: document -> document expr 
    | String: string -> string expr 
    | Double: float -> float expr 

您可以編寫一個函數get這樣的:

let get : type v. v expr -> v = function 
    Document doc -> doc 
| String s -> s 
| Double d -> d 
+0

對不起,我的意思是使用get_type樣式,因爲我希望用戶明確地確定他想要的內容。 – 2013-04-25 13:56:45

0

如果你可以用這些提取功能生活:

let extract_double = function 
    | Double v -> v 
    | _ -> raise Wrong_bson_type 

let extract_string = function 
    | String v -> v 
    | _ -> raise Wrong_bson_type 

let extract_doc = function 
    | Document v -> v 
    | _ -> raise Wrong_bson_type 

然後你可以使用單一產品風格的高階功能,允許你保留你原來的定義get

let return x = Some x 

let (>>=) mx f = 
    match mx with 
    | Some x -> f x 
    | None -> None 

let get_with exf ename doc = 
    (get ename doc) >>= fun v -> 
    return (exf v) 

let get_double = get_with extract_double 
let get_string = get_with extract_string 
let get_doc = get_with extract_doc 

減少紅色undant並將副作用抽象爲泛型綁定和返回操作。

1

隨着GADTs:

type _ asked = 
| TDouble : float asked 
| TString : string asked 
| TDocument : document asked 

let get : type v. v asked -> string -> doc StringMap.t -> v option = 
    fun asked ename doc -> 
    try 
    Some (match asked, StringMap.find ename doc with 
     | TDouble, Double f -> f 
     | TString, String s -> s 
     | TDocument, Document d -> d) 
    with Not_found -> None 

let get_double = get TDouble 
let get_string = get TString 
let get_document = get TDocument