繁体   English   中英

Prolog:取列表的前“N”个元素

[英]Prolog: Take the first "N" elements of a list

我需要编写一个 Prolog 谓词take(L, N, L1)如果列表L1以相同的顺序包含列表L的前N个元素,它就会成功。 例如:

?- take([5,1,2,7], 3, L1).
L1 = [5,1,2]
?- take([5,1,2,7], 10, L1).
L1 = [5,1,2,7] 

到目前为止,Prolog 对我来说意义不大,而且我很难分解它。 这是我到目前为止所拥有的:

take([H|T], 0, []).
take([H|T], N, L1) :-
   take(T, X, L2),
   X is N-1.

你能解释一下我在这里做错了什么吗?

这是一个实现关系对应物的定义,以take Haskell 1等函数式语言。 首先,参数顺序应该不同,这有利于部分应用。 有一个切入点,但只有在错误检查内置(=<)/2 ,如果参数包含变量,它会产生一个instantiation_error

take(N, _, Xs) :- N =< 0, !, N =:= 0, Xs = [].
take(_, [], []).
take(N, [X|Xs], [X|Ys]) :- M is N-1, take(M, Xs, Ys).

?- take(2, Xs, Ys).
   Xs = [], Ys = []
;  Xs = [_A], Ys = [_A]
;  Xs = [_A,_B|_C], Ys = [_A,_B].

请注意上面的查询是如何读取的:

如何从Xs取出 2 个元素来获得Ys

并且有3个不同的答案。 如果Xs是空的,那么Ys也是空的。 如果Xs是一个包含一个元素的列表,那么Ys 如果Xs至少有 2 个元素,那么这两个元素就是Ys


1) 唯一的区别是take(-1, Xs,Ys)失败(对于所有Xs, Ys )。 可能最好的方法是发出类似于arg(-1,s(1),2)domain_error

findall/3 有点像 Prolog 的“瑞士刀”。 我会使用这个片段:

take(Src,N,L) :- findall(E, (nth1(I,Src,E), I =< N), L).

如果实例化正确,@CapelliC代码就可以工作; 如果没有,它可能会显示不稳定的行为:

?- take(Es, 0, Xs).
**LOOPS**                   % trouble: goal does not terminate

?- take([A,_], 1, [x]).          
true.                       % trouble: variable A remains 

为了防止这种情况,您可以像这样使用iwhen/2

take(Src, N, L) :-
   iwhen(ground(N+Src), findall(E, (nth1(I,Src,E), I =< N), L)).

使用 SWI-Prolog 8.0.0 运行的示例查询:

?- take([a,b,c,d,e,f], 3, Ls).
Ls = [a,b,c].

?- take([a,b,c,d,e,f], N, Ls).
ERROR: Arguments are not sufficiently instantiated

?- take(Es, 0, Xs).
ERROR: Arguments are not sufficiently instantiated

?- take([A,_], 1, [x]).
ERROR: Arguments are not sufficiently instantiated

现在更安全!

显而易见的解决方案是:

take(List, N, Prefix) :-
    length(List, Len),
    (   Len =< N
    ->  Prefix = List
    ;   length(Prefix, N),
        append(Prefix, _, List)
    ).

更少的思考意味着更少的错误机会。 它还使谓词更通用。

你的基本情况很好

take([H|T], 0, []).

你也可以说如果 N 是 1

take([H|T],1,[H]).

但是在递归情况下,某些变量没有像 L2 那样定义。 所以我们可以把它写成

take([X|T1],N,[X|T2]):-
N>=0,
N1 is N-1,
take(T1,N1,T2).

在这种情况下,所有变量都是模式匹配的。

取(L,N,L1):-长度(L1,N),追加(L1,_,L)。

这是高性能的、通用的和确定性的:

first_elements_of_list(IntElems, LongLst, ShortLst) :-
    LongLst = [H|T],
    (   nonvar(IntElems) -> Once = true
    ;   is_list(ShortLst) -> Once = true
    ;   Once = false
    ),
    first_elements_of_list_(T, H, 1, IntElems, ShortLst),
    (Once = true -> ! ; true).

first_elements_of_list_([], H, I, I, [H]).

first_elements_of_list_([_|_], H, I, I, [H]).

first_elements_of_list_([H|LongLst], PrevH, Upto, IntElems, [PrevH|ShortLst]) :-
    Upto1 is Upto + 1,
    first_elements_of_list_(LongLst, H, Upto1, IntElems, ShortLst).

swi-prolog 中的结果:

?- first_elements_of_list(N, [a, b, c], S).
N = 1,
S = [a] ;
N = 2,
S = [a,b] ;
N = 3,
S = [a,b,c].

?- first_elements_of_list(2, [a, b, c], S).
S = [a,b].

下面是一个变体,它也支持:

?- first_elements_of_list_more(10, [5, 1, 2, 7], L1).
L1 = [5,1,2,7].
first_elements_of_list_more(IntElems, [H|LongLst], [H|ShortLst]) :-
    once_if_nonvar(IntElems, first_elements_of_list_more_(LongLst, 1, IntElems, ShortLst)).

first_elements_of_list_more_([], Inc, Elems, []) :-
    (var(Elems) -> Inc = Elems
    ; Elems >= Inc).

first_elements_of_list_more_([_|_], E, E, []).

first_elements_of_list_more_([H|LongLst], Upto, IntElems, [H|ShortLst]) :-
    succ(Upto, Upto1),
    first_elements_of_list_more_(LongLst, Upto1, IntElems, ShortLst).

once_if_nonvar(Var, Expr) :-
    nonvar(Var, Bool),
    call(Expr),
    (Bool == true -> ! ; true).

nonvar(Var, Bool) :-
    (nonvar(Var) -> Bool = true ; Bool = false).

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM