【问题标题】:FsCheck Generator for List with Pairwise Restrictions (C#)具有成对限制的列表的 FsCheck 生成器 (C#)
【发布时间】:2018-10-23 05:48:52
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 生成合法元组,然后将元组扁平化为字符串序列是否有意义?

标签: c# fscheck


【解决方案1】:

假设您有identifierkeyword 类型均为Gen<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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-25
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多