This is a simple mini program I have here that simplifies addition expressions that are queried. I can't seem to figure out how to finish it off. When I query the following:
sim(sum(sum(x,1),5),Val,[x:X]).
My result is Val = X+1+5. I would like it to simplify all the way to X+6.
Here is the code:
sim(Var, Value, Lst) :- member(Var:Value, Lst).
sim(Num, Num, _) :- number(Num).
sim(sum(Left, Right), Value, Lst) :-
sim(Left, LeftVal, Lst),
sim(Right, RightVal, Lst),
so(Value,LeftVal,RightVal).
so(Result, X, Y) :-
number(X),
number(Y), !,
Result is X + Y.
so(Result, X, Y) :- // debugging so(Result,_,Y) :-
Result = X + Y. // Y value write(Y), Result = Y.
What I do know is that my program is trying to simplify X+1 before adding X+1 and 5. When I change the last line of my "so" method to only give Y to Result I get Val = 6. Before that line I write Y to the screen for debugging purposes and it gives me 1 5 because of the recursion. Which means X must be a var? Is there a corner case not here that will allow me to simplify addition all the way down?
What I am noticing is that "so" never adds 1 and 5 because they are never arguments together in the "so" method that checks for X and Y to be numbers. X and 1 are the first arguments, then upon recursion X+1 and 5 are the arguments and it doesn't execute because number(X) fails when X is X+1
Expanding on my comment above: here is an example of an expression simplifier that separates 'symbols' from 'values' using two lists.
Notice how it uses the fact, in parsing and unparsing, that the only operator joining symbols and values is +.
Related
I'm currently learning SWI-Prolog. I want to implement a function factorable(X) which is true if X can be written as X = n*b.
This is what I've gotten so far:
isTeiler(X,Y) :- Y mod X =:= 0.
hatTeiler(X,X) :- fail,!.
hatTeiler(X,Y) :- isTeiler(Y,X), !; Z is Y+1, hatTeiler(X,Z),!.
factorable(X) :- hatTeiler(X,2).
My problem is now that I don't understand how to end the recursion with a fail without backtracking. I thought the cut would do the job but after hatTeilerfails when both arguments are equal it jumps right to isTeiler which is of course true if both arguments are equal. I also tried using \+ but without success.
It looks like you add cuts to end a recursion but this is usually done by making rule heads more specific or adding guards to a clause.
E.g. a rule:
x_y_sum(X,succ(Y,1),succ(Z,1)) :-
x_y_sum(X,Y,Z).
will never be matched by x_y_sum(X,0,Y). A recursion just ends in this case.
Alternatively, a guard will prevent the application of a rule for invalid cases.
hatTeiler(X,X) :- fail,!.
I assume this rule should prevent matching of the rule below with equal arguments. It is much easier just to add the inequality of X and Y as a conditon:
hatTeiler(X,Y) :-
Y>X,
isTeiler(Y,X),
!;
Z is Y+1,
hatTeiler(X,Z),
!.
Then hatTeiler(5,5) fails automatically. (*)
You also have a disjunction operator ; that is much better written as two clauses (i drop the cuts or not all possibilities will be explored):
hatTeiler(X,Y) :- % (1)
Y > X,
isTeiler(Y,X).
hatTeiler(X,Y) :- % (2)
Y > X,
Z is Y+1,
hatTeiler(X,Z).
Now we can read the rules declaratively:
(1) if Y is larger than X and X divides Y without remainder, hatTeiler(X,Y) is true.
(2) if Y is larger than X and (roughly speaking) hatTeiler(X,Y+1) is true, then hatTeiler(X, Y) is also true.
Rule (1) sounds good, but (2) sounds fishy: for specific X and Y we get e.g.: hatTeiler(4,15) is true when hatTeiler(4,16) is true. If I understand correctly, this problem is about divisors so I would not expect this property to hold. Moreover, the backwards reasoning of prolog will then try to deduce hatTeiler(4,17), hatTeiler(4,18), etc. which leads to non-termination. I guess you want the cut to stop the recursion but it looks like you need a different property.
Coming from the original property, you want to check if X = N * B for some N and B. We know that 2 <= N <= X and X mod N = 0. For the first one there is even a built-in called between/2 that makes the whole thing a two-liner:
hT(X,B) :-
between(2, X, B),
0 is (X mod B).
?- hT(12,X).
X = 2 ;
X = 3 ;
X = 4 ;
X = 6 ;
X = 12.
Now you only need to write your own between and you're done - all without cuts.
(*) The more general hasTeiler(X,X) fails because is (and <) only works when the right hand side (both sides) is variable-free and contains only arithmetic terms (i.e. numbers, +, -, etc).
If you put cut before the fail, it will be freeze the backtracking.
The cut operation freeze the backtracking , if prolog cross it.
Actually when prolog have failed, it backtracks to last cut.
for example :
a:- b,
c,!,
d,
e,!,
f.
Here, if b or c have failed, backtrack do not freeze.
if d or f have failed, backtrack Immediately freeze, because before it is a cut
if e have failed , it can backtrack just on d
I hope it be useful
Like the title states, I am trying to return the sum of the returned values from sub predicates but it's not working. Here is my code:
addlistnum([],[],X).
addlistnum(digits(Y,[A|T]),digits(F,[B|T]),X) :-
X is Y + F.
digits(Num, List) :-
digits(0, List, Num).
digits(Num, [], Num).
digits(N, [A|As], Num) :-
N1 is N * 10 + A,
digits(N1, As, Num).
The sub predicate works fine. It converts list to an integer. Now I want to sum the converted values.
Example:
?- digits(X,[3,3,3]).
X = 333. % works as expected
Building on that, addlistnum([3,3,3,3],[2,2,2],X) is supposed to produce X = 3555 (as 3555 is 3333 + 222), but I get false instead.
I also tried:
addlistnum([],[],X).
addlistnum([A|T],[B|T],X) :-
X is Y + F,
digits(Y,[A|T]),
digits(F,[B|T]).
It simply returns false, which gives no information about is wrong.
Problem is at these rules:
addlistnum([],[],X).
addlistnum(digits(Y,[A|T]),digits(F,[B|T]),X) :-
X is Y + F.
Second one is, "addition of two list is the addition of the integer conversion of these list":
addlistnum(A,B,X) :-
digits(NA,A),
digits(NB,B),
X is NA + NB.
first one is not necessary, "digits" for an empty list is zero, thus, this rule also covers "addition of two empty list is zero"
My aim is to take the numbers between X and Y and produce Z.
num_between(3,6, All)
For example, if X is 3 and Y is 6 then Z is a list of the numbers between X and Y inclusive. Something like num_between(3,6,[3,4,5,6]) should evaluate as true. Here's what I have so far:
num_between(0,0, []).
num_between(X,Y, All) :-
increase(X, New) , % increase number X++
\+(X = Y) , % check if X is not equal to Y
num_between(New,Y,[All|X]) . % requestion ???
increase(F,N) :- N is F+1 .
increase/1 is working and returns number that is required, but
when recursion is gone through num_between/3 it goes unlit: X is 6 then it fails as I want,
but I can not manage to keep numbers or to return them. All = [3,4,5,6].
All = All + F. Could anyone help please.
Your base clause is incorrect: since you never decrease X or Y, they would never get to zero (unless Y starts at zero, and X starts at a non-positive value). The base clause should look like this:
num_between(X, Y, []) :- X > Y.
This ensures that you get an empty result when the user enters an invalid "backward" range (say, from 6 to 3).
Now to the main clause: all you need to do is to check that the range is valid, get the next value, and make a recursive call, like this:
num_between(X, Y, [X|Tail]) :-
X =< Y,
Next is X + 1,
num_between(Next, Y, Tail).
Demo.
Your original code made an error when constructing a list - it tried to use X as the "tail" of the list, which is incorrect:
num_between(New,Y,[All|X]).
you pass on All, the result after an "expansion", down through the recursive chain of invocation. It should be the other way around - you need to pass in a Tail to collect the result, and then pre-pend X to it when the recursive invocation is over.
You have to change both your base case and your recursive clause:
num_between(X, X, [X]).
num_between(X, Y, [X|L]):-
X < Y,
increase(X, New),
num_between(New, Y, L).
First clause is the base case, it states that the number ranging from X and X is just [X].
The recursive clause states that a number X which is less than a number Y should have it in the output list (thus the [X|L] in the third argument of the head), then it increases the value (i'm just using your helper procedure for that) and recursively calling itself now with the New value for the first argument.
I would write this along these lines:
numbers_between( X , X , [X] ) . % if X and Y have converged, we have the empty list
numbers_between( X , Y , [X|Zs] ) :- % otherwise, add X to the result list
X < Y , % - assuming X is less than Y
X1 is X+1 , % - increment X
numbers_between(X1,Y,Zs) % - recurse down
. %
numbers_between( X , Y , [X|Zs] ) :- % otherwise, add X to the result list
X > Y , % - assuming X > Y
X1 is X-1 , % - decrement X
numbers_between(X1,Y,Zs) % - recurse down
. %
This Prolog program defines the third argument to be the maximum value of the first two numeric arguments:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
I think that this program works just fine. But I am told that it can give incorrect result. Can you tell when and why?
This is a textbook example.
?- max(5,1,1).
true.
Homework: Why is the program wrong? How do we make the program correct?
EDIT
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
Our intention is to say:
If X is greater than Y, then Max is X. Otherwise, Max must be Y.
Instead, what is say is:
When the first and third arguments (X and Max) can be unified, and X is greater than Y, succeed. Otherwise, if the second and third arguments (Y and Max) can be unified, succeed.
The obvious problem arises then the first and third arguments cannot be unified, but the second and the third can.
Instead:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
or
max(X, Y, Max) :- X >= Y, !, Max = X.
max(_, Max, Max).
It does work fine, provided the third argument is uninstantiated. The danger here would be if there were a way to backtrack into the second rule, or if the third argument is instantiated to the same value as the second. It's not particularly safe looking because max(X, Y, Y). is equal to max(_, Y, Y) which just sets the result to the second value without any thought. The cut at the end of the first rule effectively ensures that backtracking will not commence if X >= Y, so the second rule should only be entered when X < Y and Z is not already equal to Y.
Though it mostly works, it's not a good habit to get into. People new to Prolog tend to think procedurally and making use of the cut like this to ensure a particular result through procedural trickery ultimately holds you back and leads to convoluted Prolog that cannot be driven in different and interesting ways. There are several other ways of writing this predicate that work just as well but do not rely on the cut to ensure their behavior, for instance:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
or
max(X, Y, Z) :- X >= Y -> Z = X ; Z = Y.
Neither of these is vulnerable to the problem of the third being instantiated. Interestingly, this is a great illustration of the difference between a red cut and a green cut. Your code has a red cut, where the behavior is dependent on the cut, but if I simply change my first solution to this:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y) :- X < Y.
That's a green cut, because the behavior is not dependent on the cut, but Prolog's performance may improve slightly since it won't backtrack into the second clause to try it. Here we're explicitly telling Prolog, don't both making the next check because we know it will fail. With a red cut, there's no other check which will fail.
It's unfortunate that stating the condition twice feels redundant but relying on a single rule feels clunky. In practice, my experience is that scenarios like these are not ultimately all that common; usually you have atoms or structures you can match in the head of the clause that create behavior like we have in my first substitute, but without needing a body. For example:
perform(scan(target, X, Y)) :- ...
perform(scan(calibration, X)) :- ...
This has the same effect: Prolog will backtrack until it unifies successfully, then it will back track again, but the exclusive nature of the matching will prevent another body from being executed. If we find out it's spending too much time backtracking we can add cuts to improve the performance, but in practice it's unlikely to be a problem.
just started programming with prolog and I'm having a few issues. The function I have is supposed to take a value X and copy it N number of times into M. My function returns a list of N number of memory locations. Here's the code, any ideas?
duple(N,_,M):- length(M,Q), N is Q.
duple(N,X,M):- append(X,M,Q), duple(N,X,Q).
Those are not memory adresses. Those are free variables. What you see is their internal names in your prolog system of choice. Then, as #chac pointed out (+1 btw), the third clause is not really making sense! Maybe you can try to tell us what you meant so that we can bring light about how to do it correctly.
I'm going to give you two implementations of your predicate to try to show you correct Prolog syntax:
duple1(N, X, L) :-
length(L, N),
maplist(=(X), L).
Here, in your duple1/3 predicate, we tell prolog that the length of the resulting list L is N, and then we tell it that each element of L should be unified with X for the predicate to hold.
Another to do that would be to build the resulting list "manually" through recursion:
duple2(0, _X, []).
duple2(N, X, [X|L]) :-
N > 0,
NewN is N - 1,
duple1(NewN, X, L).
Though, note that because we use >/2, is and -/2, ie arithmetic, we prevent prolog from using this predicate in several ways, such as:
?- duple1(X, Y, [xyz, xyz]).
X = 2,
Y = xyz.
This worked before, in our first predicate!
Hope this was of some help.
I suppose you call your predicate, for instance, in this way:
?- duple(3,xyz,L).
and you get
L = [_G289, _G292, _G295] ;
ERROR: Out of global stack
If you try
?- length(X,Y).
X = [],
Y = 0 ;
X = [_G299],
Y = 1 ;
X = [_G299, _G302],
Y = 2 ;
X = [_G299, _G302, _G305],
Y = 3 ;
X = [_G299, _G302, _G305, _G308],
Y = 4 .
...
you can see what's happening:
your query will match the specified *M*, displaying a list of M uninstantiated variables (memory locations), then continue backtracking and generating evee longer lists 'til there is stack space. Your second rule will never fire (and I don't really understand its purpose).
A generator is easier to write in this way:
duple(N,X,M) :- findall(X,between(1,N,_),M).
test:
?- duple(3,xyz,L).
L = [xyz, xyz, xyz].