刚刚介绍了prolog,试图通过一些简单的练习,但我一直在坚持这个。 我正在尝试编写一个输出输入列表的所有子列表的程序,其中每个子列表的长度都是> 1,它不能扩展到更大的子列表。它还将输出子列表列表中的起始位置。所以样本输出将是
| ?- plateau([a,a,b,2,2,2,a+1,a+1,s(1,2)], I, Len).
I = 1,
Len = 2 ? ;
I = 4,
Len = 3 ? ;
I = 7,
Len = 2 ? ;
no
我仍然对整个声明性事物感到困惑,并且在切换到命令式模式时遇到了很多麻烦。 我想我希望我的程序能够做类似
的事情program([H|T],I,L):-
T = [H1|T1] %split the tail
([H] = [H1] -> Count is Count+1, program(T,I,Count)
%if First element = second element, recurse with new values
; length(T,Spot),
%get the spot where you are in the list, so we know where sublist starts
program(T,Spot,L) %run again, from tail, since sublist didn't have another element?
program([],I,L). %terminate when entire list has been run through?
所以这不起作用,从我可以说出几个原因。我没有重置'count',所以它总共可以将所有子列表的值合计在一起?有办法解决这个问题吗?我的基本情况可能也不是我想要的 - 我只是不确定它应该是什么?我可能也错过了其他的东西...非常感谢任何帮助! :) 谢谢!
答案 0 :(得分:7)
您的程序将许多不同的问题合并为一个谓词。我们试着把它们分开一点。另外,我假设您正在搜索包含相同元素的两个或更多元素的最大子列表。
让我们从你想要的近似开始:识别子列表。不要担心这太宽泛,我们稍后会对其进行改进。我会将DCG用于此目的。非终端seq//1
描述了任意序列。
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
这是一个非常有用的非终端!
?- phrase((seq(Prefix),seq(Sublist),seq(Postfix)), [a,a,b,2,2,2,a+1,a+1,s(1,2)]). Prefix = Sublist, Sublist = [], Postfix = [a,a,b,2,2,2,a+1,a+1,s(1,2)] ; Prefix = [], Sublist = "a", Postfix = [a,b,2,2,2,a+1,a+1,s(1,2)] ...
两个答案都不是预期的,我们只想要长度为2或更长的子列表,所以我们必须稍微限制一下这个定义。比如说,要求Sublist
至少包含两个元素。那是Sublist = [_,_|_]
。
?- Sublist = [_,_|_], phrase((seq(Prefix),seq(Sublist),seq(Postfix)), [a,a,b,2,2,2,a+1,a+1,s(1,2)]). Sublist = "aa", Prefix = [], Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ; Sublist = "aab", Prefix = [], Postfix = [2,2,2,a+1,a+1,s(1,2)] ...
第一个答案显示了我们正在搜索的子列表。但第二个仍然是错误的:子列表的所有元素应该相等。最简单的方法是使用maplist/2
:
?- maplist(=(_),Es). Es = [] ; Es = [_G221] ; Es = [_G221,_G221] ; Es = [_G221,_G221,_G221]
有几个地方我们可以说明这个要求。我会把它放在尽可能早的地方:
?- Sublist = [_,_|_], phrase(( seq(Prefix), seq(Sublist),{maplist(=(_),Sublist)}, seq(Postfix)), [a,a,b,2,2,2,a+1,a+1,s(1,2)]). Sublist = "aa", Prefix = [], Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ; Sublist = [2,2], Prefix = "aab", Postfix = [2,a+1,a+1,s(1,2)] ; Sublist = [2,2,2], Prefix = "aab", Postfix = [a+1,a+1,s(1,2)] ; Sublist = [2,2], Prefix = [a,a,b,2], Postfix = [a+1,a+1,s(1,2)] ; Sublist = [a+1,a+1], Prefix = [a,a,b,2,2,2], Postfix = [s(1,2)] ; false.
现在,所有子列表都包含相同的元素。唉,我们同时将[2,2]
和[2,2,2]
作为子列表。我们只想要最大子列表。那么我们如何描述最大子列表是什么?
一种方法是查看我们的子列表:我们的子列表中必须没有相同的元素。因此,要么前面没有(epsilon),要么以与我们不同的元素结束的序列。
difel(_E,[]).
difel(E, Es) :- dif(E,F), phrase((seq(_), [F]), Es).
?- Sublist = [_,_|_], phrase(( seq(Prefix),{difel(E,Prefix)}, seq(Sublist),{maplist(=(E),Sublist)}, seq(Postfix)), [a,a,b,2,2,2,a+1,a+1,s(1,2)]). Sublist = "aa", Prefix = [], E = a, Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ; Sublist = [2,2], Prefix = "aab", E = 2, Postfix = [2,a+1,a+1,s(1,2)] ; Sublist = [2,2,2], Prefix = "aab", E = 2, Postfix = [a+1,a+1,s(1,2)] ; Sublist = [a+1,a+1], Prefix = [a,a,b,2,2,2], E = a+1, Postfix = [s(1,2)] ; false.
一个不正确的答案。最后还有一个潜伏着。
?- Sublist = [_,_|_], phrase(( seq(Prefix),{difel(E,Prefix)}, seq(Sublist),{maplist(=(E),Sublist)}, ( [] | [F],{dif(E,F)},seq(_) ) ), [a,a,b,2,2,2,a+1,a+1,s(1,2)]). Sublist = "aa", Prefix = [], E = a, F = b ; Sublist = [2,2,2], Prefix = "aab", E = 2, F = a+1 ; Sublist = [a+1,a+1], Prefix = [a,a,b,2,2,2], E = a+1, F = s(1,2) ; false.
这不完全是你想要的:你只想要长度。为此,请添加length([_|Prefix],I), length(Sublist,Len)
。
所以这是最终定义:
plateau(Xs, I, Len) :- Sublist = [_,_|_], phrase(( seq(Prefix),{difel(E,Prefix)}, seq(Sublist),{maplist(=(E),Sublist)}, ( [] | [F],{dif(E,F)},seq(_) ) ), Xs), length([_|Prefix],I), length(Sublist,Len).
答案 1 :(得分:2)
这里有很多复杂的答案。考虑一下这个不使用DCG或许多内置函数(对于初学者来说可能更简单):
plateau([X|Xs], I, L) :-
plateau(Xs, 1-1-X, I, L).
plateau([X1|Xs], I0-L0-X0, I, L) :-
X0 == X1, !,
NL0 is L0 + 1,
plateau(Xs, I0-NL0-X0, I, L).
plateau(_, I-L-_, I, L) :-
L > 1.
plateau([X|Xs], I0-L0-_, I, L) :-
NI is I0 + L0,
plateau(Xs, NI-1-X, I, L).
第一个子句设置调用,该调用累积(index)
- (length)
- (sublist element)
元组作为一个术语。
如果下一个列表length
相同,则下一个子句会增加element
(注意索引不会更改)。
仅当第二个子句在测试子列表元素运行是否被破坏时(因为切割!
)失败时才调用第三个子句,并且如果运行的length
是,则返回解决方案> 1
。
最后一个子句使Prolog能够从上次运行中回溯并重新开始搜索。
编辑: gusbro的解决方案实际上非常接近这个...... +1。
答案 2 :(得分:1)
你可以这样做:
plateau([Item|Tail], I, Len):-
plateau(Tail, 1, Item, 1, I, Len).
plateau(List, From, NItem, Len, From, Len):-
(List=[Item|_] -> (Item\=NItem;var(Item)); List = []),
Len > 1.
plateau([Item|Tail], IFrom, Item, ILen, From, Len):-
MLen is ILen + 1,
plateau(Tail, IFrom, Item, MLen, From, Len).
plateau([Item|Tail], IFrom, OItem, ILen, From, Len):-
OItem \= Item,
NFrom is IFrom + ILen,
plateau(Tail, NFrom, Item, 1, From, Len).
高原/ 6的第一个条款涉及子列表的终止。或者,该项目与您正在查看的项目不同,或者您到达列表的末尾。在这种情况下,我们只在当前长度大于1的情况下继续。
第二个子句处理我们仍然匹配列表中元素的情况的递归步骤。它只是向当前子列表的计数器添加一个并进行递归。
最后一个句子处理列表中找到的新(不同)项的情况,只重置计数器并进行递归。
答案 3 :(得分:1)
我尝试使用nth1 / 3内置版,但是让它工作更麻烦...无论如何,这里是另一个实现:
plateau(L, I, Len) :-
plateau(L, 1, I, Len).
plateau(L, P, I, Len) :-
nth1(P, L, E),
skipseq(P, L, E, J),
( J > P,
Len is J - P + 1,
I is P
; Q is J + 1,
plateau(L, Q, I, Len)
).
% get the index J of last element E (after I)
skipseq(I, L, E, J) :-
T is I + 1,
nth1(T, L, E),
!, skipseq(T, L, E, J).
skipseq(I, _, _, I).
试验:
[debug] ?- plateau([a,x,x,x,u,u,h,w],I,N).
I = 2,
N = 3 ;
I = 5,
N = 2 ;
false.
答案 4 :(得分:1)
这很简单直接。我们数从1;高原是等元素的最大子序列,长度至少为2;我们继续列表。写下来吧:
plateau(L,I,N):- plateau(L,1,I,N). % count from 1
plateau([A,A|B],I1,I,N):- !, more_elts(A,B,2,K,C), % two equals, or more
(I is I1, N is K ; plateau(C,I1+K,I,N)).
plateau([_|B],I1,I,N):- plateau(B,I1+1,I,N). % move along the list
more_elts(A,[A|B],I,K,C):- !, more_elts(A,B,I+1,K,C).
more_elts(_,B,I,I,B).
更新: 这假设输入列表的所有元素都是nonvar/1
。将非实例化变量作为输入列表的元素,这使得“子列表”的概念变得棘手和模糊。例如,[a,X,b,b]
的子列表是什么? [a,a]
和[b,b,b]
两者可以是相同输入列表的子列表吗? (这让我想起旋转的可观察值,状态的叠加等等......当选择旋转观察的方向时,它不能再被改变了......参见所有关于“测量”的讨论和sokuza-kanren.scm中的量子力学(发现链接here))