% 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).