List Prolog列表
我在用Prolog编写代码时遇到了问题,它获取一个列表,删除所有素数,然后返回列表的其余部分。我已经编写了一个素数检查谓词,List Prolog列表,list,prolog,primes,List,Prolog,Primes,我在用Prolog编写代码时遇到了问题,它获取一个列表,删除所有素数,然后返回列表的其余部分。我已经编写了一个素数检查谓词,prime/1,它工作得很好,但是当我将程序应用到列表时,就像我在Prolog中尝试做的任何事情一样,我会按照需要返回没有素数的列表,但顺序相反 primemover(L, Lcomp):- primeremover(L, [], Lcomp). primeremover([], A, A). primeremover([H | T], A, X):- \+
prime/1
,它工作得很好,但是当我将程序应用到列表时,就像我在Prolog中尝试做的任何事情一样,我会按照需要返回没有素数的列表,但顺序相反
primemover(L, Lcomp):-
primeremover(L, [], Lcomp).
primeremover([], A, A).
primeremover([H | T], A, X):-
\+ prime(H),
primeremover(T, [H | A], X).
primeremover([H | T], A, X):-
prime(H),
primeremover(T, A, X).
通过查看我的代码,我可以理解为什么列表会颠倒过来,但我就是找不到解决方法。如果我尝试反转递归用例的开头和结尾,将非素数移动到中间列表中,它会以正确的顺序工作并输出,但每个值都在其自己的嵌套列表中,这比向后输出还要糟糕
有没有一种简单的方法可以解决这个问题?我认为这可以解决您的问题:
primeremover([], []).
primeremover([H | T], [H | Y]):-
\+ prime(H),
primeremover(T, Y).
primeremover([H | T], Y):-
prime(H),
primeremover(T, Y).
我不确定我是否遗漏了什么,但我相信您正在将其视为一种函数式语言而不是逻辑语言。而且,第三个论点似乎并没有给解决方案增加什么东西;可以在不丢失功能的情况下删除它
我没有你的prime
谓词,但我用它来测试:
main :- primeremover([1,2,3,4,5], A), write(A).
prime(X) :- X = 2; X = 3; X = 5.
我使用了GNU Prolog(1.4.0)。我认为这可以解决您的问题:
primeremover([], []).
primeremover([H | T], [H | Y]):-
\+ prime(H),
primeremover(T, Y).
primeremover([H | T], Y):-
prime(H),
primeremover(T, Y).
我不确定我是否遗漏了什么,但我相信您正在将其视为一种函数式语言而不是逻辑语言。而且,第三个论点似乎并没有给解决方案增加什么东西;可以在不丢失功能的情况下删除它
我没有你的prime
谓词,但我用它来测试:
main :- primeremover([1,2,3,4,5], A), write(A).
prime(X) :- X = 2; X = 3; X = 5.
我使用了GNU Prolog(1.4.0)。您不需要调用辅助函数来完成此操作,您可以直接使用输入变量和返回值来完成 代码:
您不需要调用辅助函数来执行此操作,您可以仅使用输入变量和返回直接执行此操作 代码:
您在
primeremover/3
谓词中使用第二个参数作为累加器,即作为堆栈工作的辅助参数,用于收集中间结果。这种(有用的)技术通常用于递归谓词的定义中,以获得尾部递归的好处。此技术的一个典型示例是定义用于反转列表的谓词。朴素定义不是尾部递归的,因此需要与列表长度成比例的空间:
reverse([], []).
reverse([Head| Tail], Reversed) :-
reverse(Tail, Reversed0),
append(Reversed0, [Head], Reversed).
注意,第二个子句中对reverse/2
谓词的递归调用不是最后一个调用。因此,随后的append/3
谓词调用必须通过将其保存在堆栈中而挂起,直到递归的reverse/2
谓词终止。该堆栈在每次递归调用中增加一个元素。但是,如果递归调用是最后一个调用,则不需要此堆栈。尾部递归定义可以使用累加器进行编码:
reverse(List, Reversed) :-
reverse(List, [], Reversed).
reverse([], Reversed, Reversed).
reverse([Head| Tail], List, Reversed) :-
reverse(Tail, [Head| List], Reversed).
但是,在您的特定情况下,正如Erwin和Guillermo所解释的,没有必要使用累加器,因为您可以在遍历输入列表时构造输出列表。但是,他们建议的代码可以通过避免测试输入列表的当前头是否为素数两次(对于Erwin解决方案)以及通过使用Prolog的标准if-then-else控制结构避免剪切(对于Guillermo解决方案)来改进:
prime_remover([], []).
prime_remover([Head| Tail], NonPrimes):-
( prime(Head) ->
prime_remover(Tail, NonPrimes)
; NonPrimes = [Head| NonPrimesTail),
prime_remover(Tail, NonPrimesTail)
).
请注意,此版本也是尾部递归的。您在
primeremover/3
中使用第二个参数作为累加器进行谓词,即作为堆栈工作的辅助参数,用于收集中间结果。这种(有用的)技术通常用于递归谓词的定义中,以获得尾部递归的好处。此技术的一个典型示例是定义用于反转列表的谓词。朴素定义不是尾部递归的,因此需要与列表长度成比例的空间:
reverse([], []).
reverse([Head| Tail], Reversed) :-
reverse(Tail, Reversed0),
append(Reversed0, [Head], Reversed).
注意,第二个子句中对reverse/2
谓词的递归调用不是最后一个调用。因此,随后的append/3
谓词调用必须通过将其保存在堆栈中而挂起,直到递归的reverse/2
谓词终止。该堆栈在每次递归调用中增加一个元素。但是,如果递归调用是最后一个调用,则不需要此堆栈。尾部递归定义可以使用累加器进行编码:
reverse(List, Reversed) :-
reverse(List, [], Reversed).
reverse([], Reversed, Reversed).
reverse([Head| Tail], List, Reversed) :-
reverse(Tail, [Head| List], Reversed).
但是,在您的特定情况下,正如Erwin和Guillermo所解释的,没有必要使用累加器,因为您可以在遍历输入列表时构造输出列表。但是,他们建议的代码可以通过避免测试输入列表的当前头是否为素数两次(对于Erwin解决方案)以及通过使用Prolog的标准if-then-else控制结构避免剪切(对于Guillermo解决方案)来改进:
prime_remover([], []).
prime_remover([Head| Tail], NonPrimes):-
( prime(Head) ->
prime_remover(Tail, NonPrimes)
; NonPrimes = [Head| NonPrimesTail),
prime_remover(Tail, NonPrimesTail)
).
请注意,此版本也是尾部递归的。这里是对代码的最小编辑,以更正此问题
%%//primeremover(+L,-Lcomp)
底漆去除剂(L,Lcomp):-%//是:
除油剂(L,[],Lcomp)。
除油剂([],A,A)。
primeremover([H | T],A[H | X]):-%//primeremover([H | T],A,X):-
\+素数(H),
除底漆剂(T,A,X)。%//除底漆剂(T,[H | A],X)。
除底漆剂([H | T],A,X):-
素数(H),
除油剂(T、A、X)。
我们将-add-at-end-elements追加到返回列表中,并在最深调用时将其最终结束指针设置为[]
,而不是在前面将-add-at-end-elements追加到累加器中,并从最深调用返回其最终值。两者本质上都是迭代过程,并且都是由Prolog编译的,即使用恒定的控制堆栈空间
这两个变体都是尾部递归的,但新的变体是尾部递归的
由于变量A
不再用作累加器,因此