Пролог: заменить элемент в списке по указанному индексу - PullRequest
16 голосов
/ 15 декабря 2011

Я хотел бы иметь предикат Prolog, который может заменить элемент в списке по указанному индексу.

Пример:

% replace(+List,+Index,+Value,-NewList).
?- L=[a,b,c,d], replace(L,1,z,L2).
L2 = [a,z,c,d]

Я не знаю, как это сделатьэтот.Спасибо за вашу помощь!Лоик.

Ответы [ 8 ]

13 голосов
/ 15 декабря 2011

Я дам вам базовый случай, я думаю, что вы сможете легко выполнить рекурсивный случай:

replace([_|T], 0, X, [X|T]).

edit:

Сейчасчто операция решила это, я добавлю рекурсивный случай:

replace([H|T], I, X, [H|R]):- I > 0, I1 is I-1, replace(T, I1, X, R).

edit2:

Это должно вернуть исходный список в ситуации вне границкак @GeorgeConstanza спрашивает в комментариях:

replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]):- I > -1, NI is I-1, replace(T, NI, X, R), !.
replace(L, _, _, L).

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

5 голосов
/ 17 декабря 2011

Ответ от Фортрана это нормально, но в структурах SWI-Prolog есть неограниченная арность, поэтому это должно работать:

replace([_|T], 0, X, [X|T]).
replace([H|T], I, X, [H|R]) :-
    I > 0,
    I1 is I - 1,
    replace(T, I1, X, R).

replace1(L, I, X, R) :-
    Dummy =.. [dummy|L],
    J is I + 1,
    nb_setarg(J, Dummy, X),
    Dummy =.. [dummy|R].

tr(Method, K) :-
    length(L, K),
    K1 is K - 1,
    time(call(Method, L, K1, test, R)),
    assertion(nth1(K, R, test)).

но, к моему удивлению:

?- % /home/carlo/prolog/replace.pl compiled 0,00 sec, 2,280 bytes
?- tr(replace,2000000).
% 3,999,999 inferences, 2,123 CPU in 2,128 seconds (100% CPU, 1884446 Lips)
true .

?- tr(replace1,2000000).
% 5 inferences, 1,410 CPU in 1,414 seconds (100% CPU, 4 Lips)
true.

?- tr(replace,4000000).
% 7,999,999 inferences, 3,510 CPU in 3,520 seconds (100% CPU, 2279267 Lips)
true .

?- tr(replace1,4000000).
% 5 inferences, 2,825 CPU in 2,833 seconds (100% CPU, 2 Lips)
true.

?- tr(replace,5000000).
% 9,999,999 inferences, 3,144 CPU in 3,153 seconds (100% CPU, 3180971 Lips)
true .

?- tr(replace1,5000000).
% 5 inferences, 4,476 CPU in 4,486 seconds (100% CPU, 1 Lips)
ERROR: =../2: Arguments are not sufficiently instantiated
^  Exception: (9) setup_call_catcher_cleanup(system:true, prolog_statistics:catch(user:call(replace1, [_G1, _G4, _G7, _G10|...], 4999999, test, _G15000005), _G15000021, (report(t(1324124267.2924964, 18.892632697, 28490132), 10), throw(_G15000021))), _G15000145, prolog_statistics: (_G15000032=true)) ? abort
% Execution Aborted

Myпервая попытка (с K = 10000000) убила процесс!Поэтому, к моей неприязни, пытаясь добиться некоторой производительности, я заканчиваю тем, что заполняю сообщение об ошибке в списке рассылки SWI-Prolog ...

EDIT : после публикации в рассылке SWI-PrologСписок, и (быстрое!) исправление, я восстановил, и вот версия, учитывающая подсказку об использовании памяти (теперь это все стандартный код ISO!).Из-за необычно больших значений требуется инструкция увеличения стека:

?- prolog_stack_property(global,limit(L)), N is L*2, set_prolog_stack(global,limit(N)).
N = 536870912.

Вот обновленная процедура:

replace2(L, I, X, R) :-
    Dummy =.. [dummy|L],
    J is I + 1,
    setarg(J, Dummy, X),
    Dummy =.. [dummy|R].

и тест:

?- tr(replace,10000000).
% 19,999,999 inferences, 5.695 CPU in 5.719 seconds (100% CPU, 3511942 Lips)
true .

?- tr(replace2,10000000).
% 5 inferences, 2.564 CPU in 2.571 seconds (100% CPU, 2 Lips)
true.

Код это быстрее, но, пожалуйста, обратите внимание на комментарий Яна к моей почте:

Сводится к плохой обработке ошибок в = .. (+, -).Исправлена.Кстати, я думаю, что это довольно ужасный способ сделать работу.Даже если вы хотите сделать это таким образом, просто используйте setarg / 3 вместо nb_setarg / 3.Последнее действительно должно быть последним средством.Этот метод использует больше памяти, потому что ему нужен и огромный термин, и список.Наконец, функторы (пары имя / арность) в настоящее время не восстанавливаются, поэтому вы создаете один такой объект для каждой замены списка длиной, для которой это никогда не использовалось.

4 голосов
/ 15 декабря 2011

ок, ясно, что замена с использованием рекурсии на @fortran - путь.но слишком ли это безумно / медленно для фактического использования?

replace(List, Idx, With, ListOut) :-
   length(Idx, Before),
   append(Before, After, List),
   (  After=[_Discard|Rest]
   -> true
   ;  Rest=[]
   ),
   append(Before, [With|Rest], ListOut).

По сути, вы создаете пустой массив с размером Idx и привязываете его к списку ввода.затем отбросьте элемент после этого и свяжите два списка вместе с замещающим элементом, вставленным в него.

это можно еще больше упростить, если у вас все в порядке, если вы попытаетесь установить idx N (индексирование от 0) для Nсписок элементов.

replace(List, Idx, With, ListOut) :-
   length(Idx, Before),
   append(Before, [_Discard|Rest], List),
   append(Before, [With|Rest], ListOut).
3 голосов
/ 08 декабря 2017

Другой способ, которым я придумал, который я считаю правильным (?).Я не знаю о сложности времени выполнения.

replace(I, L, E, K) :-
  nth0(I, L, _, R),
  nth0(I, K, E, R).

Использование:

?- replace(2, [1, 2, 3, 4, 5], 10, X).
X = [1, 2, 10, 4, 5].
3 голосов
/ 09 февраля 2016

Если мы используем same_length/2, append/3 и length/2, нам не нужно писать рекурсивный код:

list_nth0_item_replaced(Es, N, X, Xs) :-
   <a href="https://sicstus.sics.se/sicstus/docs/latest4/html/sicstus.html/lib_002dlists.html#index-same_005flength_002f_005b2_002c3_005d-_0028lists_0029-1" rel="nofollow">same_length</a>(Es, Xs),
   <a href="https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue#append" rel="nofollow">append</a>(Prefix, [_|Suffix], Es),
   <a href="https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue#length" rel="nofollow">length</a>(Prefix, N),
   append(Prefix, [X|Suffix], Xs).

Пример запроса, заданного OP:

?- list_nth0_item_replaced([a,b,c,d], 1, z, Xs).
   Xs = [a,z,c,d]
;  false.

Это также работает "в другом направлении"!

?- list_nth0_item_replaced(Xs, 1, z, [a,z,c,d]).
   Xs = [a,_A,c,d]
;  false.

Еще лучше, нам даже не нужно указывать конкретный индекс:

?- list_nth0_item_replaced(Es, N, X, [a,z,c,d]).
   N = 0, X = a, Es = [_A, z, c, d]
;  N = 1, X = z, Es = [ a,_A, c, d]
;  N = 2, X = c, Es = [ a, z,_A, d]
;  N = 3, X = d, Es = [ a, z, c,_A]
;  false.

?- list_nth0_item_replaced([a,b,c,d], N, X, Xs).
   N = 0, Xs = [X,b,c,d]
;  N = 1, Xs = [a,X,c,d]
;  N = 2, Xs = [a,b,X,d]
;  N = 3, Xs = [a,b,c,X]
;  false.
3 голосов
/ 24 февраля 2012

Действительно, никто не должен когда-либо делать это с простыми списками IMO любой значительной длины, так как каждое обновление будет занимать O(n) новое место. Прямое set_once / update через setarg/nb_setarg займет 0 нового пробела и с представлением списков в двоичном дереве O(log(n)) нового пробела. Замены можно также хранить в отдельном словаре, который сам по себе поддерживается в виде дерева (так как он должен расти). список чанков (как здесь ) может содержать большие чанки в дереве, каждый из которых представляет собой составной термин фиксированного размера, который можно напрямую установить / обновить через setarg/nb_setarg, и добавить новые чанки в дерево по мере необходимости.

Даже без обновления просто доступ к простым спискам безнадежно медленен, O(n) время, превращая любой алгоритм квадратичный в один миг. Списки хороши только на самом деле короткие или как домашнее задание.

2 голосов
/ 09 февраля 2016

Код, представленный в этом предыдущем ответе , весьма универсален - благодаря .

Есть ли недостатки?Да, есть и обратная сторона: Неэффективность!

В этом ответе мы улучшаем производительность и сохраняем универсальность.

:- use_module(<a href="http://www.swi-prolog.org/man/clpfd.html" rel="nofollow noreferrer">library(clpfd)</a>).

Мы действуем как этот предыдущий ответ сделал, когда он определил предикат fd_length/2:

list_nth0_item_replaced__NEW(Es, N, X, Xs) :-
   list_index0_index_item_replaced(Es, 0,N, X, Xs).

list_index0_index_item_replaced([_|Es], I ,I, X, [X|Es]).
list_index0_index_item_replaced([E|Es], I0,I, X, [E|Xs]) :-
   I0 #< I,
   I1 #= I0+1,
   list_index0_index_item_replaced(Es, I1,I, X, Xs).

Итак ... он стал быстрее?

?- numlist(1,100000,Zs), time(list_nth0_item_replaced(Zs,99999,x,Xs)).
% <b>14,499,855</b> inferences, 0.893 CPU in 0.893 seconds (100% CPU, 16237725 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
Xs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 7 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 18377 Lips)
false.

?- numlist(1,100000,Zs), time(list_nth0_item_replaced<b>__NEW</b>(Zs,99999,x,Xs)).
% <b>499,996</b> inferences, 0.049 CPU in 0.049 seconds (100% CPU, 10158710 Lips)
Zs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
Xs = [1, 2, 3, 4, 5, 6, 7, 8, 9|...] ;
% 6 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 213988 Lips)
false.

Хорошо, это быстрее .Но все еще универсально?

?- list_nth0_item_replaced__NEW([a,b,c,d], 1, z, Xs).
   Xs = [a,z,c,d]
;  false.

?- list_nth0_item_replaced__NEW(Xs, 1, z, [a,z,c,d]).
   Xs = [a,_A,c,d]
;  false.

?- list_nth0_item_replaced__NEW(Es, N, X, [a,z,c,d]).
   N = 0, X = a, Es = [_A, z, c, d],
;  N = 1, X = z, Es = [ a,_A, c, d]
;  N = 2, X = c, Es = [ a, z,_A, d],
;  N = 3, X = d, Es = [ a, z, c,_A]
;  false.

?- list_nth0_item_replaced__NEW([a,b,c,d], N, X, Xs).
   N = 0, Xs = [X,b,c,d]
;  N = 1, Xs = [a,X,c,d]
;  N = 2, Xs = [a,b,X,d]
;  N = 3, Xs = [a,b,c,X]
;  false.

Выглядит хорошо для меня!

1 голос
/ 30 апреля 2015

Как насчет того, чтобы сделать это прямым способом, подобным этому?

:- use_module(<a href="http://www.swi-prolog.org/man/clpfd.html" rel="nofollow">library(clpfd)</a>).

list_nth0_item_replaced([_|Xs], 0, E, [E|Xs]).
list_nth0_item_replaced([X|Xs], N, E, [X|Ys]) :-
   N #> 0,
   N #= N0+1,
   list_nth0_item_replaced(Xs, N0, E, Ys).

Вот пример использования указанного OP:

?- list_nth0_item_replaced([a,b,c,d],1,z,Ls).
   Ls = [a,z,c,d]
;  false.

Выше код чистый, поэтому мыможет задавать более общие вопросы и ожидать логически обоснованных ответов:

?- list_nth0_item_replaced([a,b,c,d], N, X, Ls).
   N = 0, Ls = [X,b,c,d]
;  N = 1, Ls = [a,X,c,d]
;  N = 2, Ls = [a,b,X,d]
;  N = 3, Ls = [a,b,c,X]
;  false.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...