具有成对限制(C#)的列表的FsCheck生成器

时间:2018-10-07 03:24:22

标签: c# fscheck

在C#中使用FsCheck,我需要生成一个值列表,其中某些值可能不会彼此相邻出现。这是词法分析器的标记列表。因此,例如,我不需要生成彼此相邻的两个标识符,也不必在标识符旁边生成关键字,因为当它们变成字符串时,词法分析器会将其视为一个标记。我不能简单地做类似的事情:

Gen.ListOf(Gen.Elements("fizz", "buzz", "bazz", "+", " ", "if")).Where(list => ...);

在合理的长度列表中,很可能有两个不应相邻的标记。因此,过滤器将丢弃太多的值,并且生成将非常缓慢。我已经考虑过添加或删除令牌来尝试修复列表,但这似乎非常复杂,而且我担心它会生成太短或太长的列表。

我想做的是生成列表“一次一个元素”。因此,我将随机选择长度,然后根据是否允许跟随前一个元素随机选择每个元素,直到获得完整列表为止。这似乎应该可以递归执行,但是我不知道如何使用生成器组合器来表达它。有没有办法做到这一点?我希望我可以编写一个任意的lambda,它可以直接调用其中的生成器,而不需要组成它们。这样有可能吗?

注意:我已经简化了示例。令牌的生成要复杂得多。

1 个答案:

答案 0 :(得分:0)

假设您同时拥有identifier类型的keywordGen<string>,则可以执行类似(C#-y伪代码)

var any = Gen.Elements (new[] {identifier, keyword});

Gen<ImmutableList<string>> Generate(int length, Gen<ImmutableList<string>> soFar) {
    if (length == 0) return soFar;

    var result = soFar.SelectMany(l =>  {
        // after a keyword anything goes, after an identifier must follow a keyword    
        var noId = ... // check what last token in l is
        var next = noId ? keyword : any;
        return next.SelectMany(n => l.Add(n));
    });

    return Generate(length-1, result);
}

您还可以创建一个枚举值+字符串的列表,以便枚举值定义您所生成的令牌的种类(从您刚生成的文本中找出令牌的种类对我来说有点奇怪...听起来也正是您要测试的代码。

通常,我可能会建议一种不同的方法,这取决于您拥有多少令牌以及所有约束是什么。在这种情况下,通常更容易定义一些“模式”,即合法的标记列表,您可以在不中断的情况下一个接一个地串(太多,可能在工作后做另一个过滤器)。在F#中的示例如下:https://github.com/fsprojects/fantomas/blob/master/src/Fantomas.Tests/FormattingPropertyTests.fs

这些模式通常最终都是您的语法或语法树,因此基本结构应该很简单。