如何使用多个规则以声明方式转换 Prolog 中的数据结构

How to transform data structures in Prolog using multiple rules, declaratively

我想找到一种在给定 constraints/transformation 规则集的 Prolog 中转换数据结构的方法。

励志榜样

假设我们要validate/enrichSQL根据一些规则进行查询。 我们只对简单的 SQL 查询感兴趣,现在只考虑 WHERE/GROUP BY 子句,形式为 WHERE filter(a) AND filter(b) AND ... GROUP BY c,d,...(其中 abcd 是列名)。

此类查询的模型可能如下所示:

[filter(a), filter(b), group(c), group(d)]

我们可能有这样的规则:

一些规则显然是“冲突的”(例如,如果我们添加强制分组,我们可能会超过分组的最大数量,并且将不得不产生多个解决方案或根本不产生解决方案,具体取决于特定规则)。

我试过的

到目前为止我只能想出这样的东西:

transform(In, Out) :-
  has_mandatory_grouping(In, Out1),
  limited_grouping_count(Out1, Out2),
  another_rule(Out2, Out3),
  ...
  last_rule(OutN, Out).
has_mandatory_grouping(In, Out) :- contains(In, group(a)) -> Out = In; Out = [group(a)|In].
limited_grouping_count(In, Out) :- (length(In, L), L > 2) -> subset2(Out, In), Out = [_,_], contains(Out, group(a)); Out = In.
...

所以基本上是一组有序的步骤,每个步骤都应用一些逻辑来生成要传递给下游的新结构。

我不喜欢这种方法的地方

其他想法

我也考虑过 CHR,但由于它的承诺选择性质,我看不出它如何用于需要解决方案分支的任务(探测 - 生成和修剪 - 替代解决方案)。

可能可以通过模拟多个约束存储的存在来支持分支(通过将特殊的“分支”参数附加到所有 constraints/terms),但这绝对超出了我的能力范围。

问题

有没有办法在 Prolog 中对这些规则进行建模,以便代码比非 LP 语言更具表现力?

或者我应该考虑其他系统(从 CLP 开始)?

CHR 更新

基于 CHR 和回溯(@Isabelle Newbie 建议)的解决方案有效,但生成的规则可能仍然缺乏可组合性(例如 post([group(c), group(d)]) 不会终止,因为不同的规则不断相互触发)。

虽然可以通过仔细编码和测试 CHR 来临时解决它,但也许一些更高级别的技术可以提供帮助?就像我们可以设置约束一样:

|groupings| =< 2
(a ∈ groupings) OR (a ∈ filters)
max(|groupings ⋂ original_groupings|)

并让机器找到变体。

我想原则上是可以的,但它是否是开发生产系统的可行方法?也许我应该为此创建一个单独的问题。

我认为 CHR 是一种合理的方式。您 可以 使用 CHR 探索替代解决方案,因为规则的右侧,而 通常 只是约束条件,实际上可以是任意的 Prolog 目标。这包括析取。例如:

:- chr_constraint go/0, a/0, b/0, c/0.

go <=> (a ; b ; c).

这确实探索了回溯的替代方案:

?- go.
a ;
b ;
c.

确实,使用这种表示形式很难强制执行“最多 abc 中的两个,但要探索替代方案”约束。为此,您可以使用带有列表的表示形式来表示您的“约束存储内的约束存储”,如果您愿意:

:- chr_constraint store/1.

at_most_two @
    store(Store)
    <=>
    Store = [_, _, _ | _]
    |
    store_2(Store).

store_2(Store) :-
    select(X, Store, Store1),
    select(Y, Store1, _Store2),
    store([X, Y]).
    
go :-
    store([a, b, c]).

你得到:

?- go.
store([a, b]) ;
store([a, c]) ;
store([b, a]) ;
store([b, c]) ;
store([c, a]) ;
store([c, b]) ;
false.

所以你可以以这种方式执行“最多两个”规则。

综上所述,这是给定问题的解决方案草图。一、预赛:

:- chr_constraint group/1.
:- chr_constraint grouping/1.

:- chr_constraint filter/1.
:- chr_constraint filters/1.

post_filter @
    filter(Filter),
    filters(Filters)
    <=>
    filters([Filter | Filters]).

post_group @
    group(Group),
    grouping(Groups)
    <=>
    grouping([Group | Groups]).

为了更严谨一点,这可能应该使用 sort/2 来排除重复项。

您的大部分约束都相当直接:

column_a_present @
    grouping(Grouping),
    filters(Filters)
    <=>
    \+ member(a, Grouping),
    \+ member(a, Filters)
    |
    (   grouping([a | Grouping]),
        filters(Filters)
    ;   grouping(Grouping),
        filters([a | Filters]) ).

column_a_not_present_twice @
    grouping(Grouping),
    filters(Filters)
    <=>
    member(a, Grouping),
    member(a, Filters)
    |
    false.

not_more_than_two_grouping @
    grouping(Grouping)
    <=>
    Grouping = [_, _, _ | _]
    |
    select_2_groups(Grouping).

select_2_groups(Grouping) :-
    SubGrouping = [X, Y],
    select(X, Grouping, Grouping1),
    select(Y, Grouping1, _Grouping2),
    grouping(SubGrouping).

no_column_in_filter_and_grouping @
    grouping(Grouping),
    filters(Filters)
    <=>
    common_column(Grouping, Filters)
    |
    no_common_column(Grouping, Filters).

common_column(Grouping, Filters) :-
    common_column(Grouping, Filters, _Column).

common_column(Grouping, Filters, Column) :-
    member(group(Column), Grouping),
    member(filter(Column), Filters).

no_common_column(Grouping, Filters) :-
    common_column(Grouping, Filters, Column),
    (   select(group(Column), Grouping, Grouping1),
        Filters1 = Filters
    ;   Grouping1 = Grouping,
        select(filter(Column), Filters, Filters1) ),
    grouping(Grouping1),
    filters(Filters1).

“无公共列”的情况有点不雅,但严格来说,您不允许在保护子句中绑定变量(尽管它往往适用于 SWI-Prolog 的 CHR 实现),因此有些工作是在后卫和球门之间复制。

最后,发布所有约束的主要谓词。这也是最容易处理“按列 a 添加默认分组”案例的地方:

is_group(group(_)).

is_filter(filter(_)).

post(Constraints) :-
    include(is_group, Constraints, Groups0),
    (   Groups0 = []
    ->  Groups = [group(a)]
    ;   Groups0 = Groups ),
    include(is_filter, Constraints, Filters),
    maplist(call, Groups),
    maplist(call, Filters),
    grouping([]),
    filters([]).

并以此发布一组专栏,您可以回溯所有有效的备选方案:

?- post([filter(a), filter(b), filter(c), group(d), group(e), group(f)]).
grouping([d, e]),
filters([a, b, c]) ;
grouping([d, f]),
filters([a, b, c]) ;
grouping([e, d]),
filters([a, b, c]) ;
grouping([e, f]),
filters([a, b, c]) ;
grouping([f, d]),
filters([a, b, c]) ;
grouping([f, e]),
filters([a, b, c]) ;
false.