How to get predicates by specifying the number of arguments it should have? - prolog

Suppose I have these relations:
d(m, n).
d(x, y).
d(a, b, c).
d(i, j, k).
d(1, 2, 3, 4).
How can I write a predicate mypred(Pred, NumArgs, Rel)?
Examples:
mypred(d, 2, Rel): Rel would be d(m, n), d(x, y), false.
mypred(d, 3, Rel): Rel would be d(a, b, c), d(i, j, k), false.
mypred(d, 4, Rel): Rel would be d(1, 2, 3, 4), false.

mypred(F, A, Rel_0) :-
functor(Rel_0, F, A),
call(Rel_0).
However, rather don't use such a definition. Instead try to use call/N with N > 1.
Definitions as mypred/3, are very difficult to analyze. Cross-referencing is practically impossible. On the other hand, with call/N, N > 1 quite similar functionality is possible, cross-referencing stays intact. See meta-predicate for many uses.

Related

Prolog: decompose number into its digits

I am studying prolog and I am faced with a problem that I cannot deal with.
Given a number, I have to check if the sum of the factorial of each digit that composes it is equal to the number itself.
Example:
145
1! + 4! + 5! = 1 + 24 + 120
Now my problem is just how to decompose the number so that I can factorial and sum each digit.
EDIT1.
thank to #slago I understand how decompose the number, but now I have a problem to sum the factorial terms:
fact(N):-
fact(N, N, _ListNumber).
fact(N, 0, ListNumber):-
factorial(ListNumber, 1, Sum),
Sum == N.
fact(N, Number, [D|R]):-
D is Number mod 10,
Number1 is Number div 10,
fact(N, Number1, R).
factorial([], Counter, Counter).
factorial([D|R], Counter, Sum):-
print([D|R]),
checksum(D, Counter),
factorial(R, Counter, Sum).
checksum(D, Counter):-
Counter1 is Counter * D,
M is D - 1,
M >= 2, !,
checksum(M, Counter1).
I have tried like this, but I noticed [D|R] results empty, and I don't understand why.
Your code is organized in a very confusing way. It is best to code independent predicates (for more specific purposes) and, after that, use them together to get the answer you want.
Start by creating a predicate to decompose a natural number into digits.
decompose(N, [N]) :- N<10, !.
decompose(N, [D|R]) :- N>=10, D is N mod 10, M is N//10, decompose(M, R).
Example of decomposition:
?- decompose(145, D).
D = [5, 4, 1].
Then, create a predicate to compute the factorial of a natural number.
fact(N, F) :- fact(N, 1, F).
fact(0, A, A) :- !.
fact(N, A, F) :- N>0, M is N-1, B is N*A, fact(M, B, F).
Example of factorial:
?- fact(5, F).
F = 120.
After that, create a predicate to map each number of a list into its corresponding factorial (alternatively, you could use the predefined predicate maplist/3).
map_fact([], []).
map_fact([X|Xs], [Y|Ys]) :- fact(X,Y), map_fact(Xs, Ys).
Example of mapping:
?- decompose(145, D), map_fact(D, F).
D = [5, 4, 1],
F = [120, 24, 1].
You must also create a predicate to compute the sum of the items of a list (alternatively, you could use the predefined predicate sum_list/2).
sum(L, S) :- sum(L, 0, S).
sum([], A, A).
sum([X|Xs], A, S) :- B is A+X, sum(Xs, B, S).
Example of summation:
?- decompose(145, D), map_fact(D, F), sum(F, S).
D = [5, 4, 1],
F = [120, 24, 1],
S = 145.
Finally, create the predicate to check the desired number property.
check(N) :- decompose(N, D), map_fact(D, F), sum(F, N).
Example:
?- check(145).
true.
?- check(146).
false.

Prolog query: how does addition matter for recursive queries

I'm trying to write a predicate to find the nth element of a list.
Initially I wrote something like this:
nth([X|_], 0, X).
nth([_|T],N,Z):- N > 0, nth(T, M, Z), N is M + 1.
It works for queries such as nth([1, 2, 3, 4, 5], 0, X). but for queries such as nth([1, 2, 3, 4, 5], N, 1)., I get an "arguments insufficiently instantiated error" after I enter ";" after getting the answer. I know that there will be only 1 ans in this case, but for the sake of completeness, I want to know why.
I read on stack overflow here that the following is a better solution:
nth([X|_], 0, X) :- !.
nth([_|Y], N, Z) :- N > 0, M is N-1, nth(Y, M, Z).
I want to understand why M is N-1, nth(Y, M, Z). makes a difference as against the nth(T, M, Z), N is M + 1 in my answer.
PS: I think the question title can be improved, but I'm not sure of how. If you have suggestions, please let me know!
is/2 is not a complete constraint solver. So N is M + 1 and M is N - 1 look to be equivalent but they are not. The first only succeeds when M is instantiated and second when N is instantiated. Have you tried your solution with indices other zero? They will not work. You can use plus(1, M, N) instead of either of them to get it to work. Also clause ordering matters so plus(1, M, N) should be before the recursive call to nth.
nth([X|_], 0, X).
nth([_|T],N,Z):- N > 0, plus(1, M, N), nth(T, M, Z).
If N > 0, nth(T, M, Z), plus(1, M, N) is your clause ordering, your program will try to satisfy nth(T, M, Z) first and cause an uninstantiated error at N > 0 since M is not already instantiated.
Also neither program will work in the generative case.

Automata and prolog

I've been trying to make a non-deterministic finite automata in PROLOG, here is my code:
state(1).
state(2).
state(3).
initial_state(1).
final_state(3).
alphabet(a).
alphabet(b).
delta(1, b, 2).
delta(2, a, 2).
delta(2, a, 3).
delta(3, b, 2).
accept([X|[]], Q) :-
alphabet(X),
delta(Q, X, Q1),
final_state(Q1).
accept([X|XS], Q) :-
alphabet(X),
delta(Q, X, Q1),
accept(XS, Q1).
Where accept is a function that given a string and a state, it will tell us if it's accepted by the automata.
The problem is that when I try to see if the string baba ([b,a,b,a]) is accepted by the automata (accept([b,a,b,a],1)) I get true, which is not right.
why do you think it should fail?
The solution sequence is
delta(1, b, 2)
delta(2, a, 3)
delta(2, a, 2)
delta(2, a, 3)
My personal "best practice" is to collect the proof
accept([X|[]], Q,[delta(Q, X, Q1)]) :-
alphabet(X),
delta(Q, X, Q1),
print(delta(Q, X, Q1)),nl,
final_state(Q1).
accept([X|XS], Q,[delta(Q, X, Q1)|Rest]) :-
alphabet(X),
delta(Q, X, Q1),
print(delta(Q,X,Q1)),nl,
accept(XS, Q1,Rest).
accept(String,State):-accept(String,State,_).
this shows you that the program can be proofed with the above sequence
?- accept([b,a,b,a],1, Proof).
Proof = [delta(1, b, 2), delta(2, a, 3), delta(3, b, 2), delta(2, a, 3)]

PROLOG program semantic and exercise

First of all I have a doubt about the semantic of a program , for example :
length([],0).
length([_|L],N):-
length(L,N0),
N is N0 + 1.
The first instruction means the base case , or it has other meanings ?
I have to write a prolog program that, given a number, returns a list of numbers from 0 to the given number.
For example, when the input is 5, the output is [0,1,2,3,4,5].
I'm looking for a solution of this problem but I do not know how to start.
There is a predicate in SWI-Prologs library that does almost what you need to do. It is called numlist/3. You can use it with lower and upper boundary:
?- numlist(1, 5, L).
L = [1, 2, 3, 4, 5].
And here the implementation:
numlist(L, U, Ns) :-
must_be(integer, L),
must_be(integer, U),
L =< U,
numlist_(L, U, Ns).
numlist_(U, U, List) :-
!,
List = [U].
numlist_(L, U, [L|Ns]) :-
L2 is L+1,
numlist_(L2, U, Ns).
You can get rid of the upper half of this completely, and lose one argument (your Lower is just 1).
If you play with this a bit you should be able to figure it out.

Median in Prolog

I'm trying to write rules for prolog that define a median of a list by using a partitioning method.
partition([], V, [], []).
partition([X | L], V, [X | A], B) :- (V > X), !, partition(L, V, A, B).
partition([X | L], V, A, [X | B]) :- (V < X), !, partition(L, V, A, B).
partition([X | L], V, A, B) :- (V =:= X), partition(L, V, A, B).
median([A], A).
median(L, M) :- partition(L, M, A, B), length(A, X), length(B, X).
partition(L, V, A, B) partitions list L into 2 sublists A and B with A having values less than V and B having values greater than V.
That part works fine, but when I try to write my median, I'm trying to say that it is a median when after partitioning, A and B are the same length.
median works when I use concrete values, like median([1, 2, 3], 2)
but when I try median([1, 2, 3], X).
it gives an error message ERROR: >/2: Arguments are not sufficiently instantiated.
I was wondering how to fix that? Thanks!
=:= operator requires both its operands to be instantiated. When you ask for median([1, 2, 3], X), one of its operands becomes X, which is not instantiated yet. The same problem is with other arithmetic operators like >.
To correct it, you can either use constraints programming (which provides arithmetic operators that aren't so strict) or rework your program to only use arithmetic on list elements. For example, try a classical approach like: sort the list of numbers, then divide the list into three segments: list of length N, a single element, list of length N. Hint: you can do the part after sorting using just a single append/3 and two length/2 invocations.
Easy way to make your program work with median([1, 2, 3], X) query - is to instantiate M to a member of L in the last rule:
median(L, M) :-
member(M, L),
partition(L, M, A, B), length(A, X), length(B, X).

Resources