Изменение порядка имен переменных
Как написать стандартным образом avs_term_rearranged(AVs, T, AVsR)
с учетом AVs
а также T
такой, что AVsR
это перестановка AVs
с элементами, расположенными в том же порядке, в котором их переменные располагаются слева направо в T
,
AVs
это список элементов формы A = V
где A
это атом, обозначающий имя переменной, как 'X'
а также V
является соответствующей переменной. Такие списки составляются read_term/2,3
с опцией чтения variable_names/1
(7.10.3). Кроме того, точный порядок элементов не определен.
| ?- read_term(T,[variable_names(AVs)]).
A+B+A+_+C.
AVs = ['A'=A,'B'=B,'C'=C]
T = A+B+A+_+C
T
это термин, который содержит все переменные AVs
плюс еще немного.
Обратите внимание, что в стандартной программе соответствия нельзя полагаться на порядок терминов для переменных (7.2.1):
7.2.1 Переменная
Если
X
а такжеY
переменные, которые не идентичны, тоX
term_precedesY
должен зависеть от реализации, за исключением того, что при создании отсортированного списка (7.1.6.5, 8.10.3.1 j) порядок должен оставаться постоянным.ПРИМЕЧАНИЕ. - Если
X
а такжеY
обе анонимные переменные, то они не являются одинаковыми терминами (см. 6.1.2 а).
Рассмотрим в качестве примера из 8.4.3.4:
sort([f(U),U,U,f(V),f(U),V],L).
Succeeds, unifying L with [U,V,f(U),f(V)] or
[V,U,f(V),f(U)].
[The solution is implementation dependent.]
Таким образом, есть два возможных способа, как sort/2
будет работать, и нельзя даже полагаться на успех:
sort([f(U),U,U,f(V),f(U),V],L), sort(L, K), L == K.
В качестве примера:
?- avs_term_rearranged(['A'=A,'B'=B,'C'=C], A+C+F+B, AVsR).
AVsR = ['A'=A,'C'=C,'B'=B].
4 ответа
avs_term_rearranged(AVs, T, AVsR) :-
term_variables(T, Vs),
copy_term(Vs+AVs, Vs1+AVs1),
bind_names(AVs1),
build_vn_list(Vs, Vs1, AVsR).
bind_names([]).
bind_names([N=V|AVs]) :-
N = V,
bind_names(AVs).
build_vn_list([], [], []).
build_vn_list([V|Vs],[N|Ns],NVs) :-
( atom(N) ->
NVs = [N=V|NVs1]
; var(N) ->
NVs = NVs1
),
build_vn_list(Vs, Ns, NVs1).
Использование term_variables/2
на T
получить список Xs
с переменными в нужном порядке. Затем создайте список с элементами AVs
, но в таком порядке.
avs_term_rearranged(AVs, T, AVRs) :-
term_variables(T, Xs),
pick_in_order(AVs, Xs, AVRs).
pick_in_order([], [], []).
pick_in_order(AVs, [X|Xs], AVRs) :-
( pick(AVs, X, AV, AVs1) ->
AVRs = [AV|AVRs1],
pick_in_order(AVs1, Xs, AVRs1)
;
pick_in_order(AVs, Xs, AVRs)
).
pick([AV|AVs], X, AX, DAVs) :-
(_=V) = AV,
( V==X ->
AX = AV,
DAVs = AVs
;
DAVs = [AV|DAVs1],
pick(AVs, X, AX, DAVs1)
).
Заметки:
- это квадратично, потому что
pick/4
линейный term_variables/2
не является строго необходимым, вы могли бы пройтиT
непосредственно
Мой предыдущий ответ имел квадратичную сложность во время выполнения. Если это проблема, вот линейная альтернатива. Причина, по которой это немного сложно, заключается в том, что несвязанные переменные не могут использоваться в качестве ключей для хеширования и т. Д.
Как и прежде, мы в основном переставляем список AVs
так что его элементы имеют тот же порядок, что и переменные в списке Xs
(получен из term_variables/2
). Новая идея здесь состоит в том, чтобы сначала вычислить (наземное) описание требуемой перестановки (APs
), а затем построить перестановку AV
используя это описание.
avs_term_rearranged(AVs, T, AVRs) :-
term_variables(T, Xs),
copy_term(AVs-Xs, APs-Ps),
ints(Ps, 0, N),
functor(Array, a, N),
distribute(AVs, APs, Array),
gather(1, N, Array, AVRs).
ints([], N, N).
ints([I|Is], I0, N) :- I is I0+1, ints(Is, I, N).
distribute([], [], _).
distribute([AV|AVs], [_=P|APs], Array) :-
nonvar(P),
arg(P, Array, AV),
distribute(AVs, APs, Array).
gather(I, N, Array, AVRs) :-
( I > N ->
AVRs = []
;
arg(I, Array, AV),
I1 is I+1,
( var(AV) -> AVRs=AVRs1 ; AVRs = [AV|AVRs1] ),
gather(I1, N, Array, AVRs1)
).
Эта версия очень короткая, используя member/2
(из пролога пролога) для поиска. У него очень низкое потребление вспомогательной памяти. Только список AVsR
создано. Временные термины кучи не создаются (в текущих реализациях). Имеет квадратичные накладные расходы на длину AVs
, хоть.
avs_term_rearranged(AVs, T, AVsR) :-
term_variables(T, Vs),
rearrange(Vs, AVs, AVsR).
rearrange([], _, []).
rearrange([V|Vs], AVs, AVsR0) :-
( member(AV, AVs),
AV = (_=Var), V == Var ->
AVsR0 = [AV|AVsR]
; AVsR0 = AVsR
),
rearrange(Vs, AVs, AVsR).
Другой аспект заключается в том, что member(AV, AVs)
цель по своей сути недетерминирована, даже если задействован только относительно неглубокий недетерминизм, в то время как (первая) версия @jschimpf создает точку выбора только для (;)/2
реализации if-then-else. Обе версии не оставляют никаких точек выбора.
Вот версия, более точно имитирующая идею @jschimpf. Это, однако, теперь создает вспомогательные термины, которые остаются в куче.
rearrange_js([], _, []).
rearrange_js([V|Vs], AVs0, AVsR0) :-
( select(AV, AVs0, AVs),
AV = (_=Var), V == Var ->
AVsR0 = [AV|AVsR]
; AVsR0 = AVsR,
AVs0 = AVs
),
rearrange_js(Vs, AVs, AVsR).