% File : APPLIC.PL
% Author : Lawrence Byrd + Richard A. O'Keefe
% Updated: 4 August 1984 and Ken Johnson 11-8-87
% Purpose: Various "function" application routines based on apply/2.
% Needs : append/3 from listut.pl
:- public
apply/2,
callable/1,
checkand/2,
checklist/2,
convlist/3,
exclude/3,
mapand/3,
maplist/3,
some/2,
somechk/2,
sublist/3.
:- mode
apply(+, +),
callable(?),
checkand(+, +),
checklist(+, +),
convlist(+, +, ?),
exclude(+, +, ?),
mapand(+, ?, ?),
maplist(+, ?, ?),
some(+, ?),
somechk(+, +),
sublist(+, +, ?).
% Operator declaration ued below
:- op(920,xfy,&). % Used for conjunction
% apply(Pred, Args)
% is the key to this whole module. It is basically a variant of call/1
% (see the Dec-10 Prolog V3.43 manual) where some of the arguments may
% be already in the Pred, and the rest are passed in the list of Args.
% Thus apply(foo, [X,Y]) is the same as call(foo(X,Y)),
% and apply(foo(X), [Y]) is also the same as call(foo(X,Y)).
% BEWARE: any goal given to apply is handed off to call/1, which is the
% Prolog *interpreter*, so if you want to apply compiled predicates you
% MUST have :- public declarations for them. The ability to pass goals
% around with some of their (initial) arguments already filled in is
% what makes apply/2 so useful. Don't bother compiling anything that
% uses apply heavily, the compiler won't be able to help much. LISP
% has the same problem. Later Prolog systems may have a simpler link
% between compiled and interpreted code, or may fuse compilation and
% interpretation, so apply/2 may come back into its own. At the moment,
% apply and the routines based on it are not well thought of.
apply(Pred, Args) :-
( atom(Pred),
Goal =.. [Pred|Args]
; % compound(Pred)
Pred =.. OldList,
append(OldList, Args, NewList),
Goal =.. NewList
), !,
call(Goal).
% callable(Term)
% succeeds when Term is something that it would make sense to give to
% call/1 or apply/2. That is, Term must be an atom or a compound term;
% variables and integers are out.
callable(Term) :-
nonvar(Term),
functor(Term, FunctionSymbol, _),
atom(FunctionSymbol).
% checkand(Pred, Conjunction)
% succeeds when Pred(Conjunct) succeeds for every Conjunct in the
% Conjunction. All the *and predicates in this module assume that
% a&b&c&d is parsed as a&(b&(c&d)), and that the "null" conjunction
% is 'true'. It is possible for this predicate, and most of the
% others, to backtrack and try alternative solutions. If you do not
% want that to happen, copying one of these predicates and putting a
% cut in the suggested place will produce a tail-recursive version.
% The cuts in the *and predicates are there because "non-and" is
% defined by exclusion; they cannot be assigned types.
checkand(_, true) :- !.
checkand(Pred, A&B) :- !,
apply(Pred, [A]),
checkand(Pred, B).
checkand(Pred, A) :-
apply(Pred, [A]).
% checklist(Pred, List)
% suceeds when Pred(Elem) succeeds for each Elem in the List.
% In InterLisp, this is EVERY. It is also MAPC.
checklist(_, []).
checklist(Pred, [Head|Tail]) :-
apply(Pred, [Head]),
checklist(Pred, Tail).
% mapand(Rewrite, OldConj, NewConj)
% succeeds when Rewrite is able to rewrite each conjunct of OldConj,
% and combines the results into NewConj.
mapand(_, true, true) :- !.
mapand(Pred, Old&Olds, New&News) :- !,
apply(Pred, [Old,New]),
mapand(Pred, Olds, News).
mapand(Pred, Old, New) :-
apply(Pred, [Old,New]).
% maplist(Pred, OldList, NewList)
% succeeds when Pred(Old,New) succeeds for each corresponding
% Old in OldList, New in NewList. In InterLisp, this is MAPCAR.
% It is also MAP2C. Isn't bidirectionality wonderful?
maplist(_, [], []).
maplist(Pred, [Old|Olds], [New|News]) :-
apply(Pred, [Old,New]),
maplist(Pred, Olds, News).
% convlist(Rewrite, OldList, NewList)
% is a sort of hybrid of maplist/3 and sublist/3.
% Each element of NewList is the image under Rewrite of some
% element of OldList, and order is preserved, but elements of
% OldList on which Rewrite is undefined (fails) are not represented.
% Thus if foo(X,Y) :- integer(X), Y is X+1.
% then convlist(foo, [1,a,0,joe(99),101], [2,1,102]).
convlist(_, [], []).
convlist(Pred, [Old|Olds], NewList) :-
apply(Pred, [Old,New]),
!,
NewList = [New|News],
convlist(Pred, Olds, News).
convlist(Pred, [_|Olds], News) :-
convlist(Pred, Olds, News).
% exclude(Pred, List, SubList)
% succeeds when SubList is the SubList of List containing all the
% elements for which Pred(Elem) is *false*. That is, it removes
% all the elements satisfying Pred. Efficiency would be somewhat
% improved if the List argument came first, but this argument order
% was copied from the older sublist/3 predicate, and let's face it,
% apply/2 isn't stupendously efficient itself.
exclude(_, [], []).
exclude(Pred, [Head|List], SubList) :-
apply(Pred, [Head]),
!,
exclude(Pred, List, SubList).
exclude(Pred, [Head|List], [Head|SubList]) :-
exclude(Pred, List, SubList).
% some(Pred, List)
% succeeds when Pred(Elem) succeeds for some Elem in List. It will
% try all ways of proving Pred for each Elem, and will try each Elem
% in the List. somechk/2 is to some/2 as memberchk/2 is to member/2;
% you are more likely to want somechk with its single solution.
% In InterLisp this is SOME.
some(Pred, [Head|_]) :-
apply(Pred, [Head]).
some(Pred, [_|Tail]) :-
some(Pred, Tail).
somechk(Pred, [Head|_]) :-
apply(Pred, [Head]),
!.
somechk(Pred, [_|Tail]) :-
somechk(Pred, Tail).
% sublist(Pred, List, SubList)
% succeeds when SubList is the sub-sequence of the List containing all
% the Elems of List for which Pred(Elem) succeeds.
sublist(_, [], []).
sublist(Pred, [Head|List], SubList) :-
apply(Pred, [Head]),
!,
SubList = [Head|Rest],
sublist(Pred, List, Rest).
sublist(Pred, [_|List], SubList) :-
sublist(Pred, List, SubList).