2010-07-03 53 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