Prolog 用一个变量替换另一个变量

Prolog 用一个变量替换另一个变量,prolog,Prolog,给定一个列表列表,我想替换Bs中的所有as,其中a和B是prolog变量 例如,如果列表是[[1,2,D,C],[A,D],[4,A],[1,2,A]] 我希望结果是[[1,2,D,C],[B,D],[4,B],[1,2,B]] 为此,我编写了以下内容,以替换单个列表中Bs中的所有As: /*substitude_single(+OldVar,+NewVar,+OldList,-NewList) */ substitute_single(_,_,[],[]). substitute_singl

给定一个列表列表,我想替换
B
s中的所有
a
s,其中
a
B
是prolog变量

例如,如果列表是
[[1,2,D,C],[A,D],[4,A],[1,2,A]]
我希望结果是
[[1,2,D,C],[B,D],[4,B],[1,2,B]]

为此,我编写了以下内容,以替换单个列表中
B
s中的所有
A
s:

/*substitude_single(+OldVar,+NewVar,+OldList,-NewList) */
substitute_single(_,_,[],[]). 
substitute_single(A,B,[A|As],[B|Bs]):-
   substitute_single(A,B,As,Bs).
substitute_single(A,B,[X|As],[X|Bs]):-
   substitute_single(A,B,As,Bs).
现在我把它应用到主列表的每个元素上,它本身就是一个列表:

substitute(_,_,[],[]).
substitute(A,B,[P|Ps],[Q|Qs]):-
   substitute_single(A,B,P,Q),
   substitute(A,B,Ps,Qs).
例如,当我在
substitute_single(A,B,[C,A,D,1],X)
上测试我的代码时,问题就出现了。我得到了许多解决方案,一些解决方案将值应用于变量。例如,我得到:

A = 1,
C = 1,
D = 1,
X = [B, B, B, B] ; 
或:

只有在其他许多解决方案之后,我才能得到:

X = [C, B, D, 1] 
这是我想要的解决方案。然而,接下来还有更多的解决方案(我不希望如此)

因此,我试图切断程序,以防止它产生更多的解决方案:

substitute_single(A,B,[A|As],[B|Bs]):-
   substitute_single(A,B,As,Bs),
   !.
但是现在,虽然我只得到了一个解决方案,但它不是我想要的

(对于上面的示例,我得到的解决方案是

A = 1,
C = 1,
D = 1,
X = [B, B, B, B].
)

我不知道应该如何或在哪里更改我的程序,这样它只会给我想要的解决方案


(注意:
A
B
可能是变量,也可能是常量,例如:
替换单个(A,3,[C,A,D,1],X)
)应生成
[C,3,D,1]

尝试将X\=B添加到第二个子句中:

substitute_single(A,B,[X|As],[X|Bs]):-
   X \= B,
   substitute_single(A,B,As,Bs).

因此,当X不是B时,它不会分支到新的解决方案。

您的解决方案的问题是,您正在应用术语统一作为相等测试,而您需要的是对具有
==/2
谓词的严格相同术语进行测试

对于在一个列表中交换两个可能是变量的术语,假设一个正确闭合的列表和一个模式+,?,,,-以下谓词就足够了

exchgterms([],_,_,[]).
exchgterms([X|Xs],A,B,[C|Ys]) :-
  ( X == A -> C=B ;
    ( X == B -> C=A ; C=X ) ),
  exchgterms(Xs,A,B,Ys).
您可以从这里开始解决您的问题,并更改它以满足您的需要,特别是如果您的谓词必须具有不同的模式

更新

我更改了谓词的名称,以强调它也适用于交换非变量的术语

应该清楚的是,它应该在
substitute/4
的第二个子句中用不同顺序的参数替换
substitute\u single/4
,因此您寻求的解决方案是

substitute(_,_,[],[]).
substitute(A,B,[L|Ls],[R|Rs]) :-
  exchgterms(L,A,B,R),
  substitute(A,B,Ls,Rs).

我建议您更改
substitute/4
中参数的顺序,使第三个参数成为第一个参数。在这种形式下,大多数Prolog系统都会知道,如果这个参数被实例化,就像这里的情况一样,这两个子句是互斥的,这将有助于更高效的执行。

subsvarinterm/4
就是我想到的

:- use_module(library(apply)).

substVarInTerm(A,B,Term0,Term) :-
   term_variables(Term0,Vars0),
   exclude(==(A),Vars0,Vars),
   copy_term(A^Vars^Term0,B^Vars^Term).
请注意,如果第一个参数不是变量,则
subsvarinterm/4
不起作用

您提供的其他用例的效果很好:


我会为一个纯单调的解决方案悬赏,只要悬赏可以提供。。。。和确定-对于常见情况。@重复一遍,我对
substitute_single/4
的意思是在
B
中替换
A
,替换单个列表中出现的
A
。然后,我将它应用到每个列表中,将其用于列表列表。我这样做了,但现在得到了一个
[B,B,B,B]
解决方案。不管怎么说,我不认为加入这条规则会让我如愿以偿。我的意思是,假设
X=A
(和
A≠B
),因此我不希望我的解决方案(更换后)也包含
A
,我希望它包含
B
。问题似乎出现在您的测试案例中。Prolog将大写的输入视为变量。当您使用
替换单变量(A,B,[C,A,D,1],X)进行测试时。
Prolog将C,A和D视为变量,并尝试给它们赋值,在您的情况下,每个变量都是1。尝试使用以小写字母开头的原子,比如
substitute_single(a,b,[c,a,d,1],X)
或者将输入放在单括号中,比如
substitute_single('a','b',[c','a','d',1],X)。
单括号->单引号,但我确实希望它们是变量。有时我想替换常量中变量的外观,而不实际为该变量赋值,我的输入是变量和常量列表的形式。如果第一个参数必须是变量,那么你的解决方案不符合问题末尾的注释,说明第一个和第二个参数可以是非变量。
:- use_module(library(apply)).

substVarInTerm(A,B,Term0,Term) :-
   term_variables(Term0,Vars0),
   exclude(==(A),Vars0,Vars),
   copy_term(A^Vars^Term0,B^Vars^Term).
?- substVarInTerm(A,B,[[1,2,D,C],[A,D],[4,A],[1,2,A]],R).
R = [[1, 2, D, C], [B, D], [4, B], [1, 2, B]].

?- substVarInTerm(A,B,[C,A,D,1],X).
X = [C, B, D, 1].

?- substVarInTerm(A,3,[C,A,D,1],X).
X = [C, 3, D, 1].