I am beginner in Prolog programming. I wrote this program to sum the atomic of a deep list with accumulator.
deep_sum(Xs, N) :- deep_sum(Xs, 0, N).
deep_sum([], N, N).
deep_sum([X|Y], M, N) :- atomic(X), Q is M + X, deep_sum(Y, Q, N).
deep_sum([X|Y], M, N) :- M is P + Q, deep_sum(X, P, N), deep_sum(Y, Q, N).
Why do I get "ERROR: is/2: Arguments are not sufficiently instantiated"?
It works fine without accumulator:
deep_sum([], 0).
deep_sum([X|Y], S) :- atomic(X), !, deep_sum(Y, Q), S is Q + X.
deep_sum([X|Y], S) :- deep_sum(X, P), deep_sum(Y, Q), S is P + Q.
The problem is with your last clause:
deep_sum([X|Y], M, N) :- M is P+Q, deep_sum(X,P,N), deep_sum(Y,Q,N).
The direct issue of the error is that neither P nor Q have a value when M is P+Q is executed. Just moving it to the back won't solve it though, the clause has more issues.
Let's take a look at the recursive calls. deep_sum(X,P,N) in words means "N is the deep sum of the head (X), given accumulator P". There are two issues here: P does not have a value, and N is supposed to be the total sum of the entire list, not just the head.
The same issues are there in the second recursive call. The accumulator Q does not have a value yet, and again N is used as the result. So now N has 3 meanings: deep sum of the head, deep sum of the tail, and deep sum of the entire list! Obviously something is not right there.
Let's try to put in words how the recursive rule should behave. The result N should be equal to the sum of a) the current accumulator, b) the deep sum of the head and c) the deep sum of the tail. a and b can be easily combined: simply pass in the current accumulator as the accumulator for the recursive call: deep_sum(X, M, N1). Here I use another variable N1 to hold this result. Now we just have to sum this with the deep sum of the tail. Again, we can simply pass N1 as accumulator for the recursive call, and everything will be accumulated as expected.
Putting everything together, your recursive rule should look like this:
deep_sum([X|Y], M, N) :-
deep_sum(X, M, N1),
deep_sum(Y, N1, N).
For completeness, my deep_sum/3 implementation would look something like this:
deep_sum(X, M, N) :-
number(X),
N is M + X.
deep_sum([], N, N).
deep_sum([X|Y], M, N) :-
deep_sum(X, M, N1),
deep_sum(Y, N1, N).
The main differences are:
Only one recursive clause; the clause where you handle numbers doesn't need to be recursive.
number/1 instead ofatomic/1, so you don't attempt to add strings or so.
Rearranged the order of the clauses to not leave any useless choice point once the calculation is done.
Related
Suppose I have a list like [1,[2,1,[2],[3,1,1,[[3]],1,[1]],1],2,1,2,1,3] and I want to delete every second occurrence of '1' from it for a resultant list of [1,[2,[2],[3,1,[[3]],1,[]],1],2,2,1,3]. So far what I've come up with is this:
delete_second_occurrence([], [], _, _).
delete_second_occurrence([X|L], [X|R], X, N) :-
0 is mod(N, 2),
N1 is N + 1,
delete_second_occurrence(L, R, X, N1).
delete_second_occurrence([X|L], R, X, N) :-
1 is mod(N, 2),
N1 is N + 1,
delete_second_occurrence(L, R, X, N1).
delete_second_occurrence([E|L], [E|R], X, N) :-
is_list(E),
delete_second_occurrence(E, R, X, N),
delete_second_occurrence(L, R, X, N).
delete_second_occurrence([E|L], [E|R], X, N) :-
delete_second_occurrence(L, R, X, N).
To clarify,
R is the resultant list
L is the input list
X is the element I want to be removed
N is the number of times X has been encountered
It removes every second occurrence in the lowermost level but does nothing for the nested lists. How would I go about removing the duplicates in the nested lists as well?
The main problem is the fourth clause:
delete_second_occurrence([E|L], [E|R], X, N) :-
is_list(E),
delete_second_occurrence(E, R, X, N),
delete_second_occurrence(L, R, X, N).
The second condition is true if R is the result of deleting every second occurrence of X in E. The third condition is true if R is the result of deleting every second occurrence of X in L. Furthermore, N must have the same value after calling delete_second_occurrence on E and L. Not many lists have these properties. There are two problems to address:
Choose a different variable name in the first invocation of delete_second_occurrence and update the head of the clause appropriately.
Introduce another argument that represents the state of the accumulator after the recursive call.
Instead of counting the number of times that X has been encountered, you can use a Boolean variable. There is no need to perform modular arithmetic. After doing this, you will notice that your program generates one correct solution and several incorrect ones. For example, consider the fifth clause:
delete_second_occurrence([E|L], [E|R], X, N) :-
delete_second_occurrence(L, R, X, N).
This clause states that [E|R] is the result of deleting every second occurrence of X in [E|L] if R is the result of deleting every second occurrence of X in L. This is not always true. For example, if N is 1 and E unifies with X, you certainly don't want to include E in the output list. Similarly, if E is a list that contains X, you probably shouldn't generate solutions that simply prepend E to the result of the recursive call.
I m trying to check if, in a list of lists, all sublist is equal to the length of the list of lists.
For example, if I have [[1,2],[3,4]] is true, because I have 2 lists
of 2 elements.
Otherwise, if I have
[[1],[2,3]] is false because have 2 lists but not all list has 2
elements
[[1,2],[2,3],[3,4]] is false because I have 2 lists and all list have
2 elements instead of two
.
I did these two function:
count([],0).
count([_H|T],N):-count(T,N1),N is N1+1 .
ma([],0).
ma([H|T],N):- count(H,M1),ma(T,N1), M1 is N1.
I did "count" (and work) for count element in a list and return N the number of elements in a list.
The "ma" function doesn't work because "count" is executed until 0, and return 2, after executing ma but until 1 step, and after making directly the M1 is N1, and obviously return false.
I wish to make M1 is N1 at end of the program (like in another programming language, but I think is the then't correct form.
EDIT:
Daniel suggest to use :
ma([H], N) :- length(H, N).
ma([H|T], N) :- length(H, N), ma(T, N).
But a list with 3 sublists all with 2 elements gives result 2, instead the result will be false(error) because N number of the list must be equal to N number of elements in ALL Sublist.
I will do on my own, without build-in predicate of prolog.
Here a very basic solution without built in predicates:
count_elements([],N,N).
count_elements([_|T],N,N0):-
N1 is N+1,
count_elements(T,N1,N0).
count_length_sub([],_).
count_length_sub([H|T],N):-
count_elements(H,0,N),
count_length_sub(T,N).
solve(L):-
count_elements(L,0,NO),
count_length_sub(L,NO).
?- solve([[1,2],[3,4]]).
true.
?- solve([[1,2],[2,3],[3,4]]).
false.
Your count/2 is like the length/2 builtin, except that the built-in has more instantiation patterns (try length(X, Y) and see). Prefer length/2.
You're right that your ma/2 predicate is unhelpful because 0 is not a length of a sublist. Basically, you've chosen the wrong base case here; your base case should be a list with exactly one item in it:
ma_1([H], N) :- length(H, N).
ma_1([H|T], N) :- length(H, N), ma(T, N).
You will need to wrap this in something that ensures the length matches the length of the outer list:
ma(L, N) :- length(L, N), ma_1(L, N).
Note that there is no need to obtain separate variables and assert their equality (your dance with N and N1). Prolog will simply fail, which is what you want, if N does not have the right value. (Side note, do not use is for unification. The purpose of is is to reduce an arithmetic expression on the right side to a value and assign it to the variable on the left, e.g. X is 2 + 3*4.)
Another approach would be to write your actual request in a logical form and write that instead. A logical form of this request would be something like "ma(L, N) holds if N is the length of L and for all items X of L, they are lists of length N as well". This looks like so:
ma(L, N) :-
length(L, N),
forall(member(X, L),
length(X, N)).
This has an advantage in that no spare choice points are left around, although worrying about that is usually premature optimization.
Another approach would be to employ maplist/N, which has the advantage that it will give you back lists with variables. Unfortunately, length/2 has its parameters in the wrong order so you can't do the really cute thing and just write maplist(length(2), L). However, you can make a flip/3 predicate that flips around the arguments:
flip(P, Y, X) :- call(P, X, Y).
ma(L, N) :- length(L, N), maplist(flip(length, N), L).
Or, you can import library(yall) and use its lambda expressions:
ma(L, N) :- length(L, N), maplist({N}/[X]>>length(X, N), L).
Both of these approaches allow solutions like these:
?- ma(X, N).
X = [],
N = 0 ;
X = [[_1976]],
N = 1 ;
X = [[_1982, _1988], [_1994, _2000]],
N = 2 ;
X = [[_1988, _1994, _2000], [_2006, _2012, _2018], [_2024, _2030, _2036]],
N = 3
...
Is there a faster way to implement these 2 functions in swi-prolog , in order to save time and memory?
insert(Ind,List,Val,NList) :-
nth0(Ind,List,_,R),
nth0(Ind,NList,Val,R).
build(X,N,List) :-
length(List,N),
maplist(=(X),List).
Explaining: The first one inserts at the Nth position of a list a value and the second one builds a list of N elements all equal to number X
Edit : I have figured out a way to improve insert for my program
insert(Ind,List,NList,R,Elem) :-
Valb=Val,
Val is Val+1,
nth0(Ind,NList,Val,R),
Elem=Valb.
This should replace an element of a list with its value plus 1 , but also return the previous Value (Valb) before adding 1 . I know the code is wrong how could I make it right without using nth0 another time?
In terms of time complexity, both work in O(n) time and O(n) memory. You can not construct a list with n elements in less than O(n) time, and it will require at least O(n) memory. One could construct the list lazily and thus postpone the creation, but for a real list, in terms of time complexity.
What we can do however is "merge" the logic of the two predicates, such that we do more per recursive call, for example:
build(X,N,List) :-
var(N),
!,
build_gen_(List, X, N).
build(X, N, List) :-
integer(N),
!,
build_num_(N, List, X).
build_gen_([], _, 0).
build_gen_([X|T], X, N) :-
build_gen_(T, X, N1),
N is N1 + 1.
build_num_(0, [], _) :-
!.
build_num_(N, [X|T], X) :-
N > 0,
N1 is N-1,
build_num_(N1, T, X).
We thus basically construct a special predicate that performs the two tasks (constructing a list of length N, and setting all the elements to X) with the same predicate.
I leave specializing the first predicate insert/4 as an exercise.
Give recursive definition in Prolog :
Define a predicate prod(L, N) which takes a list L of numbers
and calculates the product N of the numbers in the list.
I have found a way that works for the sum :
sum([],0).
sum([X|L],N) :- sum(L,N1),N is N1 + X.
Please help me. I have been trying for a long time and it still isn't working.
product(L, N) :- product(L, 1, N).
product([], X, N) :- N is X.
product([H|T], X, N) :- X1 is X*H, product(T, X1, N).
You need a temporary counter (X is this case). And you mistook sum for multiplication.
I am trying to understand accumulators, so I want this implementation to contain them.
I have figured how to search through a simple list.
This counts the number of occurrences of a given element:
find(Element,[],A,A).
find(Element, [H|Tail], A, N) :- Element = H, A1 is A + 1,
find(Element, Tail, A1, N).
find(Element, [H|Tail], A, N) :- find(Element, Tail, A, N).
Now how can I do I make it work for nested lists?
You should simply add another item to the matcher such that if H is a list as well, the number of occurrences are counted first:
find(Element,[],A,A).
find(Element, [Element|Tail], A, N) :-
!,
A1 is A + 1,
find(Element, Tail, A1, N).
find(Element,[[H|T]|Tail],A,N) :-
!,
find(Element,[H|T],A,R),
find(Element,Tail,R,N).
find(Element, [H|Tail], A, N) :-
find(Element, Tail, A, N).
By placing the equality of Element (second clause) before the list recursion, you can also search lists in lists. In other words:
find([1,2],[[1,2],[3],4,[1,2]])
Will return 2.
You use a cut (!) to prevent Prolog from backtracking.
Hint: don't unify in the body (Element = H) if possible. Simply reuse the same variable in the head (find(Element,[Element|Tail],A,N)). Many Prolog compilers are able to optimize the latter much better than the first case.
Hint: use cuts. You can use cuts more often to prevent Prolog from non-determinism. For instance if you count the occurences of 5 in [5,1,2,5] it can come up with 2, 1 and 0 since you never specify in your last clause that H is not equal to Element. By placing a cut in the previous clause, the Prolog interpreter will never backtrack over this.