Related
So I am an absolute beginner in Prolog. I am learning recursion at the moment and this is what I got until now on my task.
dance(start).
dance(forward(T)) :- dance(T).
dance(backward(T)) :- dance(T).
count(start,0,0).
count(forward(X),succ(Y),Z) :- count(X,Y,Z).
count(backward(X),Y,succ(Z)) :- count(X,Y,Z).
greater(succ(0),0).
greater(succ(Y),succ(Z)):-greater(Y,Z).`
Summary of what this is supposed to do: There can be dances starting with the "start" and then either forward or backward addition per recursion. In count, I managed to be able to count the amount of forward and backward in a given sequence and save them in the "succ" notation and in greater I want to compare two of such "succ" numbers. And greater shall be true if the first argument is larger (consists of more "succs" than the second argument.
Now my task is to write a new rule "more(X)" which is true if the sequence X (build from dance) has more forward than backward in it. I think I have all the subtasks I need for it but now I am helpless with chaining them together because until now I only had 2 rules with the same amount of parameters but in this case, I got one with one, two, and three parameters. The solution should be something like this
more(X):-greater(Y,Z)
but how do I get my "succ" umbers from "count" to "greater" and the given sequence X to "count"? I do have to change some of the rules otherwise count is never called, right?
So it should be more like more(X):-greater(count(X,Y,Z)) ?
But like this, I would have to change the greater rules to be able to "get" this type of parameter.
Example query ?- more(backward(forward(start))).
false.
?- more(forward(start)).
true.
Your dance/1 and count/3 predicates seems correct.
If you want "greater" where greater(X, Y) means that X is greater than Y, you'd write:
greater(succ(_), 0).
greater(succ(X), succ(Y)) :- greater(X, Y).
Your solution checks if X is exactly one greater than Y.
Note that nothing is greater than 0.
If you implement more/1 in terms of count/3 and greater/2, you'd write:
more(X) :-
count(X, Forward, Backward),
greater(Forward, Backward).
So it should be more like more(X):-greater(count(X,Y,Z)) ?
No, you are writing that as if it is Python and greater(count(X,Y,Z)) will call greater(...) on the return from count. Prolog predicates are not function calls, and count(...) does not return anything in that way.
The result of the count is Y and Z. (These names you are using could be clearer). Do the count, and then after, use the Y and Z for something else.
count(X,Y,Z),
greater(Y,Z)
The comma being AND; "this code works if count of X is Y and Z AND Y is greater than Z".
I've just started Prolog and truly wonder why the following lines, specifically the 'is' part, always produces false:
highest(L) :-
path(_,_,Z),
Z >= L,
L is Z.
highestWrap :-
highest(0).
highestWrap is called.
Thanks in advance and have a beautiful day!
Unless there is a path with length 0, this will not work, and even then, it will likely not yield what you want: it will just say true.
In Prolog variables can only be set once, that means that if L is set to 0, then it remains 0, unless you backtrack over that assignment.
Here it thus means that you call highest(0), next you instruct Prolog to call path(_, _, Z) and this can result in zero, one or more solutions. In case there are no path(_, _, Z)s, then the call will fail. But in case there are, then Z will (if I make correct assumptions about the predicate), have a numerical value, for example 7.
Now the condition Z >= L of course holds in that case (if Z is 7), so that is nog the problem. But now you specify L is Z. That thus means that you call 0 is 7. The is/2 [swi-doc] predicate aims to solve the expression of the second argument (that expression is 7 in the example, so there is not much to solve), and then aims to unify it with the term on the left side. But since 0 is not equal to 7 that fails.
If you want to obtain the highest path, you can for example make use of the aggregate [swi-doc] library:
:- use_module(library(aggregate)).
highest(L) :-
aggregate(Max(Z), path(_,_,Z), Max(L)).
You can then call it with higest(X) to unify X with the highest value for Z in a call to path(_, _, Z).
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
I'm generating random coordinates and adding on my list, but first I need verify if that coordinate already exists. I'm trying to use member but when I was debugging I saw that isn't working:
My code is basically this:
% L is a list and Q is a count that define the number of coordinate
% X and Y are the coordinate members
% check if the coordniate already exists
% if exists, R is 0 and if not, R is 1
createCoordinates(L,Q) :-
random(1,10,X),
random(1,10,Y),
convertNumber(X,Z),
checkCoordinate([Z,Y],L,R),
(R is 0 -> print('member'), createCoordinates(L,Q); print('not member'),createCoordinates(L,Q-1).
checkCoordinate(C,L,R) :-
(member(C,L) -> R is 0; R is 1).
% transforms the number N in a letter L
convertNumber(N,L) :-
N is 1, L = 'A';
N is 2, L = 'B';
...
N is 10, L = 'J'.
%call createCoordinates
createCoordinates(L,20).
When I was debugging this was the output:
In this picture I'm in the firts interation and L is empty, so R should be 1 but always is 0, the coordinate always is part of the list.
I have the impression that the member clause is adding the coordinate at my list and does'nt make sense
First off, I would recommend breaking your problem down into smaller pieces. You should have a procedure for making a random coordinate:
random_coordinate([X,Y]) :-
random(1, 10, XN), convertNumber(XN, X),
random(1, 10, Y).
Second, your checkCoordinate/3 is converting Prolog's success/failure into an integer, which is just busy work for Prolog and not really improving life for you. memberchk/2 is completely sufficient to your task (member/2 would work too but is more powerful than necessary). The real problem here is not that member/2 didn't work, it's that you are trying to build up this list parameter on the way out, but you need it to exist on the way in to examine it.
We usually solve this kind of problem in Prolog by adding a third parameter and prepending values to the list on the way through. The base case then equates that list with the outbound list and we protect the whole thing with a lower-arity procedure. In other words, we do this:
random_coordinates(N, Coordinates) :- random_coordinates(N, [], Coordinates).
random_coordinates(0, Result, Result).
random_coordinates(N, CoordinatesSoFar, FinalResult) :- ...
Now that we have two things, memberchk/2 should work the way we need it to:
random_coordinates(N, CoordinatesSoFar, FinalResult) :-
N > 0, succ(N0, N), % count down, will need for recursive call
random_coordinate(Coord),
(memberchk(Coord, CoordinatesSoFar) ->
random_coordinates(N, CoordinatesSoFar, FinalResult)
;
random_coordinates(N0, [Coord|CoordinatesSoFar], FinalResult)
).
And this seems to do what we want:
?- random_coordinates(10, L), write(L), nl.
[[G,7],[G,3],[H,9],[H,8],[A,4],[G,1],[I,9],[H,6],[E,5],[G,8]]
?- random_coordinates(10, L), write(L), nl.
[[F,1],[I,8],[H,4],[I,1],[D,3],[I,6],[E,9],[D,1],[C,5],[F,8]]
Finally, I note you continue to use this syntax: N is 1, .... I caution you that this looks like an error to me because there is no distinction between this and N = 1, and your predicate could be stated somewhat tiresomely just with this:
convertNumber(1, 'A').
convertNumber(2, 'B').
...
My inclination would be to do it computationally with char_code/2 but this construction is actually probably better.
Another hint that you are doing something wrong is that the parameter L to createCoordinates/2 gets passed along in all cases and is not examined in any of them. In Prolog, we often have variables that appear to just be passed around meaninglessly, but they usually change positions or are used multiple times, as in random_coordinates(0, Result, Result); while nothing appears to be happening there, what's actually happening is plumbing: the built-up parameter becomes the result value. Nothing interesting is happening to the variable directly there, but it is being plumbed around. But nothing is happening at all to L in your code, except it is supposedly being checked for a new coordinate. But you're never actually appending anything to it, so there's no reason to expect that anything would wind up in L.
Edit Notice that #lambda.xy.x solves the problem in their answer by prepending the new coordinate in the head of the clause and examining the list only after the recursive call in the body, obviating the need for the second list parameter.
Edit 2 Also take a look at #lambda.xy.x's other solution as it has better time complexity as N approaches 100.
Since i had already written it, here is an alternative solution: The building block is gen_coord_notin/2 which guarantees a fresh solution C with regard to an exclusion list Excl.
gen_coord_notin(C, Excl) :-
random(1,10,X),
random(1,10,Y),
( memberchk(X-Y, Excl) ->
gen_coord_notin(C, Excl)
;
C = X-Y
).
The trick is that we only unify C with the new result, if it is fresh.
Then we only have to fold the generations into N iterations:
gen_coords([], 0).
gen_coords([X|Xs], N) :-
N > 0,
M is N - 1,
gen_coords(Xs, M),
gen_coord_notin(X, Xs).
Remark 1: since coordinates are always 2-tuples, a list representation invites unwanted errors (e.g. writing [X|Y] instead of [X,Y]). Traditionally, an infix operator like - is used to seperate tuples, but it's not any different than using coord(X,Y).
Remark 2: this predicate is inherently non-logical (i.e. calling gen_coords(X, 20) twice will result in different substitutions for X). You might use the meta-level predicates var/1, nonvar/1, ground/1, integer, etc. to guard against non-sensical calls like gen_coord(1-2, [1-1]).
Remark 3: it is also important that the conditional does not have multiple solutions (compare member(X,[A,B]) and memberchk(X,[A,B])). In general, this can be achieved by calling once/1 but there is a specialized predicate memberchk/2 which I used here.
I just realized that the performance of my other solutions is very bad for N close to 100. The reason is that with diminishing possible coordinates, the generate and test approach will take longer and longer. There's an alternative solution which generates all coordinates and picks N random ones:
all_pairs(Ls) :-
findall(X-Y, (between(1,10,X), between(1,10,Y)), Ls).
remove_index(X,[X|Xs],Xs,0).
remove_index(I,[X|Xs],[X|Rest],N) :-
N > 0,
M is N - 1,
remove_index(I,Xs,Rest,M).
n_from_pool(_Pool, [], 0).
n_from_pool(Pool, [C|Cs], N) :-
N > 0,
M is N - 1,
length(Pool, L),
random(0,L,R),
remove_index(C,Pool,NPool,R),
n_from_pool(NPool, Cs, M).
gen_coords2(Xs, N) :-
all_pairs(Pool),
n_from_pool(Pool, Xs, N).
Now the query
?- gen_coords2(Xs, 100).
Xs = [4-6, 5-6, 5-8, 9-6, 3-1, 1-3, 9-4, 6-1, ... - ...|...] ;
false.
succeeds as expected. The error message
?- gen_coords2(Xs, 101).
ERROR: random/1: Domain error: not_less_than_one' expected, found0'
when we try to generate more distinct elements than possible is not nice, but better than non-termination.
I have the following code:
position(0,0).
move(f):-
position(X,Y),
number(X),
number(Y),
Y is Y+1,
X is X+1.
but when i call move(f) it returns false. number(X) and number(Y) returns true but whem i add the other two lines the function doesn't work. what's the problem?
Elaborating on some of the comments your question has received, variables in Prolog stand for a possible instantiation of a single value, just like variables in mathematics and mathematical logic, and once they are instantiated within a context they must remain consistent. If we're dealing with a formula 0 = (a + b) - (a + b), we know that it can only express its intended sense if any value assigned to the first a is also assigned to the second. That is, we can substitute any value for a, but it must be the same value throughout. Prolog works with variables in this same way. If x = x + 1, then 2 = 3; but then math would be broken.
Addressing mat's caution against using dynamic predicates, here is a possible way of handling moves, but accomplished by passing around a list of previous moves. With this method, the most recent move will always be the first element of List in the compound term moves(List).
Supposing the current history of moves is as follows:
moves([position(0,0), position(0,1), position(1,1)]).
move/3 takes a direction, a complex term representing the previous moves, and tells us what the updated list of moves is.
move(Direction, moves([From|Ms]), moves([To,From|Ms])) :-
move_in_direction(Direction,From,To).
move_in_direction/3 takes a direction, and a position, and tells us what the next position in that direction is:
move_in_direction(left, position(X1,Y1), position(X2,Y1)) :- X2 is X1 - 1.
move_in_direction(right, position(X1,Y1), position(X2,Y1)) :- X2 is X1 + 1.
move_in_direction(up, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 + 1.
move_in_direction(down, position(X1,Y1), position(X1,Y2)) :- Y2 is Y1 - 1.
Notice that, using this method, you get a back-trackable history of moves for free. I'd imagine you could use this in interesting ways -- e.g. having the player explore possible series of moves until a certain condition is met, at which point it commits or backtracks. I'd be interested to know what kind of solution you end up going with.