简体   繁体   English

Prolog初学者:如何为谓词中的每个变量制作唯一的值

[英]Prolog Beginner: How to make unique values for each Variable in a predicate

I have a prolog predicate: 我有一个序言谓词:

Add( [A|B] , Answer ) :-
    ...
    ~ Add everything in the list to come up with answer
    ...

I would now like to implement AddUnique that would return unique values for everything in the list except when I give it the variable twice. 我现在想实现AddUnique ,它将为列表中的所有内容返回唯一值, 除非我两次给变量赋值。


Here are somethings that are logically equivalent: 以下是逻辑上等效的内容:

?- AddUnique([A, B, C], 10). is equivalent to: ?- Add([A, B, C], 10), A != B, B != C, A != C. 等效于: ?- Add([A, B, C], 10), A != B, B != C, A != C.

And: 和:

?- AddUnique([A, B, B], 10). is equivalent to: ?- Add([A, B, B], 10), A != B. 等价于: ?- Add([A, B, B], 10), A != B.

Also: 也:

?- AddUnique([A, B, B], 10). is NOT equivalent to: ?- Add([A, B, B], 10), A != B, B!=B. 等于: ?- Add([A, B, B], 10), A != B, B!=B.


If ?- AddUnique([A,B,C,D], 4). 如果?- AddUnique([A,B,C,D], 4). is given it should return false since it cannot come with unique positive integers that add to four. 给定它应该返回false,因为它不能带有加到4的唯一正整数。

If ?- AddUnique([A,A,A,A], 4). 如果?- AddUnique([A,A,A,A], 4). is given it should return A=1 . 给定它应该返回A=1


Question : How can I move the A != B, B != C, A != C. logic inside the predicate without doing something like this A != A ? 问题 :如何在谓词内部移动A != B, B != C, A != C.逻辑而无需执行类似A != A

Khm... You should understand that doStuff(A,B,C,D) and doStuff(A,A,B,B) means. Khm ...您应该了解doStuff(A,B,C,D)doStuff(A,A,B,B)意思。 First is going to unify values A .. D with appropriate values which makes doStuff/4 reachable goal. 首先是要统一值A .. D与这使得适当的值doStuff/4到达目标。 And second is equal to A=B, C=D, doStuff(A,B,C,D) and doStuff(A,B,C,D), A=B, C=D (but last variant probably will cause backtracking). 第二个等于A=B, C=D, doStuff(A,B,C,D)doStuff(A,B,C,D), A=B, C=D (但最后一个变体可能会导致回溯)。 So I hope you understand that unique/1 shouldn't be done inside doStuff/4 , because it's outside restriction. 因此,我希望您理解不要在doStuff/4内完成unique/1 ,因为它超出了限制。 So you shoulad use doStuff(A,B,C,D), unique([A,B,C,D]) and doStuff(A,A,B,B), unique([A,B]) . 因此,您应该使用doStuff(A,B,C,D), unique([A,B,C,D])doStuff(A,A,B,B), unique([A,B])

I wonder how you read A is not B ... Anyway you can define unique/1 as 我不知道您如何读A is not B ...无论如何,您可以将unique/1定义为

not_unique([H|T]):- member(H, T) ; not_unique(T).
unique(L):- not(not_unique(L)).

Given your description of the addUnique/2 predicate, constraint logic programming can be used to implement a solution. 给定您对addUnique/2谓词的描述,可以使用约束逻辑编程来实现解决方案。 This is far from beginner stuff, but I'll post an explanation anyway. 不是初学者的东西,但是我还是会发布一个解释。

Firstly, it might be worthwhile looking up what constraint logic programming is, and an how to use an implementation (eg, SWI-PL clpfd ). 首先,可能有必要查找什么是约束逻辑编程,以及如何使用实现(例如SWI-PL clpfd )。 Basically, constraint logic programming (particularly the finite domain solver) will allow you to specify the following constraints over your variables on the input list to addUnique/2 : 基本上,约束逻辑编程(尤其是有限域求解器)将允许您在输入列表上的变量上为addUnique/2指定以下约束:

  1. How the variables on your input list can bind to certain numeric values (ie, integers from 0 up to and including a specified value) 输入列表中的变量如何绑定到某些数值(即,从0到指定值的整数)
  2. How the different variables on your input list cannot simultaneously bind to the same value (ie, != where different) 输入列表中的不同变量如何不能同时绑定到相同的值(即,!=不同处)
  3. How the sum of the variables in the input list plus any numbers must add up to a specified value (ie, the maximum value that variables can take on in 1, above). 输入列表中的变量加上所有数字的总和如何必须等于指定的值(即,变量可以在上面的1中取的最大值)。

Together, these specifications will allow the underlying constraint solver to automatically determine what permissible values the variables can simultaneously take on given the above constraints, giving you your solutions (there may be several, one, or none). 这些规范一起使基础的约束求解器可以自动确定在上述约束条件下变量可以同时采用的允许值,从而为您提供解决方案(可能有多个,一个或没有)。

Here is a solution using the aforementioned constraint solver in SWI-PROLOG (the clpfd solver): 这是在SWI-PROLOG中使用上述约束求解器(clpfd求解器)的解决方案:

:- use_module(library(clpfd)).  % import and use the constraint solver library

addUnique([A|As], Val) :-
    unique_vars([A|As], UVs),  % determine all unique variables
    UVs ins 0..Val,            % (1) domain of all unique variables is 0 to Val
    pairwise_inequ(UVs),       % (2) all unique variables are pairwise !=
    construct_sum_constr(As, Val, A, Program),  % (3) construct the sum constraint
    Program,              % assert the sum constraint
    label(UVs).           % label domains to enumerate a solution (backtracks)

% predicate to return a list of unique vars, if present
unique_vars([], []).
unique_vars([V|Vs], [V|Uniq]) :-
    var(V),
    \+ var_memberchk(V, Vs), !,
    unique_vars(Vs, Uniq).
unique_vars([_|Vs], Uniq) :-
    unique_vars(Vs, Uniq).

% predicate to test if a variable appears in a list (possibly including variables)
var_memberchk(V0, [V1|_]) :- 
    V0 == V1, !.
var_memberchk(V0, [_|V1s]) :- 
    var_memberchk(V0, V1s).

% create constraints that assert each in the input list != to each other
pairwise_inequ([]).
pairwise_inequ([V|Vs]) :-
    map_inequ(Vs, V),
    pairwise_inequ(Vs).

% predicate to pairwise assert inequality between all list members
map_inequ([], _).
map_inequ([V1|V1s], V0) :-
    V0 #\= V1,   % the inequality constraint
    map_inequ(V1s, V0).

% predicate to construct a summation constraint, whereby all variables in the 
% input list are constructed into a sum with +/2 and finally equated to a value
construct_sum_constr([], Val, Sum, (Sum #= Val)).
construct_sum_constr([V|Vs], Val, Sum, Program) :-
    construct_sum_constr(Vs, Val, (V + Sum), Program).

Running this code, eg, gives you: 运行此代码,例如,将为您提供:

?- addUnique([A,B,B], 6).
A = 0,
B = 3 ;
A = 4,
B = 1 ;
A = 6,
B = 0.

; enumerates the next solution for permissible bindings between variables. 列举了变量之间允许的绑定的下一个解决方案。 Note that A and B never take on the same value, as required, but all occurrences in the input list will always sum to 6 . 请注意, AB绝不会根据需要采用相同的值,但是输入列表中所有出现的内容总和为6 Another query: 另一个查询:

 ?- addUnique([A,A,A],4).
false.

The result is failure here because no single integer could be found to bind to A that, when summed up, totaled 4 , whereas: 结果是失败的,因为找不到单个整数来绑定到A ,该A加起来总计为4 ,而:

 ?- addUnique([A,A,A,A],4).
A = 1.

...as expected. ...符合预期 Also, you wanted to try: 另外,您想尝试:

?- addUnique([A,B,C,D],4).
false.

Again, the result is failure here because all the variables A , B , C and D were all asserted to be different, and cannot all bind to 1 . 同样,这里的结果是失败的,因为所有变量ABCD都被断言为不同,并且不能全部绑定为1

EDIT ps. 编辑 PS。 ony also wanted to try: ony也想尝试:

?- addUnique([A,A,A,1],4).
A = 1.

A simple modification to the code above ensures that only variables are used when calling ins to assert domains (and not any numbers in the input list). 对上述代码的简单修改可确保在调用ins声明域时仅使用变量(而不是输入列表中的任何数字)。

This is the solution that I came up with. 这是我想出的解决方案。 It will only assign the input to be numbers less than ten but works great for that! 它将只将输入分配为小于10的数字,但效果很好!

addUnique( A, Answer ) :- 
    used(A,[0,1,2,3,4,5,6,7,8,9],_),
    add(A,Answer).

add( [A|B] , Answer ) :-
    ~ Add everything in the list to come up with answer ~.


% ================================
% Ensures that all variables are unique.  
% ================================

% Base case: Assigned variables unique values
used([], Nin, Nin).

% Have already assigned a value to this variable
used([A|B], Nin, Nout) :-
        integer(A),
        helper(B,Nin,Nout).

% Have not assigned a value to this variable yet
% Assign it and remove it from the list.  
used( [A|B] , Nin, Nout) :-
        member(A,Nin),
        delete(Nin,A,Temp),
        helper(B,Temp,Nout).

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM