I am trying to find the value per key inputted in Prolog from an associative list. This function works but only for one pair. How can I get it to work for multiple pairs in a list?
assoc([pair(K,V)],Key,V) :-
K = Key.
assoc([pair(K,[])],Key,V):-
assoc([pair(K,[])],Key,V).
Your simple case, of just one pair in the list, could be written as follows:
assoc([pair(K,V)], K, V).
Your association is a list of such pairs. In Prolog, the simplest way to represent that is with a head-tail form: [H|T] where H is the first element and T is the tail or the rest of the list. In the case of a match, you shouldn't care whether there are any more elements in the list or not. So you should really write the above as:
assoc([pair(K,V)|_], K, V).
In other words, if the key of the first element matches, the value should match (and vice versa). I don't care what the rest of the association list looks like (thus, the _). If they don't, then this clause will fail and Prolog will test the next clause, which should check the rest of the list:
assoc([_|AssocRest], Key, Value) :-
assoc(AssocRest, Key, Value).
In this case, I don't care what the first pair is (I've taken care of that case already). So the head is _.
A more canonical way to represent a key value pair is with the term Key-Value. So the above would become:
assoc([K-V|_], K, V).
assoc([_|AssocRest], Key, Value) :-
assoc(AssocRest, Key, Value).
Related
I'm new to Prolog. Consider the following example:
MainList = [["A","a","0"],["A","b","0"],["B","a","0"],["B","b","0"],["A","a","1"],["B","b","1"],["C","c","1"],["C","a","0"]]
SubList = ["A","b","-']
The format of MainList: in the first index it should be the big letter, second index should be small letter and third index should be number. MainList can contain other formats (and lengths) but I choose this format because it is easier to explain. I just want to explain that there index of each sublists are divided into categories - first element of each list belongs to category1, second element to category2 and so on. The "-" symbol means we does know about the bond of that element with the other elements.
The relation I would like to create should go through the MainList and remove all of the sublists that does not follow SubList. For the example above, it should return:
Output = [["A","b","0"],["B","a","0"],["C","c","1"],["C","a","0"]]
Explanation: "A" is working together with "b" so every sublists that contains one of those elements, should be checked. if they both exists then it's ok (without duplicates). Otherwise if only one of them exists, it is not ok and we should not insert it into the Output list.
Another example (MainList is a bit different than the one before):
MainList = [["A","a","0"],["A","b","0"],["B","a","0"],["B","b","0"],["A","b","1"],["B","b","1"],["C","c","1"],["C","a","0"]]
SubList = ["C","a","0"]
Output = [["A","b","1"],["B","b","1"],["C","a","0"]]
I do understand the algorithm - we should go through each sublist of the MainList and check if the connection of the input-sublist is working, if so, we will insert it into the Output list. The problem is, I don't understand how to implement it right. What if the sublist contains more than 2 connections (as was shown in the second example)? How should I treat differently to the sublist?
EDIT: I'll try to explain a bit more. The place of each element is important. Each place represents a different category. For example, the first place might represent the big letters, the second place represents the small letters and the third place represents numbers (there could be more categories).
We get a sublist which represents a bond between two or more elements. For example we have a bond between "A" and "b": ["A","b","-"]. We should iterate through the sublists of MainList and check if each one of those sublists contains one of those elements ("A" and "b"). If it does, we should check if the bond is correct. For example if we have a sublist that has A (in the first index of course), then we should go to the second index and check if "b" is there. if it is not there, we should not insert it into the Output list, Otherwise we will insert. For example we have a bond ["A","b","-"] and we got into a sublists of MainList which looks as following: ["A","a","0"] or ["B","b","2"]. We will not inert those lists into Output. But if we got to sublists like: ["A","b","1"] and ["A","b","2"] and ["B","a","1"] we will insert those lists into Output.
I wrote a code that satisfies the output conditions for the examples you have given, but did not try other cases or consider the efficiency of the code, so you can maybe improve upon it. Here is the code :
subList(List,SubL,Out):-
(member("-",SubL),
select("-",SubL,SubRem)
;
\+ member("-",SubL),
SubRem = SubL),
findall(L,((member(L,List),checkEq(SubRem,L));
(member(L,List),checkNeq(SubRem,L))),Out).
checkEq([],_).
checkEq([S|Rest],[E|List]):-
S == E,
checkEq(Rest,List).
checkNeq([],_).
checkNeq([S|Rest],List) :-
\+ member(S,List),
checkNeq(Rest,List).
Explanation: What I did is that, first I removed the "-" character from the subarray(if it exists) in order to make the computations easier. Then, I either check the condition that each element in the SubList is in order with the elements of the selected sublist of the MainList. If this fails, I then check if none of the elements of SubList is contained in the selected sublist. If both checks fail, I move to the next sublist. Using findall/3 predicate, I find all combinations that satisfy either of these conditions and group them in the list Out
EDIT: Add the additional clause for the checkEq predicate :
checkEq([S|Rest],[E|List]):-
S \= E,
member(S,List),
checkEq([S|Rest],List).
So the final version is :
checkEq([],_).
checkEq([S|Rest],[E|List]):-
S == E,
checkEq(Rest,List).
checkEq([S|Rest],[E|List]):-
S \= E,
member(S,List),
checkEq([S|Rest],List).
I have a list of list and a list. I want to update the list of list by using the element from the second list.
For example:
I have a list of list
[[banan,NA],[apple,NA]] and a list [sweet,notsweet],
want to update the list of the list, so I will have a list of list
[[banana,sweet],[apple,notsweet]]
I have tried the code below, but I think I cannot figured out the base case correctly.
update([[]],[],[]).
update([[T|_]|HH],[FB|H2],[NState|_]) :-
NState=[T|FB],
update(HH,H2,NState).
Any help, will be much appreciate
thanks
You're almost there. First let's observe that the first list is empty if there are no more [fruit,*] pairs left, hence the first argument of your base case should be []. At that point the other lists have to be empty too, since they are of the same length.
In general, the first list will have a two-element list as its head, the first of which is being your object of interest and the second of which you don't care for, that is, something like [X,_]. The tail of that list will contain further X's, so let's maybe call it Xs. Then the first argument looks like [[X,_]|Xs]. The second argument is a flat list, so you can write [Y|Ys] (read as: the list starts with a Y that is followed by further Y's). The last argument is a two-element list [X,Y] that is followed by other such pairs (XY's), hence: [[X,Y]|XYs]. The relation has to hold for the tails as well, that can be described by a recursive goal. You can express the above in Prolog like so:
update([],[],[]).
update([[X,_]|Xs],[Y|Ys],[[X,Y]|XYs]) :-
update(Xs,Ys,XYs).
With these alterations to your predicate the example query from your comment yields the desired answer:
?- update([[banana,*],[apple,*]],[sweet,notsweet],C).
C = [[banana, sweet], [apple, notsweet]].
I am trying to write a predicate that succeeds if and only if the numbers in the list are in non-decreasing order. I am having a hard time trying to figure this out. I know that if each element is less than or equal to the previous one then it should return false but I am lost on how to do it.
ordered(L) :-
Recursion should usually be your first thought for approaching any problem in Prolog. This means:
Defining a base case, where you can easily determine that the predicate is true or false
In other cases, splitting the problem into parts - one part you can resolve immediately, another you can resolve recursively. These parts of the problem generally correspond to portions of the list.
In the simplest cases, the recursive logic is simply to apply some test to the first element of the list; if it passes, recursively apply the predicate to the remainder of the list.
In your case I think it is a bit more complex, as there is no meaningful way you can test an individual element for orderedness (and maybe that gives you a hint what the base case is ...).
ordered(L) :- ordered2(L).
% empty list is ordered
ordered2([]) :- true.
% list with one element is ordered
ordered2([_]) :- true.
% list is ordered if two first elements are ordered
% and the rest of list is ordered
ordered2([A,B|T]) :- A=<B, ordered2([B|T]).
I've been trying to find how I could do this, I'm trying to sort facts like:
package(X,Y,N), where N is a number. I want to create a list with the X values, ordered by the value of N (lowest to highest).
Tried using :
gera_caminho_tempo(,L):- findall(package(N,,S), package(,,S), Packages), msort(Packages, L).
But no results, any ideas?
For such tasks, check out the standard predicate keysort/2.
In your case, you can create pairs of the form N-package(X,Y,N), using for example:
?- findall(N-package(X,Y,N), package(X,Y,N), Pairs0).
Once you have obtained Pairs0, you can use keysort/2 to obtain the pairs sorted by key, where the first component of each pair acts as its key:
?- findall(N-package(X,Y,N), package(X,Y,N), Pairs0),
keysort(Pairs0, Pairs).
I leave relating such sorted pairs to only the sorted packages as an easy exercise.
I am trying to understand prolog through examples. I wrote a piece of code but it is not working in desired manner and I am unable to find the fault.
list([]).
test([],_,_).
test([Child|List],B,C) :-
append([Child],B,C),
test(List,B,C).
testme :-
list(Final),
test([1,2,3],Final,Result),
write(Result).
The functionality which I require from this code that the Result should be the reverse of input list. When I trace this code I found the reverse of input list in C but it is not returned.
I knows using reverse function I can easily find reverse of list but my interest is not in finding the reverse but to understand this code and working of prolog. So please can someone tell me where I am wrong and what modification is required in this code to work properly.
First let's see why your code does not work.
test([],_,_).
test([Child|List],B,C) :-
append([Child],B,C),
test(List,B,C).
You tried to define a recursive predicate with two clauses.
The arguments to this predicate are: the input list (1), an intermediate list (2) and the final result(3).
Your first clause is thus, wrong.
Suppose your input list is just an empty list. The result should be an empty list. But in your first clause you are leaving the third argument as-is. Either it was already bound to somenthing (not useful in this case) or it was unbounded and therefore it will keep that way.
The second clause deals with recursion. It takes the first element of your input list, append it in the front of the intermediate list yielding another intermediate list.
Now you do the recursion step calling test again. But note that now the three arguments are instantiated! so you won't be able to append another item to the final list.
Here goes your test/3 predicate modified to work as you expect:
test([],List,List).
test([Child|List],B,D) :-
append([Child],B,C),
test(List,C,D).
Now the first clause just unifies the second and third argument.
So in our first test case (empty input list), recall that the second argument was also an empty list, so the third argument will also be an empty list => correct.
Now for the recursive clause.
It will take the first element of the input list and append it to the intermediate list, and unify it on a fresh variable.
Now we go into the recursion step, but now we use that list (C) as the intermediate list.
This, on recursion, will build the output list, now bound to D which is what we use as output.
As you are just appending one element to the intermediate list each time, you could have got rid of the append with something like:
test([],List,List).
test([Child|List],B,C) :-
test(List,[Child|B],C).
Prolog rules can only "return" values (since there is really no distinction between parameters and return values) by unifying them with their arguments. Assuming you want an accumulator-based reverse operation, you will need three parameters to test, and it should act as if it was written as:
test(A, B, C) :- reverse(A, A2), append(A2, B, C).
Having an implicit append operation in test will make the recursion easier to write. Also, note that you can write append([A], B, C) as C = [A | B] (or replace C by [A | B]).