List 字符串列表的最长公共前缀(LCP)

List 字符串列表的最长公共前缀(LCP),list,prolog,prolog-dif,prolog-cut,logical-purity,meta-predicate,List,Prolog,Prolog Dif,Prolog Cut,Logical Purity,Meta Predicate,这是到目前为止我的代码。如何对其进行优化,使其打印前缀,例如: lcs([ H|L1],[ H|L2],[H|Lcs]) :- !, lcs(L1,L2,Lcs). lcs([H1|L1],[H2|L2],Lcs):- lcs( L1 ,[H2|L2],Lcs1), lcs([H1|L1], L2 ,Lcs2), longest(Lcs1,Lcs2,Lcs), !. lcs(_,_,[]). longest(L1,L2,Longest

这是到目前为止我的代码。如何对其进行优化,使其打印前缀,例如:

lcs([ H|L1],[ H|L2],[H|Lcs]) :-
    !,
    lcs(L1,L2,Lcs).
lcs([H1|L1],[H2|L2],Lcs):-
    lcs(    L1 ,[H2|L2],Lcs1),
    lcs([H1|L1],    L2 ,Lcs2),
    longest(Lcs1,Lcs2,Lcs),
    !.
lcs(_,_,[]).

longest(L1,L2,Longest) :-
    length(L1,Length1),
    length(L2,Length2),
    (  Length1 > Length2
    -> Longest = L1
    ;  Longest = L2
    ).
应返回
“inte”


Prolog有点生疏,有一段时间没做了:)

首先,让我们从一些相关的开始,但要简单得多

["interview", "interrupt", "integrate", "intermediate"]
(请参阅答案,如何使用双引号打印字符列表。)

这是在Prolog中相当容易的部分。唯一的缺点是它没有给我们提供最大值,而是提供所有可能的解决方案,包括最大值。请注意,不需要知道所有字符串,例如:

:- set_prolog_flag(double_quotes, chars).  % "abc" = [a,b,c]

prefix_of(Prefix, List) :-
   append(Prefix, _, List).

commonprefix(Prefix, Lists) :-
   maplist(prefix_of(Prefix), Lists).

?- commonprefix(Prefix, ["interview", "integrate", "intermediate"]).
   Prefix = []
;  Prefix = "i"
;  Prefix = "in"
;  Prefix = "int"
;  Prefix = "inte"
;  false.
所以我们得到了最后一个未知单词的部分描述作为回应。现在想象一下,稍后我们意识到
Xs=“incluse”
。Prolog没有问题:

?- commonprefix(Prefix, ["interview", "integrate", Xs]).
   Prefix = []
;  Prefix = "i", Xs = [i|_A]
;  Prefix = "in", Xs = [i, n|_A]
;  Prefix = "int", Xs = [i, n, t|_A]
;  Prefix = "inte", Xs = [i, n, t, e|_A]
;  false.
事实上,无论我们是事后诸葛亮还是在实际查询之前陈述这一点都没有区别:

?- commonprefix(Prefix, ["interview", "integrate", Xs]), Xs = "induce".
   Prefix = [], Xs = "induce"
;  Prefix = "i", Xs = "induce"
;  Prefix = "in", Xs = "induce"
;  false.
我们现在可以根据这个公式计算出最大值吗?请注意,这实际上需要某种形式的额外定量,我们在序言中没有任何直接规定。出于这个原因,我们必须将我们限制在某些我们知道是安全的情况下。最简单的方法是坚持单词列表不包含任何变量。我将为此目的使用

?- Xs = "induce", commonprefix(Prefix, ["interview", "integrate", Xs]).
   Xs = "induce", Prefix = []
;  Xs = "induce", Prefix = "i"
;  Xs = "induce", Prefix = "in"
;  false.
这种方法的缺点是,如果单词列表未知,我们会出现实例化错误

请注意,我们做了一些假设(我希望这些假设真的成立)。特别是,我们假设只有一个最大值。在这种情况下,这是成立的,但一般来说,
前缀
可能有几个独立的值。此外,我们假设
IPrefix
将始终接地。我们也可以检查一下,确定一下。或者:

maxprefix(Prefix, Lists) :-
   iwhen(ground(Lists), maxprefix_g(Prefix, Lists)).

maxprefix_g(Prefix, Lists_g) :-
   setof(N-IPrefix, ( commonprefix(IPrefix, Lists_g), length(IPrefix, N ) ), Ns),
   append(_,[N-Prefix], Ns).   % the longest one
在这里,前缀不必是单个前缀(在我们的情况下就是这样)


但是,最好是一个更纯的版本,它根本不需要使用实例化错误。

以下是我将如何实现这一点:

maxprefix_g(Prefix, Lists_g) :-
   setof(N, IPrefix^ ( commonprefix(IPrefix, Lists_g), length(IPrefix, N ) ), Ns),
   append(_,[N], Ns),
   length(Prefix, N),
   commonprefix(Prefix, Lists_g).
非全部相等/1的实现来自(您可以在编辑历史记录中找到我的实现)

我们使用
append
maplist
将列表中的字符串拆分为前缀和后缀,其中所有字符串的前缀都相同。要使这个前缀最长,我们需要声明至少两个后缀的第一个字符是不同的

这就是为什么我们使用
head/2
one\u empty\u head/1
not\u all\u equal/1
head/2
用于检索字符串的第一个字符
one_empty_head/1
用于说明如果其中一个后缀为空,则自动将其作为最长的前缀
not_all_equal/1
用于检查或说明至少两个字符不同

例子
以下是@capelical提出(并随后撤回)的代码的纯化变体:

?- longest_common_prefix(["interview", "integrate", "intermediate"], Z).
Z = [i, n, t, e] ;
false.

?- longest_common_prefix(["interview", X, "intermediate"], "inte").
X = [i, n, t, e] ;
X = [i, n, t, e, _156|_158],
dif(_156, r) ;
false.

?- longest_common_prefix(["interview", "integrate", X], Z).
X = Z, Z = [] ;
X = [_246|_248],
Z = [],
dif(_246, i) ;
X = Z, Z = [i] ;
X = [i, _260|_262],
Z = [i],
dif(_260, n) ;
X = Z, Z = [i, n] ;
X = [i, n, _272|_274],
Z = [i, n],
dif(_272, t) ;
X = Z, Z = [i, n, t] ;
X = [i, n, t, _284|_286],
Z = [i, n, t],
dif(_284, e) ;
X = Z, Z = [i, n, t, e] ;
X = [i, n, t, e, _216|_224],
Z = [i, n, t, e] ;
false.

?- longest_common_prefix([X,Y], "abc").
X = [a, b, c],
Y = [a, b, c|_60] ;
X = [a, b, c, _84|_86],
Y = [a, b, c] ;
X = [a, b, c, _218|_220],
Y = [a, b, c, _242|_244],
dif(_218, _242) ;
false.

?- longest_common_prefix(L, "abc").
L = [[a, b, c]] ;
L = [[a, b, c], [a, b, c|_88]] ;
L = [[a, b, c, _112|_114], [a, b, c]] ;
L = [[a, b, c, _248|_250], [a, b, c, _278|_280]],
dif(_248, _278) ;
L = [[a, b, c], [a, b, c|_76], [a, b, c|_100]] ;
L = [[a, b, c, _130|_132], [a, b, c], [a, b, c|_100]];
…
上面的
maplist\u t/3
maplist/2
它适用于术语相等/不相等具体化-
maplist\u t/5
与更高的算术相同:

:- set_prolog_flag(double_quotes, chars).

:- use_module(library(reif)).

lists_lcp([], []).
lists_lcp([Es|Ess], Ls) :-
   if_((maplist_t(list_first_rest_t, [Es|Ess], [X|Xs], Ess0),
        maplist_t(=(X), Xs))
       , (Ls = [X|Ls0], lists_lcp(Ess0, Ls0))
       , Ls = []).

list_first_rest_t([], _, _, false).
list_first_rest_t([X|Xs], X, Xs, true).
首先,这里是一个地面查询:

maplist_t(P_2, Xs, T) :-
   i_maplist_t(Xs, P_2, T).

i_maplist_t([], _P_2, true).
i_maplist_t([X|Xs], P_2, T) :-
   if_(call(P_2, X), i_maplist_t(Xs, P_2, T), T = false).

maplist_t(P_4, Xs, Ys, Zs, T) :-
   i_maplist_t(Xs, Ys, Zs, P_4, T).

i_maplist_t([], [], [], _P_4, true).
i_maplist_t([X|Xs], [Y|Ys], [Z|Zs], P_4, T) :-
   if_(call(P_4, X, Y, Z), i_maplist_t(Xs, Ys, Zs, P_4, T), T = false).
最后,以下是显示改进的决定论的查询:

?- lists_lcp(["interview",X,"intermediate"], "inte").
   X = [i,n,t,e]
;  X = [i,n,t,e,_A|_B], dif(_A,r)
;  false.

?- lists_lcp(["interview","integrate",X], Z).
   X = Z, Z = []
;  X = Z, Z = [i]
;  X = Z, Z = [i,n]
;  X = Z, Z = [i,n,t]
;  X = Z, Z = [i,n,t,e]
;  X = [i,n,t,e,_A|_B], Z = [i,n,t,e]
;  X = [i,n,t,_A|_B]  , Z = [i,n,t]  , dif(_A,e)
;  X = [i,n,_A|_B]    , Z = [i,n]    , dif(_A,t)
;  X = [i,_A|_B]      , Z = [i]      , dif(_A,n)
;  X = [_A|_B]        , Z = []       , dif(_A,i).

?- lists_lcp([X,Y], "abc").
   X = [a,b,c]      , Y = [a,b,c|_A]
;  X = [a,b,c,_A|_B], Y = [a,b,c]
;  X = [a,b,c,_A|_B], Y = [a,b,c,_C|_D], dif(_A,_C)
;  false.

?- lists_lcp(L, "abc").
   L = [[a,b,c]]
;  L = [[a,b,c],[a,b,c|_A]]
;  L = [[a,b,c,_A|_B],[a,b,c]]
;  L = [[a,b,c,_A|_B],[a,b,c,_C|_D]], dif(_A,_C)
;  L = [[a,b,c],[a,b,c|_A],[a,b,c|_B]]
;  L = [[a,b,c,_A|_B],[a,b,c],[a,b,c|_C]]
;  L = [[a,b,c,_A|_B],[a,b,c,_C|_D],[a,b,c]]
;  L = [[a,b,c,_A|_B],[a,b,c,_C|_D],[a,b,c,_E|_F]], dif(_A,_E) 
…
给出了一个基于
if\u3
的实现

?- lists_lcp(["interview","integrate","intermediate"], Z).
Z = [i,n,t,e].                              % succeeds deterministically
在我之前的回答中,几乎所有的问题都给出了相同的答案,所以我不在这里显示它们

列出了\u lcp([X,Y],“abc”)
,但是,新代码不再普遍终止。

一个简单的版本:

lists_lcp([], []).
lists_lcp([Es|Ess], Xs) :-
   foldl(list_list_lcp, Ess, Es, Xs).                % foldl/4

list_list_lcp([], _, []).
list_list_lcp([X|Xs], Ys0, Zs0) :-
   if_(list_first_rest_t(Ys0, Y, Ys)                 % if_/3
      , ( Zs0 = [X|Zs], list_list_lcp(Xs, Ys, Zs) )
      ,   Zs0 = []
      ).

list_first_rest_t([], _, _, false).
list_first_rest_t([X|Xs], Y, Xs, T) :-
   =(X, Y, T).                                       % =/3
示例:

:- set_prolog_flag(double_quotes, chars).
pref([],_,[]).
pref(_,[],[]).
pref([H|T1],[H|T2],[H|Tr]):-
    pref(T1,T2,Tr).
pref([H|_],[H|_],[]).
pref([H1|_],[H2|_],[]):-
    dif(H1,H2).

lcf([],[]).
lcf([W],R):-
    pref(W,W,R).
lcf([W1,W2|L],R):-
    pref(W1,W2,R),
    lcf([W2|L],R).

我最近不得不为两个列表实现这一点,这就是我提出的代码。它假定两个输入列表已充分实例化

pref("interview","integrate",R).
R = [i, n, t, e] ;
R = [i, n, t] ;
R = [i, n] ;
R = [i] ;
R = [] ;
False.

lcf(["interview", "interrupt", "integrate", "intermediate"],R).
R = [i, n, t, e]

lcf(["interview", "interrupt", X, "intermediate"],R).
R = X, X = [i, n, t, e, r]
这很容易扩展到多个列表:

longest_common_prefix([X|Xs], [X|Ys], [X|Common]) :- !,
    longest_common_prefix(Xs, Ys, Common).
longest_common_prefix(_, _, []).
如果您不喜欢使用
foldl

lcs([], []).
lcs([L1|Ls], Prefix) :-
    foldl(longest_common_prefix, Ls, L1, Prefix).

代码有什么问题吗?它提供了解决方案吗?还是错误的解决方案?让它在当前不打印前缀时“打印”前缀是一种功能添加,而不是一种“优化”。
longest_common_前缀([[a],[B],[B],[]),a=a,B=B.
给出了两个相同的解决方案?@false我的实现引入了冗余的
dif
约束,我不知道如何避免这些约束。直到
not_all_equal_/1
这是一个非常前卫的方法@false我正在写一个关于
不等于
的实现的问题,因为它似乎是一个有用的谓词,但很难正确实现…请注意,如上所述设置Prolog标志,
[I,n,t,e]=“inte”
!所以他们是一样的。似乎我的答案如何得到“inte”写如上所示!很好,但是看起来我们可以更进一步@错。进一步的更多(更好)的查询显示答案不会“重叠”?是的,确实如此。不同的方法!假设您有
lcp/3
。即两个列表的lcp。现在…我的推理有缺陷。信息技术它不仅适用于
[X,Y]
而且适用于更大的列表!(而且,…似乎
(',')/3的扩展也有缺陷。急需更多的元谓词问题…“elp!)出于某种原因,当我运行代码时,它崩溃了,它说“stack.pl:6:Singleton variables:[Y]”@repeat@blazing. 您正在运行哪个查询?这个
stack.pl
-消息对我来说没有多大意义。请提供更多数据说明您遇到的问题。代码无法编译,我试图在终端的swipl上运行它,但它并没有给我那个错误@repeat@blazing. 下载并安装
库(reif)
。我在回答中添加了一个链接。@blazing。请详细说明您遇到的错误!“那个错误”并不能帮助我定位问题所在。
pref("interview","integrate",R).
R = [i, n, t, e] ;
R = [i, n, t] ;
R = [i, n] ;
R = [i] ;
R = [] ;
False.

lcf(["interview", "interrupt", "integrate", "intermediate"],R).
R = [i, n, t, e]

lcf(["interview", "interrupt", X, "intermediate"],R).
R = X, X = [i, n, t, e, r]
longest_common_prefix([X|Xs], [X|Ys], [X|Common]) :- !,
    longest_common_prefix(Xs, Ys, Common).
longest_common_prefix(_, _, []).
lcs([], []).
lcs([L1|Ls], Prefix) :-
    foldl(longest_common_prefix, Ls, L1, Prefix).
lcs([], []).
lcs([L1|Ls], Prefix) :-
    lcs(Ls, L1, Prefix).

lcs([], Prefix, Prefix).
lcs([L1|Ls], Prefix0, Prefix) :-
    longest_common_prefix(L1, Prefix0, Prefix1),
    lcs(Ls, Prefix1, Prefix).