在列表成员实例化时不会传播约束

Wou*_*eek 5 parsing prolog clpfd

我正在为日期和时间构建解析器和生成器.在普通的编程语言中,这些将分开编写.在Prolog + CLP(FD)中我可以编写1个谓词同时执行这两个操作:-)

在我的用例中,解析多个数字并将其转换为整数或根据给定的整数生成多个数字通常是有意义的.

我的问题是,clpfd:run_propagator/2在实例化个别数字时不会调用,尽管我的声明使用了clpfd:init_propagator/2.有没有办法做到这一点,还是我在定义中犯了错误clpfd_digits/2

在SWI-Prolog中实施的代码:

:- use_module(library(apply)).
:- use_module(library(clpfd)).

:- multifile(clpfd:run_propagator/2).

day(D) --> {clpfd_digits(D, [D1,D2])}, digit(D1), digit(D2).

digit(D) --> [C], {code_type(C, digit(D))}.

clpfd_digits(N, Ds):-
  clpfd:make_propagator(clpfd_digits(N, Ds), Prop),
  clpfd:init_propagator(N, Prop),
  clpfd:init_propagator(Ds, Prop),
  forall(
    member(D, Ds),
    clpfd:init_propagator(D, Prop)
  ),
  clpfd:trigger_once(Prop).

clpfd:run_propagator(clpfd_digits(N, Ds), MState):-
  (   maplist(is_digit0, Ds)
  ->  clpfd:kill(MState),
      digits_to_nonneg(Ds, N)
  ;   integer(N)
  ->  clpfd:kill(MState),
      nonneg_to_digits(N, Ds)
  ;   true
  ).

digits_to_nonneg([], 0):- !.
digits_to_nonneg(Ds, N):-
  maplist(char_weight, Chars, Ds),
  number_chars(N, Chars).

char_weight(Char, D):-
  char_type(Char, digit(D)).

nonneg_to_digits(0, []):- !.
nonneg_to_digits(N, Ds):-
  atom_chars(N, Chars),
  maplist(char_weight, Chars, Ds).

is_digit0(D):- integer(D), between(0, 9, D).
Run Code Online (Sandbox Code Playgroud)

使用示例:

?- string_codes("12", Cs), phrase(day(D), Cs).
Cs = [49, 50],
clpfd_digits(D, [1, 2]).
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,约束不会计算为派生值D.

mat*_*mat 4

+1 使用 CLP(FD) 约束来完成此任务!

forall/2和约束不能很好地混合,因为回溯会撤销发布的约束。

您的示例按预期工作:

flip_init(Prop, D) :- clpfd:init_propagator(D, Prop).

并使用maplist(flip_init(Prop), Ds)代替forall/2.

下一个问题是digits_to_nonneg([1,2], N)简单地失败,但这与实际的约束触发无关,这会按预期发生。(顺便说一句:使用约束,您可以简化代码,以便可以在两个方向上使用单个谓词。)

另外,您可以使用:in/2代替。如果您想将其用作约束而不仅仅是测试,这通常很有用。between/3D in 0..9