Как добавить элемент в список на месте в 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), но мне не нравится иметь временный список.

обычные оговорки применяются здесь - это не домашнее задание и я я просто изучаю Пролог.

6 ответов


переменные в прологе могут быть назначены только один раз. Как только X имеет значение [1,2,3,4], оно никогда не может иметь другого значения. Временная переменная и добавление/3, Как вы упомянули, - это способ сделать это.

сказав это, вы можете сделать один трюк, который, вероятно, не рекомендуется. Если X = [1,2,3,4, Y], то вы можете сделать Y=5 и X теперь имеет значение, которое вы хотите. Я считаю, что этот метод называется списком различий.


как указывали другие, вы будете застрять с проблемой производительности.
Но как упражнение я решил попробовать создать предикат, который мог бы добавить элемент в конец списка, не используя 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 функция, как встроенная функция, скорее всего, будет быстрее, чем что-либо созданное вручную.


одним декларативным решением является использование списка различий (как предложил Даниэль в своем ответе). Список различий получает свое имя от того, что обычно представляется как разница между двумя списками: списком и его хвостом. Например, пустой список можно представить как T-T. Список с элементами 1, 2 и 3 может быть представлен как [1,2,3| T]-T (заметим, что (-)/2 стандартный встроенный оператор infix). Преимущество такого представления заключается в том, что вы можете добавить элемент в список в постоянном время, используя одно определение факта 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.

при необходимости нетрудно конвертировать между" нормальным " списком и списком различий. Я оставляю это на ваше усмотрение.


вы беспокоитесь о неправильном конце проблемы. Совместное использование структуры может произойти только путем сохранения элемента в начале списка. Этот метод имеет характеристики, которые вы хотите. Из-за способа определения списков при добавлении двух списков будет скопирован весь первый список. В данном случае это будет весь список. Мусор, генерируемый списком из одного элемента, очевидно, будет намного меньше.

Если вы действительно должны добавить, рассмотрите возможность построения списка в обратном порядке, а затем один раз в конце, что намного дешевле, или используйте списки различий, которые позволяют эффективно добавлять к концу.


вы не можете изменять списки в 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|_] после добавления этих двух элементов.


поскольку Prolog имеет приложение, которое принимает только списки, почему бы нам не использовать его для вставки нашего элемента в один из списков. т. е.

% 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).