Mercurial > hg > plml
view prolog/utils.pl @ 2:546bfd3988b0
Added unpolished pldoc documentation.
author | samer |
---|---|
date | Fri, 13 Jan 2012 15:53:04 +0000 |
parents | 0dd31a8c66bd |
children |
line wrap: on
line source
% Some general utilities :- module(utils,[ % type testing and enumeration natural/1 % test or enumerate natural numbers , isfinite/1 % check number is non NaN or Inf , int/1 % test or enumerate integers , in/2 % mathematical utilities , max/3 , min/3 % list utilities , list_idx1_member/3 % like nth1 but more useful argument order , apply_to_nth1/4 , measure/3 % match list lengths , equal_length/2 % match 2 list lengths , getopts/2 , rep/3 % make a list of repeats of the same term , cons/3 % list constructror , decons/3 % list deconstructor % comma lists , cl_list/2 , cl_list_vt/2 , cl_length/2 , cl_length_vt/2 , cl_member/2 % term manipulation , copy_head/2 % check terms for same head and arity , unify_args/5 , reinstatevars/3 , reinstatevars/4 % formatting and parsing , aphrase/2 % like phrase but makes an atom instead of a string , aphrase/3 % like aphrase but takes code list , print_list/1 % writes each element on a new line , printq_list/1 % as print_list but quotes atom as necessary , print_numbered_list/1 % database management , extensible/1 % declare dynamic multifile predicate , bt_assert/1 , bt_retract/1 , strict_assert/1 , strict_retract/1 , browse/2 % browse predicate with unknown arity , current_state/2 , set_state/2 % file system utilities , dir/2 % directory listing , path_atom/2 % expand paths , expand_path/2 % expand paths % operating system , open_with/2 % apply shell command to a file (SIDE EFFECTS) , open_with/3 % apply shell command to a file with options (SIDE EFFECTS) , shellcmd/2 % apply shell command to arguments , open_/1 % open with 'open' command (Mac OS X). , fmt_shellcmd/3 % format a shell command with switches and args , hostname/1 % user interaction , read_char_echo/1 % read one character and echo it immediately , get_key/2 % read and validate keypress , userchk/1 % unary predicate which allows user to force failure , prompt_for_key/3 % ---- high order stuff ---- % modes of computation , retry/1 % keep trying until success , parallel/1 % parallel computation using threads , bt_call/2 % Construct backtrackable operation , on_backtracking/1 % iteration , exhaust/1 % trigger all solutions to a goal (SIDE EFFECTS) , iterate/3 % apply predicate recursively till failure % mapping , for_naturals/2 % call predicate with natural numbers N down to 1 , mapints/2 % call predicate with integers , mapints/3 , mapargs/2, mapargs/3, mapargs/4 , mapargs_x/4, mapargs_x/5, mapargs_x/6 , mapargs_xx/6 , sfold/4 % structural fold for lists , take/3 , drop/3 , drop_while/3 , take_while/3 % lambda expressions , mcall/2, mcall/3, mcall/4 ]). /** <module> General utilities The predicates in this module can be divided into several groups as listed below. ---+++ Type testing and enumeration * natural/1 - test or enumerate natural numbers * isfinite/1 - check number is non NaN or Inf * int/1 - test or enumerate integers * in/2 ---+++ mathematical utilities * max/3 * min/3 ---+++ list utilities * list_idx1_member/3 % like nth1 but more useful argument order * measure/3 - match list lengths * equal_length/2 - match two list lenghts * getopts/2 * rep/3 - make a list of repeats of the same term * cons/3 - make list from head and tail * decons/3 - get head and tail from list ---+++ term manipulation * copy_head/2 - check terms for same head and arity * unify_args/5 * reinstatevars/3 * reinstatevars/4 ---+++ formatting and parsing * aphrase/2 - like phrase but makes an atom instead of a string * aphrase/3 - like aphrase but takes code list * print_list/1 - write/1 each element, one per line * printq_list/1 - as print_list but quotes atom as with writeq/1 * print_numbered_list/1 - as print_list/1 but with line numbers ---+++ database management * extensible/1 - declare dynamic multifile predicate * bt_assert/1 * bt_retract/1 * strict_assert/1 * strict_retract/1 * browse/2 - browse predicate with unknown arity * current_state/2 * set_state/2 ---+++ file system utilities * dir/2 - directory listing * path_atom/2 - expand paths * expand_path/2 - expand paths ---+++ operating system * open_with/2 - apply shell command to a file (SIDE EFFECTS) * open_with/3 - apply shell command to a file with options (SIDE EFFECTS) * shellcmd/2 - apply shell command to arguments * open_/1 - open with 'open' command (Mac OS X). * fmt_shellcmd/3 - format a shell command with switches and args * hostname/1 ---+++ user interaction * read_char_echo/1 - read one character and echo it immediately * get_key/2 - read and validate keypress * userchk/1 - unary predicate which allows user to force failure * prompt_for_key/3 - print message and get keypress from user ---+++ High order stuff ---++++ modes of computation * retry/1 - keep trying until success * parallel/1 - parallel computation using threads * bt_call/2 - Construct backtrackable operation , on_backtracking/1 ---++++ iteration * exhaust/1 - trigger all solutions to a goal (SIDE EFFECTS) * iterate/3 - apply predicate recursively till failure ---++++ mapping * for_naturals/2 - call predicate with natural numbers N down to 1 * mapints/2 - call predicate with integers * mapints/3 * mapargs/2, mapargs/3, mapargs/4 * mapargs_xx/6 * sfold/4 - structural fold for lists * take/3 * drop/3 * drop_while/3 * take_while/3 ---++++ lambda expressions * mcall/2, mcall/3, mcall/4 */ :- use_module(library(ops)). :- meta_predicate exhaust(0) , retry(0) , iterate(2,?,?) , drop_while(1,?,?) , take_while(1,?,?) , apply_to_nth1(?,2,?,?) , for_naturals(+,1) , for_ints(+,+,1) , mapints(1,?) , mapints(2,?,?) , mapargs(1,?) , mapargs(2,?,?) , mapargs(3,?,?,?) , mapargs_x(+,+,1,?) , mapargs_x(+,+,2,?,?) , mapargs_x(+,+,3,?,?,?) , mapargs_xx(2,?,?,?,?,?) , on_backtracking(0) , bt_call(0,0) , bt_assert(:) , bt_retract(:) , strict_assert(:) , strict_retract(:) , extensible(:) , aphrase(2,?) , aphrase(2,?,?) . :- multifile user:path/2. :- multifile user:demo/1. :- dynamic current_state/2. %% extensible(+P) is det. % declares 'extensible' predicates, ie ones that can have new clauses % added in other files. Equivalent to dynamic(P), multifile(P). extensible(P) :- dynamic(P), multifile(P). %% natural(+N) is semidet. %% natural(-N:natural) is multi. % % Means N is a natural number (includes 0). If N is % a variable, succeeds an infinite number of times on backtracking, % returning all natural numbers. natural(N) :- (var(N) -> between(0,inf,N); integer(N), N>=0). %% int(+N) is semidet. %% int(-N:integer) is multi. % % Means N is an integer. If N is % a variable, succeeds an infinite number of times on backtracking, % returning all integers starting at zero and interleaving positive % and negative values. int(N) :- nonvar(N), integer(N). int(N) :- var(N), (N=0; (between(1,inf,M), (N=M; N is -M))). %% isfinite(+N:number) is semidet. % % Succeeds when N is a finite number. isfinite(N):- catch(_ is N+0,error(_,_),fail). % !! workable hack %% in(+X,Set) is semidet. %% in(-X,Set) is nondet. % % Simple testing and enumration of values in some sets. % Set can be % * {A,B,_} % Explicit list of values. % * natural % Natural numbers starting from 0. % * integer % Natural numbers. % * real % Real (floating point) numbers. % * A..B % Integer range from A to B inclusive. % * A--B % Closed interval [A,B]. X in A--\B :- X in A--(\B). X in \A--(\B):- !, ch(A<X), ch(X<B). X in \A--B :- !, ch(A<X), ch(X=<B). X in A--(\B) :- !, ch(A=<X), ch(X<B). X in A--B :- !, ch(A=<X), ch(X=<B). X in A..B :- integer(A), integer(B), between(A,B,X). X in {CList} :- member_clist(X,CList). X in natural :- natural(X). % enumerate! X in integer :- int(X). % enumerates! X in real :- number(X). % same as X :: real ch(_ =<inf) :- !. ch(inf=< _ ) :- !, fail. ch(-inf=< _) :- !. ch(_ =<(-inf)) :- !, fail. ch(A=<B) :- !, A=<B. ch(inf<_ ) :- !, fail. ch(_ <inf) :- !. ch(_ <(-inf)) :- !, fail. ch(-inf<_ ) :- !. ch(A<B) :- !, A<B. %% exhaust(:Goal) is det. % % Repeat Goal until failure, then succeed. exhaust(Q) :- call(Q), fail; true. %% iterate(+P:pred(A,A), X:A, Y:A) is semidet. % apply P recursively to X until failure, then return final value Y. iterate(P,X,Y) :- call(P,X,Z) -> iterate(P,Z,Y); Y=X. %% sfold(Functor,Initial,L:list,Final) is semidet. % *Structural* fold applied to a term, % rather than a relational fold using a predicate name. sfold(_,E,[],E). sfold(O,E,[X|XX],R) :- R=..[O,X,YY], sfold(O,E,XX,YY). %% dir( +Pattern, -File) is nondet. % % Directory listing for directory matching Pattern. File % names are returned one by one on backtracking. dir(Pattern,File) :- expand_file_name(Pattern,List), member(File,List). %% path_atom( +Spec:path_expr, -Path:atom) is nondet. % % Expand a 'path expression' into a flat path (an atom). % A path expression is defined by: % == % path_expr ---> atom % literal path component name % ; path_expr/atom % child of path_expr % ; path_macro % a previously defined abbr for a path. % == % A path_macro is defined using path/2. path_atom(P,C) :- path(P,C), atom(C). path_atom(PA/B,C) :- once((nonvar(C); nonvar(B); nonvar(PA))), path_atom(PA,A), concat3atoms(A,'/',B,C). path_atom(Path,Atom) :- path(Path, Def), \+atom(Def), path_atom(Def,Atom). path_atom(Path,Atom) :- nonvar(Path), \+path(Path,_), Path\=_/_, Atom=Path. concat3atoms(A,B,C,ABC) :- nonvar(A), nonvar(B), nonvar(C), !, concat_atom([A,B,C],ABC). concat3atoms(_,_,_,ABC) :- var(ABC), !, fail. concat3atoms(A,B,C,ABC) :- nonvar(C), !, atom_concat(AB,C,ABC), atom_concat(A,B,AB). concat3atoms(A,B,C,ABC) :- nonvar(A), !, atom_concat(A,BC,ABC), atom_concat(B,C,BC). concat3atoms(A,B,C,ABC) :- maplist(atom_codes,[B,ABC],[BX,ABCX]), append(ABX,CX,ABCX), append(AX,BX,ABX), maplist(atom_codes,[A,C],[AX,CX]). %% expand_path( +P:path_expr, -FP:atom) is semidet. % % Expand path_exp including wildcards to fully qualified path expand_path(P,FP) :- path_atom(P,PP), expand_file_name(PP,[FP]). %% open_with( +Program:atom, +Thing:path_expr, +Options:list) is semidet. %% open_with( +Program:atom, +Thing:path_expr) is semidet. % % The only option is bg, which adds "&" to make command execute in background. open_with(Q,P) :- open_with(Q,P,[]). open_with(Q,P,Opts) :- expand_path(P,FP), (member(bg,Opts) -> OO=' &'; OO=''), sformat(S,'~w ~q~w',[Q,FP,OO]), shell(S). %% open_( +Thing:path_expr) is semidet. % Equivalent to open_with(open,Thing). open_(P) :- open_with(open,P). %% shellcmd( +Head:atom, +Args:list(atom)) is det. % % Execute a shell command on a given list of arguments shellcmd(Head,Args) :- concat_atom([Head|Args],' ',Cmd), shell(Cmd,_Status). %% fmt_shellcmd( +Prog:atom, +Args:list(shellarg), -Cmd) is det. % make a shell command string. fmt_shellcmd(Prog,Args,Cmd) :- phrase(utils:shellcmd(l_(Args)),FArgs), concat_atom([Prog|FArgs],' ',Cmd). shellcmd(l_([])) --> !, []. shellcmd(l_([H|T])) --> !, shellcmd(H), shellcmd(l_(T)). shellcmd(s(N,V)) --> !, shellcmd(s(N)), shellcmd(V). shellcmd(q(X)) --> !, { concat_atom(['"',X,'"'],A) }, [A]. shellcmd(s(N)) --> !, { (atom_codes(N,[_]) -> F='-' ; F='--'), atom_concat(F,N,A) }, [A]. shellcmd(l(X)) --> [X]. %% read_char_echo( -C:atom) is det. % % Read a single character from the current input, % echo it to the output. read_char_echo(C) :- get_single_char(Code), put_code(Code), flush_output, char_code(C,Code). %% set_state( +Key, +Value) is det. % % Maintains a mutable global set of Key-Value pairs, sets the value % associated with Key to Value. set_state(Flag,Value) :- ground(Flag), retractall(current_state(Flag,_)), assert(current_state(Flag,Value)). %% current_state( -Key, -Value) is nondet. %% current_state( +Key, -Value) is semidet. % % Lookup the value associated with Key, or enumerate all the % key value pairs. %% parallel( +List:list(query)) is semidet. % % Use this by giving a list of queries of the form % [Vars2:Goal, Vars2:Goal2, ...] % where Vars is the term that each thread must return % when it has finished computing its Goal. The % parallel predicate finishes when all the threads % have finished, and should result in all the Vars % being bound appropriately. parallel(Queries) :- maplist(async,Queries,Collecters), maplist(call,Collecters). % these are used to initiate and wait for each % computation thread. async_collect(Id,X:_) :- thread_join(Id,exited(X)). async(X:Goal,utils:async_collect(Id,X:_)) :- thread_create((Goal,thread_exit(X)),Id,[]). %% browse( +PredSpec, -Goal) is nondet. % % PredSpec is a term like (PredicateName/Arity). Goal % is unified with solutions of PredicateName/Arity. browse(P/A,Goal) :- current_predicate(P/A), length(L,A), Goal=..[P|L], call(Goal). %% aphrase(P:phrase(code), -A:atom, +S:list(code)) is nondet. %% aphrase(P:phrase(code), +A:atom, -S:list(code)) is nondet. %% aphrase(P:phrase(code), -A:atom, -S:list(code)) is nondet. %% aphrase(P:phrase(code), -A:atom) is nondet. % % Generate or parse an atom using given DCG phrase P. % aphrase(P,A) is equivalent to aphrase(P,A,_). aphrase(X,A) :- aphrase(X,A,_). aphrase(X,A,S) :- ( ground(A) -> atom_codes(A,S), phrase(X,S) ; phrase(X,S), atom_codes(A,S)). %% print_list( +L:list) is det. % % Print a list, one item per line. print_list([]) :- writeln('~'), nl. print_list([H|T]) :- print(H), nl, print_list(T). %% printq_list( +L:list) is det. % % Print a list, one item per line, as with writeq/1. printq_list([]) :- writeln('~'), nl. printq_list([H|T]) :- writeq(H), nl, printq_list(T). %% print_numbered_list( +L:list) is det. % % Print a list with numbered lines. print_numbered_list(L) :- length(L,Max), number_codes(Max,MC), length(MC,Width), print_num_list(Width,1,L). print_num_list(_,_,[]) :- nl. print_num_list(Width,N,[H|T]) :- succ(N,M), copy_term(H,H1), numbervars(H1,0,_), number_codes(N,NC), " "=[Pad], padleft(Pad,Width,NC,PNC), format('~s. ~q\n',[PNC,H1]), print_num_list(Width,M,T). padleft(_,W,In,In) :- length(In,W). padleft(P,W,In,[P|Out]) :- succ(V,W), padleft(P,V,In,Out). %% get_key( +Valid:list(char), -C:char) is det. % % Get and validate a key press from the user. The character % must be one of the ones listed in Valid, otherwise, an % error message is printed and the user prompted again. get_key(Valid,C) :- read_char_echo(D), nl, ( member(D,Valid) -> C=D ; D='\n' -> get_key(Valid,C) % this improves interaction with acme ; format('Unknown command "~q"; valid keys are ~q.\n', [D,Valid]), write('Command? '), get_key(Valid,C)). %% userchk(T) is semidet. % % Write T and ask this user if it is ok. User presses y or n. % userchk succeeds if if the keypress was y and fails if it was n. userchk(T) :- prompt_for_key(T,[y,n],y). %% prompt_for_key( +Msg:atom, +Keys:list(char), -Key:char) is semidet. % % Prompt user for a keypress. Prompt message is Msg, and valid keys are % listed in Keys. prompt_for_key(Msg,Keys,Key) :- format('~p ~q? ',[Msg,Keys]), get_key(Keys,Key). % ------------------- TERM MANIPULATION ------------------------------ %% copy_head(+T1,-T2) is det. %% copy_head(+T1,+T2) is semidet. % % true if T1 and T2 have the same head and arity copy_head(T1,T2) :- functor(T1,N,A), functor(T2,N,A). %% reinstatevars( F:atom, V:list, Eh, What) is nondet. %% reinstatevars( V:list, Eh, What) is nondet. % % Reverse of numbervars. Each '$VAR'(N) subterm of X is replaced % with the Nth element of V, which can be uninstantiated on entry % reinstatevars/4 uses an arbitrary functor F instead of $VAR. reinstatevars(V,'$VAR'(N),Y) :- !, nth0(N,V,Y). reinstatevars(_,X,Y) :- atomic(X), !, Y=X. reinstatevars(V,X,Y) :- mapargs(reinstatevars(V),X,Y). reinstatevars(F,V,T,Y) :- functor(T,F,1), !, arg(1,T,N), nth0(N,V,Y). reinstatevars(_,_,X,Y) :- atomic(X), !, Y=X. reinstatevars(F,V,X,Y) :- mapargs(reinstatevars(F,V),X,Y). %% unify_args( Src, SrcIndex, Dest, DestIndex, Num) is det. % % this unifies N consecutive arguments of Src and Dest starting % from SI and DI in each term respectively unify_args(_,_,_,_,0). unify_args(Src,SI,Dest,DI,N) :- arg(SI,Src,X), arg(DI,Dest,X), !, succ(SI,SI2), succ(DI,DI2), succ(N2,N), unify_args(Src,SI2,Dest,DI2,N2). % ---------------------- LIST UTILITIES ----------------------------- %member_clist(_,Z) :- var(Z), !, fail. member_clist(A,A) :- A\=(_,_). member_clist(A,(A,_)). member_clist(A,(_,B)) :- member_clist(A,B). %% measure(Ruler,In,Out) is det. % true if Out is the same length as Ruler but matches In as far as possible measure([],_I,[]). measure([_|R],[],[_|O]) :- measure(R,[],O). measure([_|R],[X|I],[X|O]) :- measure(R,I,O). %% equal_length( ?In, ?Out) is nondet. % equal_length( +L1:list, -L2:list) is det. % equal_length( -L1:list, +L2:list) is det. % % True if L1 and L2 are the same length. equal_length([],[]). equal_length([_|T1],[_|T2]) :- equal_length(T1,T2). %split_at(0,T,I-I,T). %split_at(N,[H|T],[H|I1]-Z,T1) :- succ(M,N), split_at(M,T,I1-Z,T1). %split_at2(0,T,I-I,T). %split_at2(N,[H|T],[H|I1]-Z,T1) :- split_at2(M,T,I1-Z,T1), succ(M,N). %% max(+X:number, +Y:number, -Z:number) is det. % % Unify Z with the larger of X and Y. Legal values are % any numerical value or inf or -inf. max(_,inf,inf) :- !. max(inf,_,inf) :- !. max(X,-inf,X) :- !. max(-inf,X,X) :- !. max(X,Y,Z) :- X<Y -> Z=Y; Z=X. %% max(+X:number, +Y:number, -Z:number) is det. % % Unify Z with the larger of X and Y. Legal values are % any numerical value or inf or -inf. min(_,-inf,-inf) :- !. min(-inf,_,-inf) :- !. min(X,inf,X) :- !. min(inf,X,X) :- !. min(X,Y,Z) :- X<Y -> Z=X; Z=Y. %% list_idx1_member( +L:list(A), +N:natural, ?X:A) is semidet. %% list_idx1_member( ?L:list(A), ?N:natural, ?X:A) is nondet. % % Equivalent to nth1(N,L,X). list_idx1_member(L,I,X) :- nth1(I,L,X). %% getopts( +Opts:list(option), ?Spec:list(optspec)) is det. % % Get value from option list. % == % option(A) ---> term(A). % optspec ---> option(A)/A. % == getopts(OptsIn,Spec) :- maplist(getopt(OptsIn),Spec). getopt(OptsIn,Option/Default) :- option(Option,OptsIn,Default). %% cons( ?Head:A, ?Tail:list(A), ?List:list(A)) is det. % % List constructor. cons(H,T,[H|T]). %% decons( ?Head:A, ?List:list(A), ?Tail:list(A)) is det. % % List deconstructor. decons(H,[H|T],T). % ---------------------- MAPPING, HIGH ORDER STUFF --------------------- %% for_naturals(+N:natural, P:pred(natural)) is nondet. % apply predicate to each natural number from 1 to N (backwards) for_naturals(0,_). for_naturals(N,P) :- succ(M,N), call(P,N), for_naturals(M,P). %% mapints( +P:pred(integer,A), +R:intrange, -X:list) is nondet. %% mapints( +P:pred(integer), +R:intrange) is nondet. % % Mapping predicates over lists of integers. Range is like M..N. % mapints/3 maps 2 argument predicate over implicit list of % integers M..N and explicit list of values X. mapints(_,M..N) :- N<M, !. mapints(P,M..N) :- call(P,M), plus(M,1,L), mapints(P,L..N). mapints(_,M..N,[]) :- N<M, !. mapints(P,M..N,[X|T]) :- call(P,M,X), plus(M,1,L), mapints(P,L..N,T). %% rep( +N:natural, ?X:A, -L:list(A)) is det. %% rep( -N:natural, ?X:A, -L:list(A)) is multi. % Make a list consisting of N repeats of the same term. If called % with N unbount, creates progressively longer and longer lists % on backtracking. rep(0,_,[]). rep(N,A,[A|X]) :- ( nonvar(N) -> succ(M,N), rep(M,A,X) ; rep(M,A,X), succ(M,N) ). %% mapargs( P:pred(A,B,C), T1:tuple(F,A), T2:tuple(F,B), T3:tuple(F,C)) is nondet. %% mapargs( P:pred(A,B), T1:tuple(F,A), T2:tuple(F,B)) is nondet. %% mapargs( P:pred(A), T1:term) is nondet. % % Map predicate over to args of a term preserving head. % A tuple(F,A) is a term with head functor F and any number of arguments % of type A, ie % == % tuple(F,A) ---> F ; F(A) ; F(A,A) ; F(A,A,A) ; .. . % == mapargs(P,T1) :- functor(T1,_,N), mapargs_x(1,N,P,T1). mapargs(P,T1,T2) :- ( nonvar(T1) -> functor(T1,F,N), functor(T2,F,N) ; functor(T2,F,N), functor(T1,F,N)), mapargs_x(1,N,P,T1,T2). mapargs(P,T1,T2,T3) :- functor(T1,F,N), functor(T2,F,N), functor(T3,F,N), mapargs_x(1,N,P,T1,T2,T3). mapargs_x(I,N,P,T1) :- ( I>N -> true ; arg(I,T1,X1), call(P,X1), succ(I,J), mapargs_x(J,N,P,T1)). mapargs_x(I,N,P,T1,T2) :- ( I>N -> true ; arg(I,T1,X1), arg(I,T2,X2), call(P,X1,X2), succ(I,J), mapargs_x(J,N,P,T1,T2)). mapargs_x(I,N,P,T1,T2,T3) :- ( I>N -> true ; arg(I,T1,X1), arg(I,T2,X2), arg(I,T3,X3), call(P,X1,X2,X3), succ(I,J), mapargs_x(J,N,P,T1,T2,T3)). %% drop( +N:natural, +In:list(A), -Out:list(A)) is det. drop(0,T,T). drop(N,[_|T],V) :- succ(M,N), drop(M,T,V). %% take( +N:natural, +In:list(A), -Out:list(A)) is det. take(N,T,X) :- length(X,N), append(X,_,T). %% drop_while( +P:pred(A), +In:list(A), -Out:list(A)) is det. % % Remove all elements from head of In that are accepted by P % and return the remained in Out. drop_while(P,[X|T],V) :- call(P,X) -> drop_while(P,T,V); V=[X|T]. %% take_while( +P:pred(A), +In:list(A), -Out:list(A)) is det. % % Remove all elements from head of In that are accepted by P % and return them in Out. take_while(P,[X|T],O) :- call(P,X) -> O=[X|V], take_while(P,T,V); O=[]. %% retry( :Goal) is det. % % Keep retrying Goal until it succeeds. Only makes sense if Goal % has side effects. Might be nonterminating. retry(G) :- once((repeat,G)). %% apply_to_nth1( N:natural, Op:pred(A,A), +In:list(A), +Out:list(A)) is nondet. % % Apply predicate Op to the N th element of list In and unify Out with the result. %apply_to_nth1(N,Op,Old,Init) :- % ( nonvar(N) % -> succ(M,N), split_at(M,Old,Init-[Y|Tail],[X|Tail]) % ; split_at2(M,Old,Init-[Y|Tail],[X|Tail]), succ(M,N) % ), % call(Op,X,Y). apply_to_nth1(1,P,[X|XX],[Y|XX]) :- call(P,X,Y). apply_to_nth1(N,P,[X|X1],[X|Y1]) :- nonvar(N), !, N>1, succ(M,N), apply_to_nth1(M,P,X1,Y1). apply_to_nth1(N,P,[X|X1],[X|Y1]) :- var(N), !, apply_to_nth1(M,P,X1,Y1), succ(M,N). %% mapargs_xx( +P:pred(A,B), +Src:term(_,A), +SrcIndex:natural, +Dest:term(_,B), +DestIndex:natural, +N:natural) is nondet. % % Maps predicate P over N consecutive arguments of Src and Dest. Starts % at SrcIndex th argument of Src and DestIndex th argument of Dest. mapargs_xx(_,_,_,_,_,0). mapargs_xx(Pred,Src,SI,Dest,DI,N) :- arg(SI,Src,SX), arg(DI,Dest,DX), call(Pred,SX,DX), !, succ(SI,SI2), succ(DI,DI2), succ(N2,N), mapargs_xx(Pred,Src,SI2,Dest,DI2,N2). %% mcall(P:pred(A), X:A) is nondet. %% mcall(P:pred(A,B), X:A, Y:B) is nondet. %% mcall(P:pred(A,B,C), X:A, Y:B, Z:C) is nondet. %% mcall(P:pred(A,B,C,D), X:A, Y:B, Z:C, W:D) is nondet. % % Like call/N but P can additionally be a lambda expression in one of several % forms: % * Tuple :- Body % If functor(Tuple,\,N), Body is executed after unifying tuple arguments % with arguments to mcall, eg =mcall(\(X):-member(X,[a,b,c]),Y)= is equivalent % to member(Y,[a,b,c]), or =mcall(\(X,Y):-nth1(X,[a,b,c],Y),2,c)=. % * Tuple % Equivalent to Tuple:-true. mcall(P,A) :- mc(P,\(A),Q), Q. mcall(P,A,B) :- mc(P,\(A,B),Q), Q. mcall(P,A,B,C) :- mc(P,\(A,B,C),Q), Q. mcall(P,A,B,C,D) :- mc(P,\(A,B,C,D),Q), Q. mc(Tuple:-Body,Params,Goal) :- !, copy_term(Tuple/Body,Params/Goal). mc(Tuple,Params,true) :- functor(Tuple,\,_), !, copy_term(Tuple,Params). mc(P,Params,apply(P,Args)) :- Params=..[\|Args]. %% on_backtracking( :Goal) is det. % % The first time this is called, it succeeds and does nothing. % On backtracking, Goal is called and then a failure is generated. on_backtracking(_). on_backtracking(P) :- P, !, fail. %% bt_call( :Do, :Undo) is nondet. % % Creates a backtrackable operation from a non-backtrackable Do % operation and a corresponding operation to undo it. Do can % be non-deterministic, in which case bt_call(Do,Undo) will also % have multiple solutions. Undo is called inside once/1. % % bt_call/2 is implemented both as a predicate and as a goal % expansion (see goal_expansion/2). bt_call(Do,Undo) :- Do, (true; once(Undo), fail). user:goal_expansion( bt_call(Do,Undo), (Do, (true; once(Undo), fail))). /* Might include these at some point % apply lambda term to another term app(X\\F,Y,G) :- !, copy_term(X\\F,Y\\G). app(T,A,Z) :- addargs(T,[A],Z). app(T,A,B,Z) :- addargs(T,[A,B],Z). app(T,A,B,C,Z) :- addargs(T,[A,B,C],Z). applist(F,N,A,Z) :- length(Z,N), maplist(app(F),A,Z). applist(F,N,A,B,Z) :- length(Z,N), maplist(app(F),A,B,Z). applist(F,N,A,B,C,Z) :- length(Z,N), maplist(app(F),A,B,C,Z). */ % ------------------ DATABASE ------------------------------ %% bt_assert(Clause) is det. % Backtrackable assert. bt_assert(H) :- bt_call(assert(H),retract(H)). %% bt_retract(Clause) is det. % Backtrackable retract. bt_retract(H) :- bt_call(retract(H), assert(H)). %% strict_assert(Fact) is semidet. % % Asserts fact only if it is not already true. Fails % if fact is already provable. Retracts fact on backtracking. strict_assert(H) :- \+call(H), bt_call(assert(H),retract(H)). %% strict_retract(Fact) is semidet. % % Retracts fact only if it is currently in the database. Fails % if fact is not provable. Reasserts fact on backtracking. strict_retract(H) :- call(H), bt_call(retract(H), assert(H)). % when loaded, this sets the hostname/1 predicate. :- dynamic hostname/1. %% hostname( -A:atom) is det. % % Unifies A with the computer's hostname. This is set when the % module is loaded by calling the system command 'hostname -s'. % init_hostname is det - read hostname from UNIX command hostname. init_hostname :- setup_call_cleanup( open(pipe('hostname -s'),read,SID), (read_line_to_codes(SID,C), atom_codes(H,C), retractall(hostname(_)), assert(hostname(H))), close(SID)). :- ( hostname(H) -> format('% hostname already set to ~w\n',[H]) ; init_hostname, hostname(H), format('% hostname set to ~w\n',[H]) ). % Comma lists % ie, lists built up using (,) as a pairing functor % Note, these functor lists do NOT have a nil element - the % last item in the list is the 2nd argument to the final % functor term, which can therefore be a term headed by any % other functor. Eg: % (1,(2,3)) <-> [1,2,3] % (1,(2,(3+4)) <-> [1,2,(3+4)] %% cl_list( +CL:clist(A), -L:list(A)) is det. %% cl_list( -CL:clist(A), +L:list(A)) is det. % % Convert between comma lists and ordinary lists cl_list((A,B),[A|BL]) :- cl_list(B,BL). cl_list(A,[A]) :- A\=(_,_). %% cl_length( +L:clist, -N:natural) is det. %% cl_length( -L:clist, +N:natural) is det. % % Length of a comma-list. cl_length((_,B),N) :- cl_length(B,M), succ(M,N). cl_length(X,1) :- X\=(_,_). %% cl_list_vt( +CL:clist(A), -L:list(A)) is det. %% cl_list_vt( -CL:clist(A), +L:list(A)) is det. % % Convert between comma lists (with open tails) and ordinary lists. cl_list_vt(FL,[FL]) :- var(FL), !. cl_list_vt(FL,[A|BL]) :- FL = (A,B), cl_list_vt(B,BL). cl_list_vt(A,[A]) :- A\=(_,_). %% cl_length_vt( +L:clist, -N:natural) is det. %% cl_length_vt( -L:clist, +N:natural) is det. % % Length of a comma-list with possible variable tail. % This version handles lists where the last element is variable (counts as 1) cl_length_vt(FL,1) :- var(FL), !. cl_length_vt(FL,N) :- FL=(_,B), cl_length_vt(B,M), succ(M,N). cl_length_vt(FL,1) :- FL\=(_,_). %% cl_member(-X, +L:clist) is nondet. % List membership for comma lists. cl_member(X,(X,_)). cl_member(X,(_,T)) :- cl_member(X,T). cl_member(X,X) :- X\=(_,_).