From e747b7f9c1123695a2fe2181de1afbda618885e5 Mon Sep 17 00:00:00 2001 From: Vitor Santos Costa Date: Sat, 13 Oct 2018 08:45:40 +0100 Subject: [PATCH] docs --- C/gprof.c | 70 +++---- docs/md/fli.md | 8 +- packages/ProbLog/problog_lbfgs.yap | 1 - pl/callcount.yap | 204 ++++++++++---------- pl/control.yap | 297 ++++++++++------------------- pl/profile.yap | 155 ++++++++------- pl/setof.yap | 2 +- 7 files changed, 324 insertions(+), 413 deletions(-) diff --git a/C/gprof.c b/C/gprof.c index 3a8b64dec..1de391638 100755 --- a/C/gprof.c +++ b/C/gprof.c @@ -35,7 +35,7 @@ * Revision 1.3 2006/01/17 14:10:40 vsc * YENV may be an HW register (breaks some tabling code) * All YAAM instructions are now brackedted, so Op introduced an { and EndOp introduces an }. This is because Ricardo assumes that. -* Fix attvars when COROUTING is undefined. +* Fix attvars * * Revision 1.2 2005/12/23 00:20:13 vsc * updates to gprof @@ -47,40 +47,40 @@ * * *************************************************************************/ +/// @file gprof.c -/** @defgroup Tick_Profiler Tick Profiler -@ingroup Profiling -@{ - -The tick profiler works by interrupting the Prolog code every so often -and checking at each point the code was. The profiler must be able to -retrace the state of the abstract machine at every moment. The major -advantage of this approach is that it gives the actual amount of time -being spent per procedure, or whether garbage collection dominates -execution time. The major drawback is that tracking down the state of -the abstract machine may take significant time, and in the worst case -may slow down the whole execution. - -The following procedures are available: - -+ profinit - - -Initialise the data-structures for the profiler. Unnecessary for -dynamic profiler. - -+ profon - - -Start profiling. - -+ profoff - - -Stop profiling. - - -*/ +/** @addtogroup Tick_Profiler + * @ingroup Profiling@{ + * + * The tick profiler works by interrupting the Prolog code every so often + * and checking at each point the code was. The pro/filer must be able to + * retrace the state of the abstract machine at every moment. The major + * advantage of this approach is that it gives the actual amount of time + * being spent per procedure, or whether garbage collection dominates + * execution time. The major drawback is that tracking down the state of + * the abstract machine may take significant time, and in the worst case + * may slow down the whole execution. + * + * The following procedures are available: + * + * + profinit/0 + * Initialise the data-structures for the profiler. Unnecessary for + * dynamic profiler. + * + * + profon/0 + * Start profiling. + * + * + profoff/0 + * Stop profiling. + * + * + profoff/0 + * Stop profiling. + * + * + showprofres/0 and showprofres/1 + * Stop tick counts per predicate. + * + * + */ #ifdef SCCS static char SccsId[] = "%W% %G%"; @@ -841,7 +841,7 @@ static void RemoveCode(CODEADDR clau) } } -static int +static Int showprofres( USES_REGS1 ) { buf_ptr buf; diff --git a/docs/md/fli.md b/docs/md/fli.md index 5888f0375..7bd0b5e61 100644 --- a/docs/md/fli.md +++ b/docs/md/fli.md @@ -13,11 +13,11 @@ predicates in a language other than Prolog. Under Unix systems, most language implementations were linkable to `C`, and the first interface exported the YAP machinery to the C language. YAP also implements most of the SWI-Prolog foreign language interface. This gives portability with a number of SWI-Prolog packages and avoids garnage collection by using @ref slotInterface. Last, a new C++ based interface is being designed to work with the swig (www.swig.orgv) interface compiler. - +@} @defgroup ChYInterface YAP original C-interface -@{ @ingroup fli_c_cxx +@{ Before describing in full detail how to interface to C code, we will examine a brief example. @@ -50,8 +50,8 @@ system. @} @defgroup CallYAP Using the compiler: -@{ @ingroup ChYInterface +@{ Under Linux you should use: @@ -1588,4 +1588,4 @@ the future we plan to split this library into several smaller libraries @} -@} \ No newline at end of file +@} diff --git a/packages/ProbLog/problog_lbfgs.yap b/packages/ProbLog/problog_lbfgs.yap index e18049a1d..6026e5f6a 100644 --- a/packages/ProbLog/problog_lbfgs.yap +++ b/packages/ProbLog/problog_lbfgs.yap @@ -833,7 +833,6 @@ gradient_descent :- forall(tunable_fact(FactID,GroundTruth), (XZ is 0.5, X[FactID] <== XZ,set_fact_probability(FactID,XZ))), problog_flag(sigmoid_slope,Slope), - lbfgs_set_parameter(min_step, Solver, 0.0), lbfgs_run(Solver,BestF), format('~2nOptimization done~nWe found a minimum ~4f.~n',[BestF]), forall(tunable_fact(FactID,GroundTruth), set_tunable(FactID,X)), diff --git a/pl/callcount.yap b/pl/callcount.yap index d3ba19a37..8c160613d 100644 --- a/pl/callcount.yap +++ b/pl/callcount.yap @@ -15,57 +15,61 @@ * * *************************************************************************/ -%% @{ /** - @file callcount.yap - @short support call counting. - - @defgroup Profiling Profiling Prolog Programs - @ingroup extensions - -YAP includes two profilers. The count profiler keeps information on the -number of times a predicate was called. This information can be used to -detect what are the most commonly called predicates in the program. The -count profiler can be compiled by setting YAP's flag profiling -to `on`. The time-profiler is a `gprof` profiler, and counts -how many ticks are being spent on specific predicates, or on other -system functions such as internal data-base accesses or garbage collects. - -The YAP profiling sub-system is currently under -development. Functionality for this sub-system will increase with newer -implementation. - - + * @file callcount.yap + * @short support call counting. + * + * @defgroup Profiling Profiling Prolog Programs + * @brief the clock and the tick profilers. + * @ingroup extensions + * @{ + * + * YAP includes two profilers. The count profiler keeps information on the + * number of times a predicate was called. This information can be used to + * detect what are the most commonly called predicates in the program. The + * count profiler can be compiled by setting YAP's flag profiling + * to `on`. The time-profiler is a `gprof` profiler, and counts + * how many ticks are being spent on specific predicates, or on other + * system functions such as internal data-base accesses or garbage collects. + * + * + Call_Counting + * + + * */ /** - @defgroup Call_Counting Counting Calls - @ingroup Profiling - @{ - -Predicates compiled with YAP's flag call_counting set to -`on` update counters on the numbers of calls and of -retries. Counters are actually decreasing counters, so that they can be -used as timers. Three counters are available: - -+ `calls`: number of predicate calls since execution started or since -system was reset; -+ `retries`: number of retries for predicates called since -execution started or since counters were reset; -+ `calls_and_retries`: count both on predicate calls and -retries. - -These counters can be used to find out how many calls a certain -goal takes to execute. They can also be used as timers. - -The code for the call counters piggybacks on the profiling -code. Therefore, activating the call counters also activates the profiling -counters. - -These are the predicates that access and manipulate the call counters. +@} */ + +/** + * @defgroup Call_Counting Counting Calls + * @ingroup Profiling + * @{ + * + * Predicates compiled with YAP's flag call_counting set to + * `on` update counters on the numbers of calls and of + * retries. Counters are actually decreasing counters, so that they can be + * used as timers. Three counters are available: + * + * + `calls`: number of predicate calls since execution started or since + * system was reset; + * + `retries`: number of retries for predicates called since + * execution started or since counters were reset; + * + `calls_and_retries`: count both on predicate calls and + * retries. + * + * These counters can be used to find out how many calls a certain + * goal takes to execute. They can also be used as timers. + * + * The code for the call counters piggybacks on the profiling + * code. Therefore, activating the call counters also activates the profiling + * counters. + * + * These are the predicates that access and manipulate the call counters. + * */ + :- system_module( '$_callcount', [call_count/3, call_count_data/3, call_count_reset/0], []). @@ -74,68 +78,68 @@ These are the predicates that access and manipulate the call counters. /** @pred call_count_data(- _Calls_, - _Retries_, - _CallsAndRetries_) - - -Give current call count data. The first argument gives the current value -for the _Calls_ counter, next the _Retries_ counter, and last -the _CallsAndRetries_ counter. - -*/ + * + * + * Give current call count data. The first argument gives the current value + * for the _Calls_ counter, next the _Retries_ counter, and last + * the _CallsAndRetries_ counter. + * + * */ call_count_data(Calls, Retries, Both) :- '$call_count_info'(Calls, Retries, Both). /** @pred call_count_reset - - -Reset call count counters. All timers are also reset. - -*/ + * + * + * Reset call count counters. All timers are also reset. + * + */ call_count_reset :- '$call_count_reset'. /** @pred call_count(? _CallsMax_, ? _RetriesMax_, ? _CallsAndRetriesMax_) - - -Set call counters as timers. YAP will generate an exception -if one of the instantiated call counters decreases to 0: - -+ _CallsMax_ - - throw the exception `call_counter` when the -counter `calls` reaches 0; - -+ _RetriesMax_ - - throw the exception `retry_counter` when the -counter `retries` reaches 0; - -+ _CallsAndRetriesMax_ - - throw the exception -`call_and_retry_counter` when the counter `calls_and_retries` -reaches 0. - - YAP will ignore counters that are called with unbound arguments. - -Next, we show a simple example of how to use call counters: - -~~~~~{.prolog} - ?- yap_flag(call_counting,on), [-user]. l :- l. end_of_file. yap_flag(call_counting,off). - -yes - -yes - ?- catch((call_count(10000,_,_),l),call_counter,format("limit_exceeded.~n",[])). - -limit_exceeded. - -yes -~~~~~ -Notice that we first compile the looping predicate `l/0` with -call_counting `on`. Next, we catch/3 to handle an -exception when `l/0` performs more than 10000 reductions. - - + * + * + * Set call counters as timers. YAP will generate an exception + * if one of the instantiated call counters decreases to 0: + * + * + _CallsMax_ + * + * throw the exception `call_counter` when the + * counter `calls` reaches 0; + * + * + _RetriesMax_ + * + * throw the exception `retry_counter` when the + * counter `retries` reaches 0; + * + * + _CallsAndRetriesMax_ + * + * throw the exception + * `call_and_retry_counter` when the counter `calls_and_retries` + * reaches 0. + * + * YAP will ignore counters that are called with unbound arguments. + * + * Next, we show a simple example of how to use call counters: + * + * ~~~~~{.prolog} + * ?- yap_flag(call_counting,on), [-user]. l :- l. end_of_file. yap_flag(call_counting,off). + * + * yes + * + * yes + * ?- catch((call_count(10000,_,_),l),call_counter,format("limit_exceeded.~n",[])). + * + * limit_exceeded. + * + * yes + * ~~~~~ + * Notice that we first compile the looping predicate `l/0` with + * call_counting `on`. Next, we catch/3 to handle an + * exception when `l/0` performs more than 10000 reductions. + * + * */ call_count(Calls, Retries, Both) :- '$check_if_call_count_on'(Calls, CallsOn), @@ -150,7 +154,3 @@ call_count(Calls, Retries, Both) :- %% @} -/** -@} -*/ - diff --git a/pl/control.yap b/pl/control.yap index 8cd98ab06..700bae5a0 100644 --- a/pl/control.yap +++ b/pl/control.yap @@ -88,81 +88,81 @@ */ /** @pred forall(: _Cond_,: _Action_) - - -For all alternative bindings of _Cond_ _Action_ can be -proven. The example verifies that all arithmetic statements in the list - _L_ are correct. It does not say which is wrong if one proves wrong. - -~~~~~{.prolog} -?- forall(member(Result = Formula, [2 = 1 + 1, 4 = 2 * 2]), - Result =:= Formula). -~~~~~ - - -*/ + * + * + * For all alternative bindings of _Cond_ _Action_ can be + * proven. The example verifies that all arithmetic statements in the list + * _L_ are correct. It does not say which is wrong if one proves wrong. + * + * ~~~~~{.prolog} + * ?- forall(member(Result = Formula, [2 = 1 + 1, 4 = 2 * 2]), + * Result =:= Formula). + * ~~~~~ + * + * + */ forall(Cond, Action) :- \+((Cond, \+(Action))). /** @pred ignore(: _Goal_) - - -Calls _Goal_ as once/1, but succeeds, regardless of whether -`Goal` succeeded or not. Defined as: - -~~~~~{.prolog} -ignore(Goal) :- - Goal, !. -ignore(_). -~~~~~ - - -*/ + * + * + * Calls _Goal_ as once/1, but succeeds, regardless of whether + * `Goal` succeeded or not. Defined as: + * + * ~~~~~{.prolog} + * ignore(Goal) :- + * Goal, !. + * ignore(_). + * ~~~~~ + * + * + */ ignore(Goal) :- (Goal->true;true). /** @pred if(? _G_,? _H_,? _I_) - -Call goal _H_ once per each solution of goal _H_. If goal - _H_ has no solutions, call goal _I_. - -The built-in `if/3` is similar to `->/3`, with the difference -that it will backtrack over the test. Consider the following -small data-base: - -~~~~~{.prolog} -a(1). b(a). c(x). -a(2). b(b). c(y). -~~~~~ - -Execution of an `if/3` query will proceed as follows: - -~~~~~{.prolog} - ?- if(a(X),b(Y),c(Z)). - -X = 1, -Y = a ? ; - -X = 1, -Y = b ? ; - -X = 2, -Y = a ? ; - -X = 2, -Y = b ? ; - -no -~~~~~ - -The system will backtrack over the two solutions for `a/1` and the -two solutions for `b/1`, generating four solutions. - -Cuts are allowed inside the first goal _G_, but they will only prune -over _G_. - -If you want _G_ to be deterministic you should use if-then-else, as -it is both more efficient and more portable. - -*/ + * + * Call goal _H_ once per each solution of goal _H_. If goal + * _H_ has no solutions, call goal _I_. + * + * The built-in `if/3` is similar to `->/3`, with the difference + * that it will backtrack over the test. Consider the following + * small data-base: + * + * ~~~~~{.prolog} + * a(1). b(a). c(x). + * a(2). b(b). c(y). + * ~~~~~ + * + * Execution of an `if/3` query will proceed as follows: + * + * ~~~~~{.prolog} + * ?- if(a(X),b(Y),c(Z)). + * + * X = 1, + * Y = a ? ; + * + * X = 1, + * Y = b ? ; + * + * X = 2, + * Y = a ? ; + * + * X = 2, + * Y = b ? ; + * + * no + * ~~~~~ + * + * The system will backtrack over the two solutions for `a/1` and the + * two solutions for `b/1`, generating four solutions. + * + * Cuts are allowed inside the first goal _G_, but they will only prune + * over _G_. + * + * If you want _G_ to be deterministic you should use if-then-else, as + * it is both more efficient and more portable. + * + */ if(X,Y,Z) :- ( CP is '$last_choice_pt', @@ -174,75 +174,14 @@ if(X,Y,Z) :- '$call'(Z,CP,if(X,Y,Z),M) ). -/** @pred call( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Closure,...,? Ai,...) is iso - - -Meta-call with extractpattern arguments, where _Closure_ is a closure -that is converted into a goal by appending the _Ai_ additional -arguments. YAP supports up to 10 extra arguments. - -*/ +/** @pred call( Closure,...,? Ai,...) is iso + * + * + * Meta-call with extra pattern arguments, where _Closure_ is a closure + * that is converted into a goal by appending the _Ai_ additional + * arguments. YAP supports up to 10 extra arguments. + * + */ call(X,A) :- '$execute'(X,A). call(X,A1,A2) :- '$execute'(X,A1,A2). @@ -266,11 +205,11 @@ call(X,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10) :- '$execute'(X,A1,A2,A3,A4,A5,A6,A7,A8,A call(X,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11) :- '$execute'(X,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11). /** @pred call_cleanup(: _Goal_, : _CleanUpGoal_) - -This is similar to call_cleanup/1 but with an additional - _CleanUpGoal_ which gets called after _Goal_ is finished. - -*/ + * + * This is similar to call_cleanup/1 but with an additional + * _CleanUpGoal_ which gets called after _Goal_ is finished. + * + */ call_cleanup(Goal, Cleanup) :- '$gated_call'( false , Goal,_Catcher, Cleanup) . @@ -468,32 +407,20 @@ query_to_answer(G, V, Status, Bindings) :- %% @} -%% @{ %% @addtogroup Global_Variables +%% @{ -/** @pred nb_getval(+ _Name_, - _Value_) - - -The nb_getval/2 predicate is a synonym for b_getval/2, -introduced for compatibility and symmetry. As most scenarios will use -a particular global variable either using non-backtrackable or -backtrackable assignment, using nb_getval/2 can be used to -document that the variable is used non-backtrackable. - - -*/ /** @pred nb_getval(+ _Name_,- _Value_) - - -The nb_getval/2 predicate is a synonym for b_getval/2, introduced for -compatibility and symmetry. As most scenarios will use a particular -global variable either using non-backtrackable or backtrackable -assignment, using nb_getval/2 can be used to document that the -variable is used non-backtrackable. - - -*/ + * + * + * The nb_getval/2 predicate is a synonym for b_getval/2, introduced for + * compatibility and symmetry. As most scenarios will use a particular + * global variable either using non-backtrackable or backtrackable + * assignment, using nb_getval/2 can be used to document that the + * variable is used non-backtrackable. + * + */ nb_getval(GlobalVariable, Val) :- '__NB_getval__'(GlobalVariable, Val, Error), (var(Error) @@ -508,31 +435,19 @@ nb_getval(GlobalVariable, Val) :- /** @pred b_getval(+ _Name_, - _Value_) - - -Get the value associated with the global variable _Name_ and unify -it with _Value_. Note that this unification may further -instantiate the value of the global variable. If this is undesirable -the normal precautions (double negation or copy_term/2) must be -taken. The b_getval/2 predicate generates errors if _Name_ is not -an atom or the requested variable does not exist. - -Notice that for compatibility with other systems _Name_ must be already associated with a term: otherwise the system will generate an error. - - -*/ -/** @pred b_getval(+ _Name_,- _Value_) - - -Get the value associated with the global variable _Name_ and unify -it with _Value_. Note that this unification may further instantiate -the value of the global variable. If this is undesirable the normal -precautions (double negation or copy_term/2) must be taken. The -b_getval/2 predicate generates errors if _Name_ is not an atom or -the requested variable does not exist. - - -*/ + * + * + * Get the value associated with the global variable _Name_ and unify + * it with _Value_. Note that this unification may further + * instantiate the value of the global variable. If this is undesirable + * the normal precautions (double negation or copy_term/2) must be + * taken. The b_getval/2 predicate generates errors if _Name_ is not + * an atom or the requested variable does not exist. + * + * Notice that for compatibility with other systems _Name_ must be already associated with a term: otherwise the system will generate an error. + * + * + */ b_getval(GlobalVariable, Val) :- '__NB_getval__'(GlobalVariable, Val, Error), (var(Error) @@ -548,9 +463,9 @@ b_getval(GlobalVariable, Val) :- %% @} -%% @{ %% @addtogroup YAPControl +%% @{ /* This is the break predicate, it saves the importante data about current streams and diff --git a/pl/profile.yap b/pl/profile.yap index 9d42acc75..0ba28626d 100644 --- a/pl/profile.yap +++ b/pl/profile.yap @@ -23,83 +23,80 @@ showprofres/1], []). /** +`* @defgroup Exc_Profiling The Exception Based Tick Profiler. * @ingroup Profiling * @{ - -The count profiler works by incrementing counters at procedure entry or -backtracking. It provides exact information: - -+ Profiling works for both static and dynamic predicates. -+ Currently only information on entries and retries to a predicate -are maintained. This may change in the future. -+ As an example, the following user-level program gives a list of -the most often called procedures in a program. The procedure -list_profile/0 shows all procedures, irrespective of module, and -the procedure list_profile/1 shows the procedures being used in -a specific module. - -~~~~~ -list_profile :- - % get number of calls for each profiled procedure - setof(D-[M:P|D1],(current_module(M),profile_data(M:P,calls,D),profile_data(M:P,retries,D1)),LP), - % output so that the most often called - % predicates will come last: - write_profile_data(LP). - -list_profile(Module) :- - % get number of calls for each profiled procedure - setof(D-[Module:P|D1],(profile_data(Module:P,calls,D),profile_data(Module:P,retries,D1)),LP), - % output so that the most often called - % predicates will come last: - write_profile_data(LP). - -write_profile_data([]). -write_profile_data([D-[M:P|R]|SLP]) :- - % swap the two calls if you want the most often - % called predicates first. - format('~a:~w: ~32+~t~d~12+~t~d~12+~n', [M,P,D,R]), - write_profile_data(SLP). -~~~~~ - - -These are the current predicates to access and clear profiling data: - - - -*/ + * + * The count profiler works by incrementing counters at procedure entry or + * backtracking. It provides exact information: + * + * + Profiling works for both static and dynamic predicates. + * + Currently only information on entries and retries to a predicate + * are maintained. This may change in the future. + * + As an example, the following user-level program gives a list of + * the most often called procedures in a program. The procedure + * list_profile/0 shows all procedures, irrespective of module, and + * the procedure list_profile/1 shows the procedures being used in + * a specific module. + * + * ~~~~~ + * list_profile :- + * % get number of calls for each profiled procedure + * setof(D-[M:P|D1],(current_module(M),profile_data(M:P,calls,D),profile_data(M:P,retries,D1)),LP), + * % output so that the most often called + * % predicates will come last: + * write_profile_data(LP). + * + * list_profile(Module) :- + * % get number of calls for each profiled procedure + * setof(D-[Module:P|D1],(profile_data(Module:P,calls,D),profile_data(Module:P,retries,D1)),LP), + * % output so that the most often called + * % predicates will come last: + * write_profile_data(LP). + * + * write_profile_data([]). + * write_profile_data([D-[M:P|R]|SLP]) :- + * % swap the two calls if you want the most often + * % called predicates first. + * format('~a:~w: ~32+~t~d~12+~t~d~12+~n', [M,P,D,R]), + * write_profile_data(SLP). + * ~~~~~ + * + * + * These are the current predicates to access and clear profiling data: + * + * + * + **/ :- use_system_module( '$_errors', ['$do_error'/2]). - +%% user:prolog_predicate_name()/ +% % hook predicate, taken from SWI-Prolog, for converting possibly explicitly- % qualified callable terms into an atom that can be used as a label for % describing a predicate; used e.g. on the tick profiler defined below :- multifile(user:prolog_predicate_name/2). /** @pred profile_data( ?Na/Ar, ?Parameter, -Data_) - - -Give current profile data on _Parameter_ for a predicate described -by the predicate indicator _Na/Ar_. If any of _Na/Ar_ or - _Parameter_ are unbound, backtrack through all profiled predicates -or stored parameters. Current parameters are: - -+ calls -Number of times a procedure was called. - -+ retries -Number of times a call to the procedure was backtracked to and retried. - - -+ profile_reset - - -Reset all profiling information. - - - - + * + * + * Give current profile data on _Parameter_ for a predicate described + * by the predicate indicator _Na/Ar_. If any of _Na/Ar_ or + * _Parameter_ are unbound, backtrack through all profiled predicates + * or stored parameters. Current parameters are: + * + * + calls + * Number of times a procedure was called. + * + * + retries + * Number of times a call to the procedure was backtracked to and retried. + * + * + * + profile_reset + * Reset all profiling information. + * */ :- meta_predicate profile_data(:,+,-). @@ -144,24 +141,24 @@ profile_reset :- profile_reset. /** @pred showprofres - - -Show profiling info. - - -*/ + * + * + * Show profiling counts for all predicates. + * + * + */ showprofres :- showprofres(-1). /** @pred showprofres( _N_) - -Show profiling info for the top-most _N_ predicates. - - - -The showprofres/0 and `showprofres/1` predicates call a user-defined multifile hook predicate, `user:prolog_predicate_name/2`, that can be used for converting a possibly explicitly-qualified callable term into an atom that will used when printing the profiling information. - - + * + * Show profiling info for the top-most _N_ predicates. + * + * + * + * The showprofres/0 and `showprofres/1` predicates call a user-defined multifile hook predicate, `user:prolog_predicate_name/2`, that can be used for converting a possibly explicitly-qualified callable term into an atom that will used when printing the profiling information. + * + * */ showprofres(A) :- '$offline_showprofres', diff --git a/pl/setof.yap b/pl/setof.yap index 1cb10a3f2..89e12a5f4 100644 --- a/pl/setof.yap +++ b/pl/setof.yap @@ -36,8 +36,8 @@ /** @defgroup Sets Collecting Solutions to a Goal -@{ @ingroup builtins +@{ When there are several solutions to a goal, if the user wants to collect all the solutions he may be led to use the data base, because backtracking will