Why doesn't this clpfd query terminate until I add a redundant constraint?

假装没事ソ 提交于 2020-01-04 04:13:05

问题


I've written some predicates which take the length of a list and attaches some constraints to it (is this the right vocabulary to be using?):

clp_length([], 0).
clp_length([_Head|Rest], Length) :-
  Length #>= 0, Length #= Length1 + 1,
  clp_length(Rest, Length1).

clp_length2([], 0).
clp_length2([_Head|Rest], Length) :-
  Length #= Length1 + 1,
  clp_length2(Rest, Length1).

The first terminates on this simple query, but the second doesn't:

?- Small in 1..2, clp_length(Little, Small).
Small = 1,
Little = [_1348] ;
Small = 2,
Little = [_1348, _2174] ;
false.

?- Small in 1..2, clp_length2(Little, Small).
Small = 1,
Little = [_1346] ;
Small = 2,
Little = [_1346, _2046] ;
% OOPS %

This is strange to me, because Length is pretty clearly greater than 0. To figure that out you could either search, find the zero, and deduce that adding from zero can only increase the number, or you could propagate the in 1..2 constraint down. It feels like the extra clause is redundant! That it isn't means my mental model of clpfd is pretty wrong.

So I think I have two questions (would appreciate answers to the second as comments)

  • Specifically, why does this additional constraint cause the query to work correctly?

  • Generally, is there a resource I can use to learn about how clpfd is implemented, instead of just seeing some examples of how it can be used? I'd prefer not to have to read Markus Triska's thesis but that's the only source I can find. Is that my only option if I want to be able to answer questions like this one?


回答1:


1mo, there is the issue with naming. Please refer to previous answers by mat and me recommending relational names. You won't go far using inappropriate names. So list_length/2 or list_fdlength/2 would be an appropriate name. Thus we have list_fdlength/2 and list_fdlength2/2.

2do, consider the rule of list_fdlength2/2. Nothing suggests that 0 is of relevance to you. So that rule will be exactly the same if you are using 0 or 1 or -1 or whatever as base case. So how should this poor rule ever realize that 0 is the end to you? Better, consider a generalization:

list_fdlength2(fake(N), N) :-  % Extension to permit fake lists
   N #< 0.
list_fdlength2([], 0).
list_fdlength2([_Head|Rest], Length) :-
   Length #= Length1 + 1,
   list_fdlength2(Rest, Length1).

This generalization shows all real answers plus fake answers. Note that I have not changed the rule, I added this alternative fact only. Thus the fake solutions are actually caused by the rule:

?- list_fdlength2(L, 1).
      L = [_A]
   ;  L = [_A, _B|fake(-1)]
   ;  L = [_A, _B, _C|fake(-2)]
   ;  ...

?- list_fdlength2(L, 0).
      L = []
   ;  L = [_A|fake(-1)]
   ;  L = [_A, _B|fake(-2)]
   ;  ...

Each clause tries to contribute to the solutions just in the scope of the clause. But there is no way to derive (by the built-in Prolog execution mechanism) that some rules are no longer of relevance. You have to state that explicitly with redundant constraints as you did.

Now, back to your original solution containing the redundant constraint Length #>= 0. There should not be any such fake solution at all.

list_fdlength(fake(N), N) :-
   N #< 0.
list_fdlength([], 0).
list_fdlength([_Head|Rest], Length) :-
   Length #>= 0,
   Length #= Length1 + 1,
   list_fdlength(Rest, Length1).

?- list_fdlength(L, 1).
      L = [_A]
   ;  L = [_A, _B|fake(-1)]   % totally unexpected
   ;  false.

?- list_fdlength(L, 0).
      L = []
   ;  L = [_A|fake(-1)]       % eek
   ;  false.

There are fake answers, too! How ugly! At least, they are finite in number. But, you could have done it better by using Length #>= 1 in place of Length #>=0. With this little change, there are no longer any fake solutions when N is non-negative and thus also your original program will be better.



来源:https://stackoverflow.com/questions/50992433/why-doesnt-this-clpfd-query-terminate-until-i-add-a-redundant-constraint

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!