96 lines
2.7 KiB
Prolog
96 lines
2.7 KiB
Prolog
% Utility Predicates
|
|
|
|
% The following predicates are used in the puzzle solutions.
|
|
% unique_solution( +Goal ) holds when Goal has one ground solution. Operationally,
|
|
% Goal may produce several solutions, ("don't care" non-deterministically), but they
|
|
% must all be identical (==).
|
|
|
|
unique_solution( Goal ) :-
|
|
findall( Goal, Goal, [Solution|Solutions] ),
|
|
same_solution( Solutions, Solution ),
|
|
Solution = Goal.
|
|
|
|
same_solution( [], _Solution ).
|
|
same_solution( [Solution0|Solutions], Solution ) :-
|
|
Solution0 == Solution,
|
|
same_solution( Solutions, Solution ).
|
|
|
|
% forall( +Enumerator, +Test ) is true if Enumerator and Test are goals and Test holds everywhere
|
|
% that Enumerator does. NB: does not further instantiate arguments.
|
|
|
|
%% forall( Enumerator, Test ) :-
|
|
%% \+ (call(Enumerator), \+ call(Test)).
|
|
|
|
% member( ?Element, ?List ) holds when Element is a member of List.
|
|
member( H, [H|_] ).
|
|
member( H, [_|T] ):-
|
|
member( H, T ).
|
|
|
|
% select( ?Element, ?List0, ?List1 ) is true if List1 is equal to List1 with Element removed.
|
|
|
|
select( H, [H|T], T ).
|
|
select( Element, [H|T0], [H|T1] ):-
|
|
select( Element, T0, T1 ).
|
|
|
|
% memberchk( +Element, +List ) succeeds (once) if Element is a member of List.
|
|
memberchk( Element, List ):-
|
|
member( Element, List ),
|
|
!.
|
|
|
|
% between( +Lower, +Upper, ?Index ) is true if Lower =< Index =< Upper. Two valid cases are
|
|
% possible:
|
|
% - Index is already instantiated to an integer so the checks on order are applied (test).
|
|
% - Index is a logical variable so a series of alternative solutions is generated as the
|
|
% monotonic sequence of values between Lower and Upper (non-deterministic generator).
|
|
|
|
%% between( Lower, Upper, Index ):-
|
|
%% integer( Lower ),
|
|
%% integer( Upper ),
|
|
%% Lower =< Upper,
|
|
%% ( integer( Index ) -> % Case 1: "test"
|
|
%% Index >= Lower,
|
|
%% Index =< Upper
|
|
%% ; var( Index ) -> % Case 2: "generate".
|
|
%% generate_between( Lower, Upper, Index )
|
|
%% ).
|
|
|
|
generate_between( Lower, Upper, Index ) :-
|
|
( Lower =:= Upper ->
|
|
Index = Lower
|
|
; Index = Lower
|
|
; Next is Lower + 1,
|
|
Next =< Upper,
|
|
generate_between( Next, Upper, Index )
|
|
).
|
|
|
|
% sum( +List, ?Sum ) holds when the List of numbers sum to Sum.
|
|
|
|
sum( [H|T], Sum ) :-
|
|
sum1( T, H, Sum ).
|
|
|
|
sum1( [], Sum, Sum ).
|
|
sum1( [H|T], Sum0, Sum ):-
|
|
Sum1 is Sum0 + H,
|
|
sum1( T, Sum1, Sum ).
|
|
|
|
% put_chars( +Chars ) if Chars is a (possibly empty) list of character codes and the
|
|
% corresponding characters are written to the current output stream.
|
|
|
|
put_chars( [] ).
|
|
put_chars( [Char|Chars] ) :-
|
|
put( Char ),
|
|
put_chars( Chars ).
|
|
|
|
% get_chars( ?Chars ) if Chars is a (possibly empty) list of character codes read
|
|
% from the current input stream.
|
|
|
|
get_chars( Input ) :-
|
|
get0( Char ),
|
|
( Char > -1 ->
|
|
Input = [Char|Chars],
|
|
get_chars( Chars )
|
|
; otherwise ->
|
|
Input = []
|
|
).
|
|
|