24

如果我在 Prolog 中有一个列表,例如 X = [1, 2, 3, 4],如何将元素 5 添加到列表的末尾以使 X = [1, 2, 3, 4, 5]?

append 函数需要两个列表,即 append(A,B,C) 将 A 和 B 连接到列表 C。

我可以使用临时列表 Y = [1, 2, 3, 4] 和 Z = [5] 来执行此操作,然后执行附加(Y,Z,X),但我不喜欢临时列表。

通常的免责声明在这里适用——这不是家庭作业,我只是在学习 Prolog。

4

6 回答 6

12

正如其他人指出的那样,您将陷入性能问题。
但是作为一个练习,我决定尝试创建一个谓词,它可以将一个元素附加到列表的末尾,而不使用append.

% add_tail(+List,+Element,-List)
% Add the given element to the end of the list, without using the "append" predicate.
add_tail([],X,[X]).
add_tail([H|T],X,[H|L]):-add_tail(T,X,L).

我建议您只需使用该append功能,作为内置功能,它可能比任何手动制作的功能都要快。

于 2014-09-13T19:37:21.607 回答
11

Prolog 中的变量只能赋值一次。只要 X 具有值 [1,2,3,4],它就永远不会有另一个值。就像您提到的那样,临时变量和 append/3 是这样做的方法。

话虽如此,您可以做一个可能不推荐的技巧。如果 X = [1,2,3,4,Y] 那么你可以做 Y=5 并且 X 现在有你想要的值。我相信这种技术被称为差异列表。

于 2013-02-23T06:29:08.467 回答
3

一种声明性解决方案是使用差异列表(正如丹尼尔在其回答中建议的那样)。差异列表的名称通常表示为两个列表之间的差异:列表及其尾部。例如,一个空列表可以表示为T-T。包含元素 1、2 和 3 的列表可以表示为[1,2,3| T]-T(注意这(-)/2是标准的内置中缀运算符)。这种表示的优点是您可以通过使用append/3谓词的单个事实定义在恒定时间内将元素附加到列表中:

append(L1-T1, T1-T2, L1-T2).

一个使用示例:

?- append([1,2,3,4| T1]-T1, [5| T2]-T2, Result).
T1 = [5|T2],
Result = [1, 2, 3, 4, 5|T2]-T2.

如有必要,在“正常”列表和差异列表之间进行转换并不难。我把它作为练习留给你。

于 2014-09-13T21:14:21.863 回答
3

你不能在 Prolog 中修改列表,但是你可以创建一个未指定长度的列表:

main :-
    A = [1,2,3,4|_].

nth0/3然后,您可以在 SWI-Prolog 中使用插入元素:

:- initialization(main).

main :-
    A = [1,2,3,4|_],
    nth0(4,A,5),
    writeln(A).

插入此元素后,A = [1,2,3,4,5|_].

您还可以定义一个将项目就地附加到列表末尾的函数,然后像这样使用它:

:- initialization(main).

append_to_list(List,Item) :-
    List = [Start|[To_add|Rest]],
    nonvar(Start),
    (var(To_add),To_add=Item;append_to_list([To_add|Rest],Item)).

main :-
    A = [1,2,3|_],
    append_to_list(A,4),
    append_to_list(A,4),
    writeln(A).

在本例中,A = [1,2,3,4,4|_]在这两个项目之后附加。

于 2018-02-06T03:28:03.283 回答
3

由于 Prolog 有 append 只接受列表,我们为什么不使用它来将我们的元素插入其中一个列表。IE

% E = element, L = list, R = result
% e.g. add_elem_in_list ([1,2,3,4], 5, R).
add_elem_in_list(L, E, R) :- append(L, [E], R).
于 2018-02-12T13:16:31.730 回答
2

你担心问题的错误结局。结构共享只能通过将一个元素放在列表的开头来实现。该方法具有您想要的性能特征。由于列表的定义方式,当您附加两个列表时,将复制整个第一个列表。在这种情况下,这将是整个列表。单项列表产生的垃圾显然会比这小得多。

如果您确实必须追加,请考虑向后构建列表,然后在末尾反转一次,这样会便宜得多,或者使用差异列表,这样可以有效地追加到末尾。

于 2013-02-22T16:45:23.427 回答