-
From @triska's videos (I can't remember which one), he shows how to wrangle an "unfair enumeration". Something along the lines of: xy --> [].
xy --> ( "x" | "y" ), xy.
genxy(In) :-
length(In, N),
phrase(xy, In).
%@ Xy = []
%@ ; Xy = "x"
%@ ; Xy = "y"
%@ ; Xy = "xx"
%@ ; Xy = "xy"
%@ ; Xy = "yx"
%@ ; Xy = "yy"
%@ ; Xy = "xxx"
%@ ; Xy = "xxy"
%@ ; Xy = "xyx"
%@ ; Xy = "xyy"
%@ ; ... . In his DCG tutorial and video he also shows semicontext notation, used to sum up the leaves on a tree: num_leaves(Tree, N) :-
phrase(num_leaves_(Tree), [0], [N]).
num_leaves_(nil), [N] --> [N0], { N #= N0 + 1 }.
num_leaves_(node(_,Left,Right)) -->
num_leaves_(Left),
num_leaves_(Right).
?- num_leaves(node(a,node(b,nil,nil),
node(c,nil,
node(d,nil,nil))), N).
%@ N = 5. However, I am scratching my head at how we might accomplish fair enumeration for this tree: ?- num_leaves(T, N).
%@ T = nil, N = 1
%@ ; T = node(_A,nil,nil), N = 2
%@ ; T = node(_A,nil,node(_B,nil,nil)), N = 3
%@ ; T = node(_A,nil,node(_B,nil,node(_C,nil,nil))), N = 4
%@ ; T = node(_A,nil,node(_B,nil,node(_C,nil,node(_D,nil,nil)))), N = 5
%@ ; ... . 🤔 |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 10 replies
-
With the following building block, we can fairly enumerate the trees with iterative deepening, by constraining the length of the list of nodes: nodes(nil) --> []. nodes(node(Node,L,R)) --> [Node], nodes(L), nodes(R). Yielding: ?- length(Ls, _), phrase(nodes(Tree), Ls). Ls = [], Tree = nil ; Ls = [_A], Tree = node(_A,nil,nil) ; Ls = [_A,_B], Tree = node(_A,nil,node(_B,nil,nil)) ; Ls = [_A,_B], Tree = node(_A,node(_B,nil,nil),nil) ; ... . ?- length(Ls, _), phrase(nodes(Tree), Ls), num_leaves(Tree, N). Ls = [], Tree = nil, N = 1 ; Ls = [_A], Tree = node(_A,nil,nil), N = 2 ; Ls = [_A,_B], Tree = node(_A,nil,node(_B,nil,nil)), N = 3 ; Ls = [_A,_B], Tree = node(_A,node(_B,nil,nil),nil), N = 3 ; Ls = [_A,_B,_C], Tree = node(_A,nil,node(_B,nil,node(_C,nil,nil))), N = 4 ; ... . Finding good names is often an interesting challenge. For example, in the case of "gen"xy, what about ?- xys("xy"). true ; false. |
Beta Was this translation helpful? Give feedback.
-
(Starting another thread for this to better organize)
The ?- X #= 2, X = 1 + 1.
false.
?- X = 1 + 1, X #= 2, X = 1 + 1.
% Not monotonic! Adding an additional contraint made the set of solutions bigger!
X = 1+1.
% This happens because, by default, library(clpz) treats unbound variables as
% "something that could be an integer", but technically it accepts expressions
% there too (it's an "defaulty" representation). Maybe this is easier to understand:
?- 1 + 1 #= 2.
true.
?- X #= 2.
X = 2. % This misses correct solutions like the 1 + 1 above.
% The point of (#)/1 is that when it sees a compound term of the form #N, it
% assumes that N is "something that could be an integer" (it could already be
% an integer, or an variable that can be bound to an integer). This removes all
% the ambiguity because, in particular, N can't be an expression. The same
% examples as earlier but using (#)/1 to to signal to library(clpz) that X can
% only be an integer and not an expression:
?- #X #= 2, X = 1 + 1.
false.
?- X = 1 + 1, #X #= 2, X = 1 + 1.
% This basically means "I expected an integer but you gave me 1+1".
% An error here doesn't break monotonicity because it can be understood as
% "the system couldn't find a set of solutions". The same applies to
% non-termination. It's fine (and even desirable) for monotonic predicates to
% error or not terminate if they can't find solutions without preserving
% monotonicity, and that is the entire idea behind library(si).
error(type_error(integer,1+1),must_be/2).
?- #(1+1) #= 2.
% 1+1 is not an integer.
error(type_error(integer,1+1),must_be/2).
?- #2 #= 2.
true.
?- #X #= 2.
% In this case this is really the full set of solutions, because we
% explicitly ruled out expressions with (#)/1.
X = 2.
% We can define/assert clpz:monotonic/0 to put library(clpz) in monotonic mode.
% In this mode it refuses naked variables so that it doesn't use the defaulty
% representation and therefore end up with non-monotonic behavior.
?- assertz(clpz:monotonic).
% You can also just put a "clpz:monotonic." fact in your program.
true.
?- X #= 2.
% This query that was problematic because it didn't list expressions now is
% just refused with an instantiation error. Remember, errors don't break
% monotonicity.
error(instantiation_error,instantiation_error(unknown(_47),1)).
?- #X #= 2.
% This still works, because with the clean representation using (#)/1, we know
% that X can only be an integer.
X = 2.
?- #1 #= 2.
% "Could be an integer" also includes "actual integers", but you don't really need
% (#)/1 here.
false. |
Beta Was this translation helpful? Give feedback.
-
Not at all! Having an extra % A good naming convention for monotonic predicates is to simply list the arguments
% if it's obvious what relation between them you are representing.
element_pos_list0(H, 0, [H | _]).
element_pos_list0(Elt, N, [_|T]) :-
#N #>= 1,
#Next #= #N - 1,
element_pos_list0(Elt, Next, T). The way Scryer executes this predicate is something like checking every single alternate definition you gave it. So for example:
As you can imagine, there is no need to check the second implementation if the first passed, they are mutually exclusive. There are many ways to do this, let's see some of them. Cuts
|
Beta Was this translation helpful? Give feedback.
With the following building block, we can fairly enumerate the trees with iterative deepening, by constraining the length of the list of nodes:
Yielding: