在这种Prolog的代码,我打算列出前N素数,
(...)
biggerPrime(N,P) :-
isPrime(N),
P is N,
!.
biggerPrime(N,P) :-
N1 = N+1,
biggerPrime(N1,P).
primeListAcc(0,A,R,R) :- !.
primeList(N,L) :-
primeListAcc(N,1,[],L).
primeListAcc(N,A,L,R) :-
N1 is N-1,
biggerPrime(A,P),
A1 is P+1,
primeListAcc(N1,A1,[P|L],R).
如果我想名单下令向后正常工作:
?- primeList(5,L).
L = [11, 7, 5, 3, 2].
但是,如果我改变从代码的最后一行[P | L]到[L | P]是这样的:
primeListAcc(N,A,L,R) :-
N1 is N-1,
biggerPrime(A,P),
A1 is P+1,
primeListAcc(N1,A1,[L|P],R).
我得到:
?- primeList(5,L).
L = [[[[[[]|2]|3]|5]|7]|11].
我在想什么? 这是推动我疯了!
太好了,所以你已经发现将元素添加到列表的末尾的问题。 在Prolog,我们可以做到这一点
add(X,L,Z):- L=[X|Z].
等待,是什么? 如何解读呢? 我们必须知道这里的调用约定。 我们预计 L
和Z
进来作为非实例变量,我们安排L
从现在指向一个新创建的利弊节点与X
在其头部,并Z
它的尾巴。 Z
被实例化,可能的话,在将来的某个呼叫。
IOW我们在这里创建的是一个开放式的列表, L = [X|Z] = [X, ...]
primeList(N,L) :-
primeListAcc(N,1,[],L).
primeListAcc(N,A,Z,L) :- N > 0, % make it explicitly mutually-exclusive,
N1 is N-1, % do not rely on red cuts which are easily
biggerPrime(A,P), % invalidated if clauses are re-arranged!
A1 is P+1,
L = [P|R], % make L be a new, open-ended node, holding P
primeListAcc(N1,A1,Z,R). % R, the tail of L, to be instantiated further
primeListAcc(0,A,R,R). % keep the predicate's clauses together
我们现在可以看到Z
是不是真的在这里需要的,因为它承载着[]
向下的递归调用链,持平。 因此,我们可以重新写primeListAcc
没有Z
参数,因此其最终条款将是
primeListAcc(0,A,R):- R=[].
保持Z
围绕作为非实例变量允许它稍后可能具有非空列表实例化,以及(当然,仅一次 (除非回溯发生))。 这就形成了“差异列表”技术的基础。
要回答你的问题的文字 -在这里,可以考虑这种互动成绩单:
1 ?- X=[a|b].
X = [a|b]
2 ?- X=[a|b], Y=[X|c].
X = [a|b]
Y = [[a|b]|c]
该[a|b]
的输出是一个缺点节点被刚刚打印方式中,当它的尾巴(这里, b
) 不是一个列表。 原子,数字, 不是列表。
回想一下,一个列表要么是空列表[]
或仿函数的项'.'
和两个参数,它的第二个参数是一个列表。 的语法[P|Ps]
是该术语简化符号'.'(P, Ps)
,这是一个列表,如果Ps
是一个列表(如在您的示例的情况下)。 术语'.'(Ps, P)
,而另一方面,这也可以写成[Ps|P]
如你正在做的),是不是列表,如果P
不是一个列表。 你可以得到具有反向列表reverse/2
。