Ваша программа в настоящее время слишком общая.В конце концов, есть три решения для вашего запроса, но только первое предназначено.Последнее неверно.
?- transform([foo, 1, 2], Xs).
Xs = [bar, 1, 2]
; Xs = [foobar, 1, 2]
; Xs = [foo, 1, 2]. % wrong
Сокращение теперь может уменьшить количество решений.Но в большинстве случаев это идет не так.Ваш вопрос, где вы должны "вставить разрез, чтобы добиться такого поведения?"имеет только в качестве ответа: нет места.Вам нужно сделать гораздо больше для достижения этого.
По сути, то, что вы описываете, является поэлементным преобразованием.Поэтому, может быть, мы придерживаемся описания одного элемента за раз.
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X). % too general!
Используйте maplist(el_transformed, [foo, 1, 2], Xs)
, как и раньше ...
Обратите внимание, что эта версия так же неверна, как и ваш исходный код.Но теперь просто добавить дополнительное условие:
el_transformed(X, Y) :-
isSpecial(X),
isSpecial(Y),
dif(X,Y).
el_transformed(X, X) :-
dif(X, foo),
dif(X, bar),
dif(X, foobar).
Есть один большой недостаток: в некоторых случаях эта версия не очень детерминирована:
?- el_transformed(foobar, bar).
true
; false.
Если вы хотитечтобы получить еще больше, рассмотрите возможность использования library(reif)
, доступных как для SICStus , так и SWI .
el_transformed(X, Y) :-
if_(( ( X = foo ; X = bar ; X = foobar ),
( Y = foo ; Y = bar ; Y = foobar ) ),
dif(X,Y),
X = Y).
С этой формулировкой нам не нужно повторятьсами.Чтобы проверить, в порядке ли код, давайте попробуем самый общий запрос:
?- el_transformed(X, Y).
X = foo, Y = bar
; X = foo, Y = foobar
; X = bar, Y = foo
; X = bar, Y = foobar
; X = foobar, Y = foo
; X = foobar, Y = bar
; X = Y, dif(Y,foobar), dif(Y,bar), dif(Y,foo).
Вы просматриваете все возможные случаи, которые могут произойти!Больше не нужно рассматривать случаи.
Так что, как правило: всякий раз, когда вам нужен предикат, который должен работать "двунаправленно", попробуйте вместо этого написать его как "всенаправленный"!
Возможно, вам не понравилось явное упоминание X = foo ; X = bar ; X = foobar
, в этом случае нам пришлось бы копать глубже, увеличив isSpecial/1
до isSpecial_t/2
:
isSpecial_t(X, T) :-
call(
( X = foo
; X = bar
; X = foobar
), T).
el_transformed(X, Y) :-
if_( ( isSpecial_t(X), isSpecial_t(Y) ), dif(X, Y) , X = Y ).