Постараемся решить простую проблему, используя if_/3
; например, я попытаюсь разбить список (отсортированный по предикату p/2
) в двух списках: префикс, в котором для каждого элемента X
мы имеем p(X, true)
и остаток (в котором, если список был отсортирован по p/2
, мы бы p(X, false)
Я буду использовать библиотеку reif
, как here Итак, вот полный код моей программы:..
:- use_module(reif).
pred_prefix(Pred_1, List, L_true, L_false) :-
pred_prefix_aux(List, Pred_1, L_true, L_false).
pred_prefix_aux([], _, [], []).
pred_prefix_aux([X|Xs], Pred_1, True, False) :-
if_( call(Pred_1, X),
( True = [X|True0],
pred_prefix_aux(Xs, Pred_1, True0, False)
),
( True = [],
False = [X|Xs]
)
).
предикат, переданный в мета-предикат будет принимать два аргумента: первый элемент текущего списка, а второй будет либо true
, либо false
. В идеале этот предикат всегда будет успешным и не оставит за собой точек выбора.
В первом аргументе if_/2
предикат вычисляется с помощью текущего элемента списка; второй аргумент - то, что происходит, когда true
; третий аргумент - это то, что происходит, когда false
.
С этим я могу разделить список в ведущих a
сек и отдых:
?- pred_prefix([X, B]>>(=(a, X, B)), [a,a,b], T, F).
T = [a, a],
F = [b].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,c,d], T, F).
T = [],
F = [b, c, d].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,a], T, F).
T = [],
F = [b, a].
?- pred_prefix([X, B]>>(=(a, X, B)), List, T, F).
List = T, T = F, F = [] ;
List = T, T = [a],
F = [] ;
List = T, T = [a, a],
F = [] ;
List = T, T = [a, a, a],
F = [] .
Как вы можете избавиться от ведущих 0 для примера:
?- pred_prefix([X, B]>>(=(0, X, B)), [0,0,1,2,0,3], _, F).
F = [1, 2, 0, 3].
Конечно, это может были написаны гораздо проще:
drop_leading_zeros([], []).
drop_leading_zeros([X|Xs], Rest) :-
if_(=(0, X), drop_leading_zeros(Xs, Rest), [X|Xs] = Rest).
Здесь я только что удалил все ненужные аргументы uments.
Если бы вы должны сделать это безif_/3
, вы должны были бы написать:
drop_leading_zeros_a([], []).
drop_leading_zeros_a([X|Xs], Rest) :-
=(0, X, T),
( T == true -> drop_leading_zeros_a(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
Здесь мы предполагаем, что =/3
действительно всегда удается без выбора точек и T
всегда будет либо true
или false
.
И, если у нас не было =/3
либо, вы бы написать:
drop_leading_zeros_full([], []).
drop_leading_zeros_full([X|Xs], Rest) :-
( X == 0 -> T = true
; X \= 0 -> T = false
; T = true, X = 0
; T = false, dif(0, X)
),
( T == true -> drop_leading_zeros_full(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
, который не является идеальным. Но теперь, по крайней мере, вы сами можете увидеть, в одном месте, что происходит на самом деле.
PS: Пожалуйста, внимательно прочитайте код и взаимодействие верхнего уровня.
Вы должны быть экспертом в более чем Prolog, чтобы понять это ;-) Невозможно помочь и загадочное имя. Имейте в виду, что первый аргумент не может быть ничем: он должен быть предикатом, который детерминистически детерминистически и объединяет свой второй аргумент с «истинным» или «ложным» («reification»), иначе возникает ошибка. –
PS: Я не говорю, что это не полезный предикат, но есть нечто большее, чем кажется на первый взгляд. Скрытие уродства под слоями косвенности является общей чертой стиля программирования Prolog, пропагандируемого «несколькими главными вкладчиками», о которых вы говорите. Надеюсь, что я не нахожу слишком негативным, я считаю, что хорошо иметь такие конструкции и пытаться применить их к существующим и новым проблемам. –
PPS: В частности, изучите определение '=/3', прямо под' if_/3', [по вашей собственной ссылке] (http://stackoverflow.com/a/27358600/1812457), где вы видите, что требуется написать предикат, который хорошо сочетается с 'if_/3'. –