C# 表达式访问者,如何否定构建过滤器
C# Expression visitor, how to negate build filters
我正在为第三方构建自己的 IQuerable 实现 api。
此 Api 接受过滤器作为包含 AND 语句和过滤器列表的 OR 列表,如下所示:
public class Or {
List<And> ands
}
public class And {
field, operator, value..
}
Filters = new List<Or>();
现在构建这些过滤器很顺利,每当我有一个 or 语句时,我都会在 or 上展开所有当前过滤器,每当我得到和语句时,我都会将它添加到所有 or 中。这些似乎工作正常,除了现在每当我在多个字段上有一个一元非表达式时,我就会迷路。
说我有:(a and b) or (c and d)
这将变成过滤器:
a, b // list of ands, vertical list of or
c, d
否定这将导致:(!a or !b) and (!c or !d)
!a, !c
!a, !d
!b, !c
!b, !d
这仍然是可能的,但我想弄清楚的是,如果它被双重否定,我将如何恢复它,这将再次导致 (a and b) or (c and d)
。但我似乎无法弄清楚。也许我应该在将它们变成这些和/或列表之前使用不同的中间过滤器结构。我怎样才能做到这一点?
当你反转你的例子时,它会变成一个更大的列表,正如你所注意到的:
!((a & b) | (c & d))
!(a & b) & !(c & d) // De Morgan's laws
(!a | !b) & (!c | !d) // De Morgan's laws
(!a & !c) | (!a & !d) | (!b & !c) | (!b & !d) // Normalization
当你再次反转它时,列表会变得更大:
!((!a & !c) | (!a & !d) | (!b & !c) | (!b & !d))
!(!a & !c) & !(!a & !d) & !(!b & !c) & !(!b & !d) // De Morgan's laws
(!!a | !!c) & (!!a | !!d) & (!!b | !!c) & (!!b | !!d) // De Morgan's laws
(a | c) & (a | d) & (b | c) & (b | d) // Double negation
(a & a & b & b) | (a & a & b & d) | (a & a & c & b) | (a & a & c & d) | (a & d & b & b) | (a & d & b & d) | (a & d & c & b) | (a & d & c & d) | (c & a & b & b) | (c & a & b & d) | (c & a & c & b) | (c & a & c & d) | (c & d & b & b) | (c & d & b & d) | (c & d & c & b) | (c & d & c & d) // Normalization
哇,最后一行真长!你会注意到一些连词从句有重复,即 a & a & b & b
。所以,第一步是删除重复的谓词:
(a & b) | (a & b & d) | (a & c & b) | (a & c & d) | (a & d & b) | (a & d & b) | (a & d & c & b) | (a & d & c) | (c & a & b) | (c & a & b & d) | (c & a & b) | (c & a & d) | (c & d & b) | (c & d & b) | (c & d & b) | (c & d)
现在我们可以对每个连词中的谓词进行排序,并删除重复的连词:
(a & b) | (a & b & c) | (a & b & c & d) | (a & b & d) | (a & c & d) | (b & c & d) | (c & d)
好了,清零了很多!但是,我们在这个表达式中得到的仍然比我们开始时更多。但是,如果您仔细观察,其中一些连词是多余的 - 例如a & b & c => a & b
。因此,如果一个连词是另一个连词的 super-set,我们可以将其删除:
(a & b) | (c & d)
原来的条款!由于您没有在问题中包含任何代码,因此我不会在我的答案中包含任何代码 - 执行上述步骤取决于您。但是,我建议您简化模型:
public class Predicate
{
public string Field { get; set; }
public Operator Operator { get; set; }
public string Value { get; set; }
}
public enum NormalForm
{
Conjunctive,
Disjunctive
}
public class Filter
{
public NormalForm NormalForm { get; set; }
public List<List<Predicate>> Predicates { get; set; }
}
这是一种更灵活的表示形式,将使反演更容易,因为在应用德摩根定律后,您最终会得到合取范式,并且必须转换回来。
我正在为第三方构建自己的 IQuerable 实现 api。
此 Api 接受过滤器作为包含 AND 语句和过滤器列表的 OR 列表,如下所示:
public class Or {
List<And> ands
}
public class And {
field, operator, value..
}
Filters = new List<Or>();
现在构建这些过滤器很顺利,每当我有一个 or 语句时,我都会在 or 上展开所有当前过滤器,每当我得到和语句时,我都会将它添加到所有 or 中。这些似乎工作正常,除了现在每当我在多个字段上有一个一元非表达式时,我就会迷路。
说我有:(a and b) or (c and d)
这将变成过滤器:
a, b // list of ands, vertical list of or
c, d
否定这将导致:(!a or !b) and (!c or !d)
!a, !c
!a, !d
!b, !c
!b, !d
这仍然是可能的,但我想弄清楚的是,如果它被双重否定,我将如何恢复它,这将再次导致 (a and b) or (c and d)
。但我似乎无法弄清楚。也许我应该在将它们变成这些和/或列表之前使用不同的中间过滤器结构。我怎样才能做到这一点?
当你反转你的例子时,它会变成一个更大的列表,正如你所注意到的:
!((a & b) | (c & d))
!(a & b) & !(c & d) // De Morgan's laws
(!a | !b) & (!c | !d) // De Morgan's laws
(!a & !c) | (!a & !d) | (!b & !c) | (!b & !d) // Normalization
当你再次反转它时,列表会变得更大:
!((!a & !c) | (!a & !d) | (!b & !c) | (!b & !d))
!(!a & !c) & !(!a & !d) & !(!b & !c) & !(!b & !d) // De Morgan's laws
(!!a | !!c) & (!!a | !!d) & (!!b | !!c) & (!!b | !!d) // De Morgan's laws
(a | c) & (a | d) & (b | c) & (b | d) // Double negation
(a & a & b & b) | (a & a & b & d) | (a & a & c & b) | (a & a & c & d) | (a & d & b & b) | (a & d & b & d) | (a & d & c & b) | (a & d & c & d) | (c & a & b & b) | (c & a & b & d) | (c & a & c & b) | (c & a & c & d) | (c & d & b & b) | (c & d & b & d) | (c & d & c & b) | (c & d & c & d) // Normalization
哇,最后一行真长!你会注意到一些连词从句有重复,即 a & a & b & b
。所以,第一步是删除重复的谓词:
(a & b) | (a & b & d) | (a & c & b) | (a & c & d) | (a & d & b) | (a & d & b) | (a & d & c & b) | (a & d & c) | (c & a & b) | (c & a & b & d) | (c & a & b) | (c & a & d) | (c & d & b) | (c & d & b) | (c & d & b) | (c & d)
现在我们可以对每个连词中的谓词进行排序,并删除重复的连词:
(a & b) | (a & b & c) | (a & b & c & d) | (a & b & d) | (a & c & d) | (b & c & d) | (c & d)
好了,清零了很多!但是,我们在这个表达式中得到的仍然比我们开始时更多。但是,如果您仔细观察,其中一些连词是多余的 - 例如a & b & c => a & b
。因此,如果一个连词是另一个连词的 super-set,我们可以将其删除:
(a & b) | (c & d)
原来的条款!由于您没有在问题中包含任何代码,因此我不会在我的答案中包含任何代码 - 执行上述步骤取决于您。但是,我建议您简化模型:
public class Predicate
{
public string Field { get; set; }
public Operator Operator { get; set; }
public string Value { get; set; }
}
public enum NormalForm
{
Conjunctive,
Disjunctive
}
public class Filter
{
public NormalForm NormalForm { get; set; }
public List<List<Predicate>> Predicates { get; set; }
}
这是一种更灵活的表示形式,将使反演更容易,因为在应用德摩根定律后,您最终会得到合取范式,并且必须转换回来。