我试图制作一个小的prolog程序,它可以从列表中删除一个特定的数字,但我似乎无法将其生成一个列表。
代码通过
delete_all(X,[H|T],Ans):- X\=H ,Ans= H; delete_all(X,T,Ans).
当我输入
| ?- delete_all(x,[a,b,x],Answer).
我得到了
Answer = a ? ;
Answer = b ? ;
no
到目前为止,我试过
| ?- findall(Ans,delete_all(x,[a,b,x],Ans),Ans).
返回
Ans = [a,b]
是的,我想要这个输出,但我不想改变查询的形式。 还有其他办法吗?
对不起,如果我错过了基础知识,我真的很陌生。
答案 0 :(得分:0)
既然你说你对Prolog很新,我认为如果我们首先评估你的代码而不是立即提供一个工作样本可能会有用:
delete_all(X,[H|T],Ans):-
X \= H, Ans = H ;
delete_all(X,T,Ans).
用语言来说,这说:
X不等于H,如果是,则将Ans与H
统一起来或强>
递归地为尾部执行相同的操作
这解释了为什么当元素不等于X时,程序会为您提供单独的结果,并且只要在列表中遇到元素,它就会返回false。为了进一步解释为什么在后一种情况下你会弄错,让我们检查两个小的执行例子:
delete_all(e,[a,e],Ans)
CASE e \= a -> Ans = e (Prolog returns you Ans)
delete_all(a,[a],Ans)
CASE a = a -> the OR operator triggers and recursion starts
但是,我们现在进入这种情况:
delete_all(a,[],Ans)
此时,Prolog无法匹配您的任何代码,因为该列表为空,您不会在任何地方指定此代码。你能看到并理解发生了什么(并且出错了)吗?
现在我们已经评估了您的代码,让我们尝试编写一个模型,在其中我们可以表达您要完成的任务。将任何问题分解成小块总是一个好主意,所以让我们开始吧:
我们可以尝试删除空列表中的内容 - >这应该永远是真的
我们可以尝试删除非空列表中的内容,但我们希望返回实际生成的列表,而不是已删除的元素。为此,我们需要跟踪在递归过程中不需要删除的每个元素,只需跳过我们想要删除的元素。
我们将从基本案例开始:
% Base case for empty list (_ = wildcard, since we do not care about
% which element we want to delete out of an empty list)
delete_all(_,[],[]).
现在我们确实需要处理元素:
% X is not equal to H, so we want to preserve H in our result list
delete_all(X,[H|T],[H|Ans]) :-
...
...
% X is equal to H, so we want to skip H and not add it to our result list
delete_all(X,[H|T],Ans) :-
...
...
正如您所看到的,我们现在有一个非常易于阅读和结构化的模型来解决您的问题。现在剩下的就是编写实际条件和递归调用。我认为你自己尝试写这些可能是个好主意,因为这是最好的学习方式!
另外,不要抱歉,每个人都必须从某个地方开始,这就是为什么每个人都在这里提供帮助的原因。如果你让它工作,请告诉我,如果没有,我会编辑这篇文章以指定更多信息。
祝你好运!修改强>
你现在非常接近,这只是最后一点解释。请考虑以下事项:
% Prepend H to our current result being built recursively
recursion([H|T],[H|Result]):-
recursion(T,Result). % Call recursively with the result tail
所以这样做是继续在结果前面加上每个H,然后使用我们的结果尾部进行实际的递归调用。这就像倒退一样。
让我们分解一下:
recursion([a,b,c],Result)
% Execution:
(1) case H = a : prepend a to result and call recursively with result tail
(2) case H = b : prepend b to result and call recursively with result tail
(3) case H = c : prepend c to result and call recursively with result tail
现在我们达到输入列表变空的程度,这就解释了为什么我们需要基本情况:
recursion([],[]).
这种情况说:每当我们的输入列表为空时,我们将前置一个空列表以获得我们想要的结果。 如果你想知道为什么一个空列表,那就是Prolog的工作方式:
[a,b,c] is equivalent to [a,b,c|[]]
总结(并且仍然没有明确地给出答案:)),你正确地写了条件,基本情况也存在,问题在于你如何进行递归调用。你能搞清楚吗?让我发布!
修改强>
您的尝试与工作解决方案之间的区别在于您正在进行的递归通话。如果你这样做:
recursion([H|T],R) :-
recursion(T,[H|R])
你最终会得到一个反向列表,因为你使递归调用在H之前加上H,这不是我们想要的。这就是为什么我们在delete_all的头部指定prepend,这就是为什么你的代码不能正常工作 - 只用结果来递归调用。这也是我们用空列表编写基本案例的原因。我会给你一个简单的例子:
% We prepend H to our result in the recursive call
recursion([],R) :- write('Ended with result: '), writeln(R).
recursion([H|T],R) :-
recursion(T,[H|R]). % <- here
输出:
?- recursion([1,2,3],R).
Ended with result:
[3, 2, 1|_G5835] % <- note the uninstantiated var and reversed order
正确的例子:
% We prepend H to our result in the head of recursion2
recursion2([],[]).
recursion2([H|T],[H|R]) :- % <- here
recursion2(T,R). % and recursive call with just R
输出:
?- recursion2([1,2,3],R).
R = [1, 2, 3]
所以你只需要编辑你的递归调用,而不是在你的结果中添加H,在你的头脑中做,并在递归调用中传递你的Ans var。
% We prepend H to Ans in the head of delete_all
delete_all(X,[H|T],[H|Ans]) :-
...
delete_all(X,T,Ans). % <- and make the recursive call with just Ans
那应该解决它:)。