Files
mercury/extras/references/samples/max_of.m
Warwick Harvey 9cbccbd5dc This change adds a new extras directory, "references".
Estimated hours taken: 70 (plus whatever pets spent when he wrote the
original version of this)

This change adds a new extras directory, "references".  This directory
contains two impure reference type modules and a module that allows scoped
non-backtrackable update, along with examples of using them and tests.
These modules are intended to be useful when HAL is retargetted to Mercury,
for implementing global variables (backtracking and non-backtracking), and
may also be useful for the debugger.

In order to implement these features, a new memory zone "global heap" was
added to the runtime system, for a heap which is not reclaimed on failure,
along with a pair of functions for copying terms to this heap.

runtime/mercury_deep_copy.c:
runtime/mercury_deep_copy.h:
	Added two functions, MR_make_permanent() and
	MR_make_partially_permanent(), which essentially do a deep copy of a
	term to the global heap.
	(In conservative GC grades, these functions actually do nothing).

runtime/mercury_engine.c:
runtime/mercury_engine.h:
	Added fields global_heap_zone and e_global_hp (for the global heap
	and its heap pointer) to the MR_mercury_engine_struct, along with
	appropriate initialisation, etc.
	Defined MR_heap_zone, MR_solutions_heap_zone, and
	MR_global_heap_zone for convenient access to the corresponding field
	of the relevant Mercury engine.

runtime/mercury_memory.c:
	Added code for handling the size and zone size of the global heap.

runtime/mercury_regorder.h:
runtime/mercury_regs.h:
	Defined MR_global_hp (the global heap pointer for general use),
	along with corresponding other changes.

runtime/mercury_wrapper.c:
runtime/mercury_wrapper.h:
	Added declarations and initialisation of the size and zone_size of
	the global_heap.
	Added an entry for MR_GLOBAL_HP_RN to print_register_usage_counts()
	(plus missing entries for MR_SOL_HP_RN, MR_MIN_HP_REC and
	MR_MIN_SOL_HP_REC).

New files:

extras/references/Mmakefile:
	Mmakefile for building and testing these modules.

extras/references/README:
	Description of contents of this directory.

extras/references/global.m:
	A wrapper module for building a library containing the nb_reference,
	reference and scoped_update modules.

extras/references/nb_reference.m:
	Implements references which are not backtracked on failure.

extras/references/reference.m:
	Implements references which *are* backtracked on failure.

extras/references/scoped_update.m:
	Allows nested scoping of non-backtracking references.

extras/references/samples/Mmakefile:
extras/references/samples/max_of.m:
extras/references/samples/max_test.exp:
extras/references/samples/max_test.m:
	An example of using a non-backtracking reference (to find the
	maximum of the solutions generated by a predicate), with tests.

extras/references/tests/Mmakefile:
extras/references/tests/ref_test.exp:
extras/references/tests/ref_test.m:
	Some tests of references (backtracking and non-backtracking) and
	scoping.
1998-06-18 04:30:48 +00:00

97 lines
3.2 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1998 University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File : max_of.m
% Authors : pets (Peter Schachte)
% Stability : low
% Purpose : demonstration of nb_reference type
%
% This module defines a predicate max_of/2 that is equivalent to
%
% max_of(Pred, Max) :-
% unsorted_solutions(Pred, List),
% List = [First|Rest],
% foldl((pred(X,Y,Z) is det :- max(X,Y,Z)), Rest, First, Max).
%
% but which is potentially more efficient, because it avoids building a
% list of solutions.
:- module max_of.
:- interface.
:- pred max_of(pred(int), int).
:- mode max_of(pred(out) is nondet, out) is semidet.
:- mode max_of(pred(out) is multi, out) is det.
:- implementation.
:- import_module nb_reference.
:- import_module int, bool.
% This implementation uses two non-backtrackable references, one to keep
% track of whether or not we've had any solutions, and the other to store the
% max "so far." For each solution we find, if it's the first, we set the max
% so far to it, and we record that we've had some solutions. If not the
% first solution, then we update the max if the new solution is larger than
% the max so far. When we've found all the solutions, we make sure we've
% found at least one solution, and then return the max so far as the result.
%
% There is one difficulty implementing this predicate. When the Pred
% argument is a multi closure, we want max_of to be det. But when Pred is
% nondet, we must check to make sure than we have had any solutions; if not,
% max_of/2 must fail. Unfortunately, the Mercury compiler can't determine
% that when Pred is multi, the test will always succeed, so the determinacy
% checker complains that max_of/2 in that mode is actually semidet. We work
% around that with the min_solutions/1 predicate, which is implemented with
% C code. This allows us to have different code for different modes, which
% allows us to work around the problem. It would be much more convenient if
% Mercury allowed us to have different code for different modes of a
% predicate implemented in Mercury.
:- pragma promise_pure(max_of/2).
max_of(Pred, Max) :-
impure new_nb_reference(no, Someref),
impure new_nb_reference(0, Maxref),
(
Pred(Value),
semipure value(Someref, Some),
( Some = no ->
impure update(Someref, yes),
impure update(Maxref, Value)
;
semipure value(Maxref, Prev),
( Value > Prev ->
impure update(Maxref, Value)
;
true
)
),
fail
;
impure min_solutions(Pred, MinSolutions),
(
MinSolutions = 1
;
semipure value(Someref, yes)
),
semipure value(Maxref, Max)
).
:- impure pred min_solutions(pred(T), int).
:- mode min_solutions(pred(out) is nondet, out(bound(0))) is det.
:- mode min_solutions(pred(out) is multi, out(bound(1))) is det.
:- pragma c_code(
min_solutions(_Pred::(pred(out) is nondet), Res::out(bound(0))),
will_not_call_mercury, "Res = 0;").
:- pragma c_code(
min_solutions(_Pred::(pred(out) is multi), Res::out(bound(1))),
will_not_call_mercury, "Res = 1;").