for a program I'm writing I need to make a list of lists, with pairs of numbers representing a product and sum of 2 given numbers.
For now I have a function which I can specify how many times I want to add a list to the list, which will be expanded with the full functionality later.
Here's what I have:
s1(0, X).
s1(Q, X) :-
N is Q - 1,
multiply(2, 3, Y),
A = Y ,
add(2, 3, Z),
B = Z,
addToEnd([A], [B], X),
s1(N,X).
multiply(A, B, C):-
C is A * B.
add(A, B, C) :-
C is A + B.
addToEnd([], L, L).
addToEnd([H|T], L2, [H|L3]) :-
addToEnd(T, L2, L3).
However, when I run s1(2,X)
for example, I get [6,5]
returned, then nothing else, it just hangs. When I run s1(0,X)
, i get true
, then false
when I hit ;
Can anyone help me with this? I can't see what I'm doing wrong, I feel like it should work!
To clarify how I feel this should work:
I call s1(2,X).
N = 1
, [6,5]
added to list in X([[6,5]])
s1(1,X).
N=0
, [6,5]
added to the list in X ([[6,5],[6,5]])
s1(0,X).
X = [[6,5],[6,5]]
In Prolog list elements are enclosed by brackets and separated by commas. Another way to represent a list is to use the head/tail notation [H|T]. Here the head of the list, H, is separated from the tail of the list, T, by a vertical bar. The tail of a list is the original list with its first element removed.
Recursion is a technique in which one predicate uses itself (may be with some other predicates) to find the truth value. Let us understand this definition with the help of an example − is_digesting(X,Y) :- just_ate(X,Y). is_digesting(X,Y) :-just_ate(X,Z),is_digesting(Z,Y).
Obtaining the first elements You need to use the [A|_] pattern: indeed a list where the head is A , and we are not interested in the rest (tail). like: foo([],[]). foo([[A|_]|L], [A|P]) :- foo(L, P).
So, there are many things to say here. First and foremost, as in most declarative languages, a variable cannot really change value.
What that means is that X = 1.
will unify 1
to X
as you'd expect, but if you add X = 2.
after that in your query (X = 1, X = 2.
), Prolog will say false
. The reason behind that is that you cannot unify 1
with 2
and that X
has truly become 1
, therefore X
cannot be unified to 2
.
Though, and that differs from Haskell, Ocaml and the like, you can bind partially a variable, as in X = h(Y).
. You'll then be able to further unify it X = h(a(Z)).
, while you couldn't in the languages mentionned earlier (where a variable is really just an alias for a value).
Why does he tell me all that you wonder? Well, that's your main problem here. You first bind X
to [6, 5]
, and then expect to further bind it to some other things. Once a variable is ground (ie does not contain any free variable inside itself), you cannot ever change its value again.
So here your recursion won't do anything but eventually prove X
false. Here it doesn't however since you end up calling addToEnd/3
with the same arguments each time ([6]
, [5]
and [6, 5]
).
That being said, let's look at how we could improve your code.
First, a remark:
multiply(2, 3, Y),
A = Y ,
add(2, 3, Z),
B = Z,
addToEnd([A], [B], X),
can be written
multiply(2, 3, Y),
add(2, 3, Z),
addToEnd([Y], [Z], X),
without any loss of information since you do not use A
and B
again.
Now, let's forget about addToEnd/3
for a moment and think about what you want.
If you enter s1(0, Q)
, do you really want Q
to stay free? Because that's what you state at the moment. It'd make more sense to bind Q
to []
in that case. Plus, that'll make a good recursive base case as you'll soon see.
s1(0, []).
is a shortcut to say
s1(0, Q) :- Q = [].
since Prolog does unification in clause heads (the part before :-
).
Then, I'll cheat a little but it'll just be to stay clear. You could state that when going from s1(4, Q)
to s1(5, Q)
you expect Q to hold one more value of some calculus.
Here, we could state that as follows:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
s1(PreviousN, Q).
Now, we just have to give a value to SomeCalculus
:
s1(N, [SomeCalculus|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
SomeCalculus = [X, Y],
s1(PreviousN, Q).
or, if you followed correctly, we could directly write:
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
So the complete program would be:
s1(0, []).
s1(N, [[X, Y]|Q]) :-
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
Now, if you test that, you might remark that the program loops just as yours when you hit the ;
key. That's because Prolog thinks the second clause can apply to 0
too.
So let's edit the program once more:
s1(0, []).
s1(N, [[X, Y]|Q]) :-
N > 0,
PreviousN is N - 1,
X is 2 * 3,
Y is 2 + 3,
s1(PreviousN, Q).
Now everything is fine.
I hope this'll help you to get a better understanding of how to build a proper predicate through recursion. I didn't spend much time correcting your addToEnd/3
predicate because my rambling about variables should already have told you a lot about what's wrong about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With