简体   繁体   English

轻松获取F#中的函数名称

[英]Easily getting function names in F#

While searching for an answer, I've found several questions that don't quite match my situation — so I'll ask a new one. 在寻找答案时,我发现了几个与我的情况不太匹配的问题 - 所以我会问一个新问题。

I am writing some FsCheck tests for a data structure, where I want to check about twenty different properties on each copy of the data structure I construct. 我正在为数据结构编写一些FsCheck测试,我想在我构建的数据结构的每个副本上检查20个不同的属性。 What I've done so far is to write a predicate for each property, then I've made a list of the predicates and I'll call them each in turn with List.forall , like so: 到目前为止我所做的是为每个属性编写一个谓词,然后我创建了一个谓词列表,我将依次用List.forall调用它们,如下所示:

module PropertyChecks =
    let ``Tail length plus tree length should equal vector length`` vec =
        treeLength vec.root + Array.length vec.tail = vec.len

    let ``The tail may only be empty iff the vector is empty`` vec =
        (vec.len = 0) = (Array.length vec.tail = 0)

    let ``The tail's length may not exceed tailMax`` vec =
        Array.length vec.tail < tailMax

    let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
        (vec.len > tailMax) || (Array.length vec.root = 0)

    // And so on for about 15 more predicates

    let allProperties =
      [
        ``Tail length plus tree length should equal vector length``
        ``The tail may only be empty iff the vector is empty``
        ``The tail's length may not exceed tailMax``
        ``If vec.len <= tailMax, all items are in tail and root is empty``
      ]

    let checkProperties vec =
        allProperties |> List.forall (fun pred -> pred vec)

    // Rest of my code omitted since it's not relevant to the question

The problem I'm facing is that I expect that when one property fails because I didn't construct the data structure properly, two or three other properties will fail at the same time. 我面临的问题是,我希望当一个属性因为我没有正确构造数据结构而失败时,两个或三个其他属性将同时失败。 I'd like to get a list of all failing properties, which means that in checkProperties I'd like to extract the name of the failing predicate. 我想获得所有失败属性的列表,这意味着在checkProperties我想提取失败谓词的名称 I've seen multiple answers along the lines of "You can't get the MethodInfo from an arbitrary F# function that you received as an argument, because you never know whether you got the function itself, or a lambda, or an anonymous function". 我已经看到了多个答案:“你无法从作为参数接收的任意F#函数中获取MethodInfo ,因为你永远不知道你是否拥有函数本身,或者是lambda,还是匿名函数” 。 But here, I not only know that I have real functions, I know what their names are. 但在这里,我不仅知道我有真正的功能,我知道他们的名字是什么。 I could easily copy and paste their names into strings, and make allProperties a list of (string,function) tuples. 我可以轻松地将其名称复制并粘贴到字符串中,并使allProperties成为(字符串,函数)元组的列表。 But I'm already feeling not quite happy with having copied-and-pasted once (to put the predicates into that list), and I'd rather not do it twice. 但是我已经对复制和粘贴一次(将谓词放入该列表)感到不满意,而我宁愿不做两次。

What's a better solution that making a list of (function name, function) tuples? 制作(函数名称,函数)元组列表的更好的解决方案是什么? Could I move allProperties and checkProperties out of the PropertyChecks module so that it contains nothing but predicates, and then use reflection to walk that module? 我可以将allPropertiescheckProperties移出PropertyChecks模块,以便它只包含谓词,然后使用反射来遍历该模块吗?

I'm very new to the entire .Net reflection system, so I might well be missing something obvious. 我对整个.Net反射系统都很陌生,所以我很可能会遗漏一些明显的东西。 Please feel free to point out the obvious if I'm missing it; 如果我错过了,请随时指出明显的; I won't feel insulted. 我不会感到被侮辱。

The best option for running FsCheck tests is to use FsCheck together with some unit test runner. 运行FsCheck测试的最佳选择是将FsCheck与一些单元测试运行器一起使用。 The test runner takes care of finding all the properties, running them and printing nice error logs when something goes wrong. 测试运行器负责查找所有属性,运行它们并在出现问题时打印漂亮的错误日志。

The two most common test runners in .NET that both work with FsCheck are xUnit and NUnit and the FsCheck documentation describes how to use them: .NET中与FsCheck一起使用的两个最常见的测试运行器是xUnit和NUnit,FsCheck文档描述了如何使用它们:

In both cases, you mark the properties with the [<Property>] attribute and the test runner will use this to find them and run them using FsCheck for you. 在这两种情况下,使用[<Property>]属性标记属性,测试运行器将使用它来查找它们并使用FsCheck为您运行它们。 So you'll need something like: 所以你需要这样的东西:

[<Property>]
let ``Tail length plus tree length should equal vector length`` vec =
    treeLength vec.root + Array.length vec.tail = vec.len

[<Property>]
let ``The tail may only be empty iff the vector is empty`` vec =
    (vec.len = 0) = (Array.length vec.tail = 0)

[<Property>]
let ``The tail's length may not exceed tailMax`` vec =
    Array.length vec.tail < tailMax

[<Property>]
let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
    (vec.len > tailMax) || (Array.length vec.root = 0)

Here is a hacky bash script that might be easier than reflection: 这是一个hacky bash脚本,可能比反射更容易:

#!/bin/bash
echo "let pairs = [|";
cat t.fs | grep let | grep -o "\`\`[^\`]*\`\`" | tr -d \` | awk '{printf "    (\"%s\",``%s``);\n", $0,$0}';
echo "|]"

which gives: 这使:

let pairs = [|
    ("Tail length plus tree length should equal vector length",``Tail length plus tree length should equal vector length``);
    ("The tail may only be empty iff the vector is empty",``The tail may only be empty iff the vector is empty``);
    ("The tail's length may not exceed tailMax",``The tail's length may not exceed tailMax``);
    ("If vec.len <= tailMax, all items are in tail and root is empty",``If vec.len <= tailMax, all items are in tail and root is empty``);
|]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM