2010-07-03 57 views
2

我使用的代碼目前的格局了很多程序,我目前正在寫:創建和使用功能的註冊表在F#

let test_titles = ["a_fault"; "b_fault"; "c_fault"] 
let tests: (unit -> 'component option) list = [a_fault; b_fault; c_fault] 
let test_registry = List.zip test_titles tests 
let apply_test (title, test) = test() |> Option.map (fun s -> (title, s)) 
let result: (string * 'component) option = test_registry |> List.tryPick apply_test 

擁有的識別故障的組件和錯誤測試的測試註冊表類型恰好與函數的名稱相同。

  1. 有沒有更好的方法來創建這個test_registry,最好是沒有我手動編寫測試名稱(動態獲取函數名稱)?

  2. 一般來說,這是否是慣用的F#?

編輯:代碼必須在最後一行是錯誤的。結果使用test_registry而不是tests來計算。

回答

1

避免需要在代碼中明確寫入測試名稱(作爲字符串)的一種方法是使用引號。您可以創建一個「帶引號」的函數值列表,而不是創建函數列表和字符串列表。然後,您可以編寫處理引文的代碼,併爲您提供所需的一切。

我假設你的測試看起來大致如下(函數取單位並返回一些值作爲結果)。該列表將被構造如下:

let test_a() = Some 32 
let test_b() = None 
let tests = [ <@ test_a @>; <@ test_b @> ] 

然後,你可以寫這樣的代碼來獲取有關考試的信息:

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Quotations.Patterns 

let getInfo (e:Expr<unit -> 'R>) = // ' 
    match e.Raw with 
    // This expects that the quotation contains a reference to a global 
    // function (in some module) that takes unit as the parameter 
    | Lambda(a, Call(_, m, _)) -> 
     // Return a tuple containing a name of the test (string) and 
     // a function that invokes it (note that the invocation will be 
     // a bit slow as we use reflection here) 
     m.Name, (fun() -> m.Invoke(null, [| |]) :?> 'R) // ' (*) 
    // Ohter quotations will cause an exception 
    | _ -> failwith "unexpected quotation" 

下面是一個例子,你將如何使用:

​​

或者,您可以修改行(*)以生成一個函數,該函數返回測試名稱和結果,從而不需要Option.map

// An alternative version of the marked line in the 'getInfo' function 
(fun() -> m.Name, m.Invoke(null, [| |]) :?> 'R) // ' (*) 

// Then you can write just: 
tests |> List.map getInfo |> List.tryPick (fun test -> test()) 
+0

謝謝。它使我花了一段時間才明白,但我得到了它的工作。對於那些將來查看答案的人,考慮到我編輯了問題,「備選」之後的部分目前無效。 – 2010-07-05 10:46:29

2

這看起來不錯,但要考慮的另一個選擇是讓測試知道他們自己的名字。現在你有(我推斷)

type Test<'comp> = unit -> 'comp option 

,你可以有替代

type Test<'comp> = unit -> string * 'comp option 

其中字符串名稱。

我沒有很好的理解你的'大圖片'來提供其他建議或知道這是否合理。

如果測試是某個模塊M的一部分,您可以巧妙地使用(?)操作符,例如, M?foo可以訪問測試功能及其名稱。

+0

我試圖避免重複,或者在'let test1(x)= Some(「test1」,x)'或者在外部列表中將它們作爲問題。 '?'運算符的想法對我來說看起來不錯。你的意思是它應該被定義爲返回字符串和實際功能?如果是這樣,怎麼樣?我已經在一個新的問題分裂這個: http://stackoverflow.com/questions/3178829/f-dynamic-operator-giving-access-both-to-function-and-function-name – 2010-07-05 10:41:50