I'm new to Prolog and have decided to try to solve a problem in which I have a sequence of symbols that each have the value 1 or -1. What I need is to add them all together, one element at a time, and extract at which index the sum for the first time drops below 0. Since I'm coming from an imperative background, I'm imagining a count variable and a for-loop, but obviously I can't do that in Prolog.
value('(', 1).
value(')', -1).
main(R) :- readFile("input", R), ???
readFile(Path, R) :-
open(Path, read, File),
read_string(File, _, Str),
stringToCharList(Str, Xs),
maplist(value, Xs, R).
stringToCharList(String, Characters) :-
name(String, Xs),
maplist(toChar, Xs, Characters ).
toChar(X, Y) :- name(Y, [X]).
As you can see, all that I've really managed so far is to read the file that contains the sequence, and convert it to 1s and -1s. I have no idea where to go from here. I suppose the problem is three-fold:
I need to iterate over a list
I need to sum each element in the list
I need to return a certain index
Any suggestions? Can I somehow cut off the list where iteration would have dropped the sum below zero, and just return the length?
I'll use a principle in Prolog of an auxiliary variable to act as a counter until the conditions reach what we want. Then the auxiliary counter is unified with a variable at that point in the base case.
I'm assuming here, blindly, that your code works as stated. I did not test it (that's up to you).
main(IndexAtZeroSum) :- readFile("input", R), index_at_zero_sum(R, IndexAtZeroSum).
readFile(Path, R) :-
open(Path, read, File),
read_string(File, _, Str),
stringToCharList(Str, Xs),
maplist(value, Xs, R).
stringToCharList(String, Characters) :-
name(String, Xs),
maplist(toChar, Xs, Characters ).
toChar(X, Y) :- name(Y, [X]).
% The following predicate assumes indexing starting at 0
index_at_zero_sum([V|Vs], IndexAtZeroSum) :-
index_at_zero_sum(Vs, V, 0, IndexAtZeroSum).
% When sum is zero, Index is what we want
index_at_zero_sum(_, 0, Index, Index).
index_at_zero_sum([V|Vs], Sum, CurIndex, Index) :-
S is Sum + V,
NextIndex is CurIndex + 1,
index_at_zero_sum(Vs, S, NextIndex, Index).
index_at_zero_sum/2 provides the index for the given list where the sum becomes zero. It does so by using an auxiliary predicate, index_at_zero_sum/4, starting with a sum at the first value (the sum being the value itself) and the current index starting at 0. So the 2nd argument is the sum at index 0. Subsequent calls to index_at_zero_sum/4 increment the index and accumulate the sum until the sum becomes 0. At that point, the base case succeeds and unifies the 4th argument with the current index. If the sum never becomes 0 before the list becomes empty, the predicate fails.
You can also avoid reading the entire file and creating a numeric list by using get_char/2:
index_at_zero_sum(Path, Index) :-
open(Path, read, File),
get_char(File, C),
value(C, V),
( index_at_zero_sum(File, V, 0, Index)
-> close(File)
; close(File),
fail
).
index_at_zero_sum(_, 0, Index, Index).
index_at_zero_sum(File, Sum, CurIndex, Index) :-
get_char(File, C),
value(C, V),
S is Sum + V,
NewIndex is CurIndex + 1,
index_at_zero_sum(File, S, NewIndex, Index).
Related
I'm trying to slice a list, and create a result list that contains elements that fall between two indices, exclusive.
indexOf([Element|_], Element, 0). % We found the element
indexOf([_|Tail], Element, Index):-
indexOf(Tail, Element, Index1), % Check in the tail of the list
Index is Index1+1. % and increment the resulting index
less_than(0, 0).
less_than(X, N) :-
X < N.
greater_than(0, 0).
greater_than(X, N) :-
X > N.
slice([], 0, 0, []).
slice([H|T], I, N, R) :-
indexOf([H, T], H, Ind), % get the index of the H
(less_than(Ind, N),
greater_than(Ind, I), % check if that index is between I and N
slice(T, I, N, H|R); % if it is - call the slice again, but append the H to the result
slice(T, I, N, R)). % if it's not, just call the slice regularly with the rest of the items
For instance, slice([a,b,c,d], 0, 4, R) R should be [b,c].
Now this always fails but I'm not sure why.
Is it the base case, or is is the 'if-else' statement between the parenthesis?
I would like to accomplish this without using build-in predicates.
There are some fundamental flaws in your algorithm. You seem to be processing one item at a time but didn't take into account that your input list keeps shrinking, so you should decrement your starting index by one each time. Your base case probably should not check that the ending index be 0, you never modify it and you wouldn't call it with 0.
Also your indexOf/3 procedure seems to find any location where the item is found (there may be duplicate items in your list).
You are also misusing the list structure [Head|Tail] (forgot the square brackets).
You may do your slicing by using append/3:
slice(L, From, To, R):-
length(LFrom, From),
length([_|LTo], To),
append(LTo, _, L),
append(LFrom, R, LTo).
In this answer I assumed your index starts at 1.
Sample run:
?- slice([a,b,c,d,e], 1, 4, R).
R = [b, c].
I have a list L given and my task is to create cumulative duplicates, depending on how many I want.
I have the list [a,b,c,d] for example and want the first element to be duplicated 4 times (optional), then every subsequent element has to be duplicated like the previous one + 1.
Let's assume my predicate is called list_copy(L,N,R), the result with L = [a,b,c,d] and K = 2 should be:
?- list_copy([a,b,c,d],2,R).
R = [a,a,b,b,b,c,c,c,c,d,d,d,d,d]
I've managed to create a universal code for duplicating a list two times:
dupl([],[]).
dupl([O|U],[O,O|U1]) :- dupl(U,U1).
Depending on how many O's I put in my second line I get that many duplicates.
My question is, though: How can I implement a 3rd variable as a cumulative counter, in order to get the desired outcome?
When you have to "count" stuff one by one, consider using succ/2. It has the nice property that it works both ways, and fails when you call it with succ(X, 0).
So first, the predicate that does "cumulative duplicates":
cum_dup([], _, []).
cum_dup([X|Xs], N, Ys) :-
repeat_n(X, N, Ys, Back),
succ(N, N1),
cum_dup(Xs, N1, Back).
This uses the predicate repeat_n/4, which takes an elements, a non-negative integer, and repeats the element. It leaves a "hole" in the back of the list that you can fill with the rest of the result using cum_dup/3. Here is a straight-forward implementation of repeat_n/4:
repeat_n(_, 0, L, L).
repeat_n(X, N, [X|Xs], Rest) :-
succ(N0, N),
repeat_n(X, N0, Xs, Rest).
This will already give you the result you need:
?- cum_dup([a,b,c,d], 2, R).
R = [a, a, b, b, b, c, c, c, c, d, d, d, d, d] ;
false
It leaves behind a harmless choice point. There are too many ways to make a repeat_n/4 that does not leave unnecessary choice points:
use CLP(FD)
use a cut
use a conditional (if-then-else)
use a structure instead of an integer
Just one example:
repeat_n(X, N, L, Back) :-
length(Ns, N),
repeat_n_(Ns, X, L, Back).
repeat_n_([], _, L, L).
repeat_n_([_|Ns], X, [X|Xs], L) :-
repeat_n_(Ns, X, Xs, L).
Here, instead of counting with an integer, you (ab)use a list of that length.
I guess you can think about it yourself and ask another question if you really need to.
--- EDIT --- modified using succ/2, as suggested by Boris (thanks!).
I suppose you can use a helper clause. Like list_copy_h/4 in the following example
list_copy_h([], _, _, []).
list_copy_h([_ | Tin], 0, Num, Lout) :-
succ(Num, Np1),
list_copy_h(Tin, Np1, Np1, Lout).
list_copy_h([H | Tin], Count, Num, [H | Tout]) :-
succ(Cm1, Count),
list_copy_h([H | Tin], Cm1, Num, Tout).
list_copy(Lin, Num, Lout) :-
list_copy_h(Lin, Num, Num, Lout).
The idea is use a counter (the second argument) that decrease to zero. When zero, the head of the input list is discharged and the cycle begin again with the following element of the input list but increasing the starting value of the counter. The third argument is the starting value.
I am aware on how to find the index of a specific element in Prolog but is there a way to find the index of the first instance of a number greater than say X. For instance, say I have a list of all ones but there is a random number greater than one somewhere in the list. How could I go about finding the index of the first instance of a number greater than 1? I am really new to Prolog and am not too good at subgoals of predicates.
You want to write a relation between a list an index and a value. Let's call it list_1stindex_gt/3. It is opportune to have a fourth argument to keep track of the current index. However, it would be nice to not bother the user with this accumlator, so you could use and auxiliary predicate with an additional argument for the current index, let's call it list_1stindex_gt_/4. Assuming you want to start counting the indices at 1 (otherwise change the fourth argument to 0) you can define list_1stindex_gt/3 like so:
:-use_module(library(clpfd)).
list_1stindex_gt(L,I,GT) :-
list_1stindex_gt_(L,I,GT,1).
For list_1stindex_gt_/4 you have 2 cases:
The head of the list is greater than the third argument: Then you know the desired index.
The head of the list is smaller or equal to the third argument: Then you increment the accumlator by 1 and continue the search in the tail of the list.
You can write that in Prolog like so:
list_1stindex_gt_([X|Xs],I,GT,I) :- % case 1
X #> GT.
list_1stindex_gt_([X|Xs],I,GT,Acc0) :- % case 2
X #=< GT,
Acc1 #= Acc0+1,
list_1stindex_gt_(Xs,I,GT,Acc1).
Example queries: At which index is the first element greater than 1 in the given list?
?- list_1stindex_gt([1,1,1,1,5,1,1,2],I,1).
I = 5 ? ;
no
At which index can the first element greater than 1 be in a list of three variables?
?- list_1stindex_gt([A,B,C],I,1).
I = 1,
A in 2..sup ? ;
I = 2,
A in inf..1,
B in 2..sup ? ;
I = 3,
A in inf..1,
B in inf..1,
C in 2..sup ? ;
no
At which index can the first element greater than the variable X be in a list of three variables?
?- list_1stindex_gt([A,B,C],I,X).
I = 1,
X#=<A+ -1 ? ;
I = 2,
X#>=A,
X#=<B+ -1 ? ;
I = 3,
X#>=A,
X#=<C+ -1,
X#>=B ? ;
no
Furthermore, you could consider #mat's suggested improvement from this answer to a previous question by you: Following the idea behind (#<)/3 you can define (#>)/3 and then define list_1stindex_gt_/4 using if_/3 like so:
:-use_module(library(clpfd)).
#>(X, Y, T) :-
zcompare(C, X, Y),
greater_true(C, T).
greater_true(<, false).
greater_true(>, true).
greater_true(=, false).
list_1stindex_gt(L,I,GT) :-
list_1stindex_gt_(L,I,GT,1).
list_1stindex_gt_([X|Xs],I,GT,Acc0) :-
if_(X #> GT,
(I #= Acc0),
(Acc1 #= Acc0+1, list_1stindex_gt_(Xs,I,GT,Acc1))).
This way the first query succeeds without leaving unnecessary choice points open:
?- list_1stindex_gt([1,1,1,1,5,1,1,2],I,1).
I = 5.
Here's a slightly different take on it:
:- use_module(library(clpfd)).
:- use_module(library(lists)).
:- asserta(clpfd:full_answer).
zs_first_greater(Zs, Index, Pivot) :-
append(Prefix, [E|_], Zs),
maplist(#>=(Pivot), Prefix),
E #> Pivot,
length([_|Prefix], Index). % 1-based index
Sample queries using SICStus Prolog 4.3.3:
| ?- zs_first_greater([1,1,1,2,1,1], I, 1).
I = 4 ? ;
no
| ?- zs_first_greater([1,1,1,2,1,1], I, 3).
no
| ?- zs_first_greater([], I, 3).
no
| ?- zs_first_greater([1,1,1,1,5,1,1,2], I, 1).
I = 5 ? ;
no
Thanks to clpfd we can also ask very general queries:
| ?- zs_first_greater([A,B,C,D], I, X).
I = 1,
A#>=X+1,
A in inf..sup,
X in inf..sup ? ;
I = 2,
A#=<X,
B#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup ? ;
I = 3,
A#=<X,
B#=<X,
C#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup,
C in inf..sup ? ;
I = 4,
A#=<X,
B#=<X,
C#=<X,
D#>=X+1,
A in inf..sup,
X in inf..sup,
B in inf..sup,
C in inf..sup,
D in inf..sup ? ;
no
To get any index in L, holding an element V greater than N, you could write:
?- L=[1,2,3,1,2,3],N=2, nth1(I,L,V),V>N.
and to limit to first instance:
?- L=[1,2,3,1,2,3],N=2, once((nth1(I,L,V),V>N)).
If you have library(clpfd) available, and your list has domain limited to integers, element/3 can play the same role as nth1/3, giving a bit more of generality
Here's a solution, as others pointed out it's not general, it will only work if the List of integers and the Threshold are ground terms.
As with most list processing predicates we need to think about it recursively:
Check the header of the list (its first element). If it's greater than the provided threshold then we are done.
Otherwise apply step 1. to the tail of the list (the list that remains after removing the header).
As you want the index of the element (as opposed to its actual value), we also need to keep track of the index and increment it in step 2. To do that we'll need a helper predicate.
%
% Predicate called by the user:
%
% The element of List at Index is the first one greater than Threshold.
%
idx_first_greater(List, Threshold, Index) :-
% here we use our helper predicate, initializing the index at 1.
idx_first_greater_rec(List, Threshold, 1, Index).
%
% Helper predicate:
%
% idx_first_greater_rec(List, Threshold, CurIdx, FoundIdx) :
% The element of List at FoundIndex is the first one greater
% than Threshold. FoundIdx is relative to CurIdx.
%
% Base case. If the header is greater than the Threshold then we are done.
% FoundIdx will be unified with CurIdx and returned back to the recursion stack.
idx_first_greater_rec([H|_], Threshold, Index, Index) :- H > Threshold, !.
% Recursion. Otherwise increment CurIdx and search in the tail of the list
idx_first_greater_rec([_|T], Threshold, CurIdx, FoundIdx) :-
NewIdx is CurIdx+1,
idx_first_greater_rec(T, Threshold, NewIdx, FoundIdx).
Notes:
The predicate will fail if the empty list is passed or if no element greater than Threshold was found. This looks to me like a good behavior.
This solution is tail-recursive, so it can be optimized by Prolog automatically.
Sample output:
?- idx_first_greater([1,1,1,2,1,1], 1, Idx).
Idx = 4 ;
false.
?- idx_first_greater([1,1,1,2,1,1], 3, Idx).
false.
?- idx_first_greater([], 3, Idx).
false.
This is my first time asking a question here but I have a problem that I really can't wrap my head around which is Prolog recursion especially when it deals with list. So the task that I am supposed to solve is to write a drop predicate that works like this. For example, drop([1,2,3,4,5,6,7,8,9], 2, L) where L = [1,3,5,7,9] and N=n where elements at position n, 2n, 3n.... will be removed. The list starts from 1 is another thing to be noted.
Here is my attempt so far and thought process:
drop([], _, []).
indexOf([X|_], X, 1). %Using 1 because the question says the first element starts from 1.
indexOf([_|Ys], Y , I):-
indexOf(Ys, Y, N),
I is N + 1.
drop([X|Xs], Y, [X|_]) :-
indexOf([X|Xs] , X , A),
Z is A mod Y,
Z \== 0.
drop([X|Xs], Y, Zs) :-
%indexOf([X|Xs], X, A),
drop(Xs, Y, Zs).
I created an indexOf predicate to find the index of the elements starting from 1 . Next, my idea was to use the my first drop recursive case (in the code above it is the 5th case) to check and see whether the position of the element returns a remainder of zero when divided by the Y (second input). if it does not return a remainder of zero, then the X remains inside the list and is not dropped. Then, prolog moves on to the 2nd drop recursive case which can only be arrived when Z=0 and it will drop X from the list to return Zs. In essence, an element with index n, 2n, 3n... that is returned by indexOf will be dropped if it does not return a remainder of zero when divided by Y (second input).
I have not learnt Cut at this point of the course at the moment. I would appreciate if someone can point me to the right direction. I have been working on this for almost a day.
I am still trying to adapt the logic and declarative thinking in this programming paradigm. I would appreciate it if you could share with me, how did you personally go about mastering Logic programming?
First, looking at your approach, there's a flaw with using the indexOf/3. That is, at a given point in time when you need to know the index of what you're removing, you don't know what the item is yet until you get to it. At that point, the index is 1.
That's one issue with the following rule:
drop([X|Xs], Y, [X|_]) :-
indexOf([X|Xs], X, A),
Z is A mod Y,
Z \== 0.
The first subquery: indexOf([X|Xs], X, A) will succeed with A = 1 on its first attempt, just by definition (of course, X has index 1 in list [X|Xs]. As it succeeds, then the next line Z is A mod Y yields 1 since 1 mod Y is always 1 if Y > 0. And therefore, Z \== 0 will always succeed in this case.
Thus, you get the result: [X|_] where X is the first element of the list. So the first solution you get for, say, drop([1,2,3,4], 2, L). is L = [1|_]. Your third drop/3 predicate clause just recurses to the next element in the list, so then it will succeed the second clause the same way, yielding, L = [2|_], and so on...
Starting from the top, here's a way to think about a problem like this.
Auxiliary predicate
I know I want to remove every N-th element, so it helps to have a counter so that every time it gets to N I will ignore that element. This is done with an auxiliary predicate, drop/4 which will also have a recurring counter in addition to the original N:
drop(L, N, R) :-
drop(L, N, 1, R). % Start counter at 1
Base rule
If I drop any element from the empty list, I get the empty list. It doesn't matter what elements I drop. That's expressed as:
drop([], _, _, []).
You have this rule expressed correctly already. The above is the 4-argument version.
Recursive rule 1 - The N-th element
I have list [X|Xs] and X is the N-th element index, then the result is R if I skip X, reset my index counter to 1, and drop the N-th element from Xs:
drop([_|Xs], N, N, R) :- % I don't care what the element is; I drop it
drop(Xs, N, 1, R).
Recursive rule 2 - Other than the N-th element
I have list [X|Xs] and X is the A-th element (< N), then the result is [X|R] if I increment my index counter (A), and drop N-th elements from Xs with my updated index counter:
drop([X|Xs], N, A, [X|R]) :-
A < N,
NextA is A + 1,
drop(Xs, N, NextA, R).
Those are all the needed rules (4 of them).
I have a problem in which I have a list of elements and I have to cycle through all instances of a particular /2 predicate to find which one has he highest number of matching elements in its list. In terms of implementation I can't seem to figure out how I should be updating the highest match so far and then stopping when there are no more.
findAnswer(MyList, HighMatchNum,_):-
answer(X,Y),
myIntersection(MyList, Y, NUM), //handles a single instance check and returns how many elements match.
NUM > HighMatchNum,
findAnswer(MyList, NUM, answer(X,Y)).
//Knowledge base
answer(sample1, [a,b,c,d]).
answer(sample2, [d,c,e]).
there is library(aggregate):
findAnswer(MyList, HighMatchNum, K) :-
aggregate_all(max(N, Key),
( answer(Key, List),
myIntersection(MyList, List, N)
),
max(HighMatchNum, K)).
myIntersection(MyList, List, N) :-
intersection(MyList, List, L),
length(L, N).
% Knowledge base
answer(sample1, [a,b,c,d]).
answer(sample2, [d,c,e]).
yields
?- findAnswer([a], C, K).
C = 1,
K = sample1.
?- findAnswer([d,e], C, K).
C = 2,
K = sample2.
To find the best, we have to search through the whole list, to its end. We will maintain the best so far and its score as additional arguments:
best_match(MyList,R-RN):-
findall(X, (answer(A,L), X=A-L), ALL),
ALL = [A-L|T],
myIntersection(MyList, L, N),
find_best(MyList,T,A,N,R,RN).
find_best(_,[],A,N,A,N).
find_best(MyList,[B-H|T],A,N,R,RN):-
myIntersection(MyList, H, K),
( K>N -> find_best( MyList, T, B, K, R, RN)
; find_best( MyList, T, A, N, R, RN ).
this produces the name and score of the best match.
Simply assert it, I can not see how you can propagate the max value in your solution.
:- dynamic maxval/1
:- maxval(0).
findAnswer(MyList, HighMatchNum) :-
answer(X,Y),
myIntersection(MyList, Y, NUM), %handles a single instance check and returns how many elements match.
NUM > HighMatchNum, %If this fails, try other answer
retract(maxval(_), assert(maxval(X)),!, %else retract the previous value and assert the new one
findAnswer(MyList, NUM).
Finally check the value of maxval/1 as maxval(X). This algorithm will always fail so you will get the solution in the user database, the problem is with your implementation, you may check your logic. However it will assert the proper answer. You must remember to always implement a base case for any recursive procedure.