Is there a way to add an element to a list, without caring if it already exists and the list still not containing duplicates?
If you are looking for efficiency there are better data structures than lists, ready to use in SWI-Prolog. In particular, your use case fits on add_nb_set(+Key, !Set, ?New).
Argument New controls the behaviour WRT duplicates: you should use true for don't care.
Argument Set must be 'allocated' with empty_nb_set(Set)
edit: I apologize, argument Set must be a free var for don't care behaviour, like for instance add_nb_set(Key, Set, _), as evident for the documentation page....
You could first check whether the item is contained in the list and add it only if it's not.
For example:
add_no_duplicates(List, Item, NList):-
member(Item, List) -> NList=List ; NList=[Item|List].
Test:
?- add_no_duplicates([a,b,c],d,L).
L = [d, a, b, c].
?- add_no_duplicates([a,b,c,d],d,L).
L = [a, b, c, d].
Note that add_no_duplicates/3 as written needs the input list (List) to be instantiated.
Basically you want to implement a set; check the list manual page for set operations.
It appears that there is no add but there is union/3 so you can add elements by intersecting with a set of the new element (intersection([NewEl], OldSet, NewSet). Note that you dont have to convert a list to a set (a list is a set as long as it doesnt have duplicates; list_to_set/2 just eliminates them).
Now if you have a list with duplicates and you want sometimes to add an element the way you stated you will have to implement something yourself.
Related
I'm trying to delete an element form a list of lists. This is an example of what I'm trying to do:
List=[[1,2],[1,3],[4,2]]
Max=2
delete(List,[_|Max],List2)
List2=[[1,3]]
Explanation: so given List I'm trying to use delete/3 to remove every element that has Max as its second element in its sub list. I'm not sure if this is possible with the standard delete/3 predicate. Thanks for the help!
This is what I have tried so far for my own predicate. I realize this isn't working and won't do what I want it.
second_element(E, []).
second_element(E,L):-
member(X,L),
X==[_,E],
delete(L,X,L2),
second_element(E,L2).
I'm not going to just give you the answer to your assignment. But I can give you a pattern commonly used in Prolog that it follows. This plus the hint I provided in comments are all the tools you need to solve the problem.
If you have a list, L, and you want another list, RL, that omits those elements of L that meet a certain criteria, then this is handled with a common recursive pattern:
filtered_list([], []). % The empty list is a filtered version of the empty list
filtered_list([H|T], R) :-
( meets_criteria_for_omission(H)
-> R = RT % R will not have H if it meets criteria for omission
; R = [H|RT] % R will have H if it does not meet criteria for omission
),
filtered_list(T, RT).
In the case of the original problem, the criteria for omission is that the element being checked is a list whose second element is given. That means that element being checked against must be provided as an argument. And the criteria to check is whether it is the second element of the given list.
This is as much information as I can give without simply handing over the complete answer, but it's 85% of the way there. Time to use the noodle.
I have a number of lists containing letters and I have written a predicate that checks whether or not there are duplicates present in one of these given lists:
noDuplicates([]).
noDuplicates([H|T]):-
not(member(H, T)),
noDuplicates(T).
I have 10 lists and I want to know if there are no duplicates in any of them, so I made them into sublists of one big list, something like:
[[A,B,C], [C,A,D], [E,F,G]...]]
(So there can be duplicates in the big list, but not the individual sublists).
I get that I have to do the duplicates test 10 times; once for every sublist, but how do I write this in Prolog? I could probably write it down 10 times, but my guess is I can use recursion to make prolog repeat itself until all sublists have been checked.
So basically: I want this predicate to repeat itself N times, until N is 10. I'm really struggling with it though. Does anyone have any idea on what to do?
Let us generalize the question as follows:
You have a predicate p/1 that expresses what you want for a single list.
Thus, to lift this definition to a list of such lists, you can define a predicates ps/1 as follows:
ps([]).
ps([L|Ls]) :-
p(L),
ps(Ls).
Every time you see this pattern, you can use maplist/2. That is, the above is equivalent to:
ps(Ls) :- maplist(p, Ls).
The goal maplist(p, Ls) is true iff p holds for each element L of Ls.
Note that it will limit your understanding of Prolog if you think in terms of "looping" and "repeating". These are imperative notions and only make sense when the list is already fully instantiated. However, from Prolog, we expect more than that: We expect a full-fledged relation to also generate lists for which the relation holds. And in such cases, there is nothing to "repeat" yet: We start from nothing, and ask Prolog what solutions there are in general.
Thus, think in terms of describing when the relation ps/1 holds for lists of lists:
It holds for the empty list [].
It holds for the list [L|Ls] if our initial predicate (p/1) holds for L, and ps/1 holds for the remaining list Ls.
This declarative reading is applicable in all directions, no matter how many list elements are already instantiated, if any. It works for 10 lists just as well as for zero and 50.
Given string lists [a, b, c] and [d, e] for example,
return the list [ad, bd, cd, ae, be, ce].
Please don't give me an answer, just a point the the right direction for a new learner.
This problem is tricky because you need to traverse two lists. If you write it as one function there are two recursions to keep track of.
One way to approach it is to start with a simpler problem. Say you have a list of strings l and a string s. Can you write a function that makes a new list with s appended to each of the strings in l?
There is a straightforward solution to this simpler problem using List.map. Or you can write an explicitly recursive function.
After that you have only one remaining recursion to figure out, which may be easier.
Update
Now you have your function you can easily write a function that appends a string to all the elements of the first list of strings. The layout would look like this:
let cross_concat list1 list2 =
let concat_string_to_list1 s = concat_string_to_list list1 s in
...
With this definition, you can use List.map again to get the final results (need to concatenate the resulting lists of strings into one list). Or you can use List.fold_right to build up the result as you go. Or you can write your own recursive function.
If you haven't written too many recursive functions, this would be something to think through.
I want to define predicate which takes a list, adds an element to the list, let's say the number "1", and then returns the list.
I've found out I can add elements to a list using append/3, but I want to use in inside another predicate, thus why I want it to return "my modified list".
My object-oriented mindset tells me to ask the interpreter something like: ?-append(X,5,X). , so that it takes the list X, adds 5 to it, and returns "the new X", but I know that's not how unification works, so my mind is kinda in a glitch.
Can anyone please try to explain how something like this could be achievable?
You are already very close to the solution, so I only rephrase what you are beginning to sense already:
First, you cannot modify a list in pure Prolog.
Instead, you should think in terms of relations between entities. In your case, think in terms of relations between lists.
So, "adding the number 1" to a list is a relation between two lists, which could look like this:
list_with_one(Ls, [1|Ls]).
Note that this works in all directions! You can use it to:
generate answers
test particular cases
"reverse" the direction etc.
So, all you need to do in your case is to think in terms of relations between lists: One without an element, and how this relates to a different list with the element.
Obviously, these two lists will be indicated by different variables and different arguments.
Note in particular that append(X, 5, X) cannot hold: First of all, append/3 is meant to be a relation between lists, and 5 is not a list. Second, assuming you wrote for example append(Xs, [5], Xs), then this would be true if there where a list Xs such that if the element 5 were appended to Xs, the resulting list would again be Xs. Good luck finding such a list... Note also the naming convention to denote lists by letting the variable name end with an s.
It is also falls a bit short to blame this on your "object-oriented mindset", since you can have object oriented programming in Prolog too.
Although lists in Prolog cannot be modified, it is possible to add elements to the end of a list with an unspecified length. In this way, items can be "appended" to a list without creating another list:
:- initialization(main).
append_to_list(List,Item) :-
append_to_list(List,Item,0).
append_to_list(List,Item,Index) :-
% using SWI-Prolog's nth0 predicate
(nth0(Index,List,Check_Item),
var(Check_Item),
nth0(Index,List,Item));
(Next_Index is Index+1,
append_to_list(List,Item,Next_Index)).
main :-
A = [1,2,3|_],
append_to_list(A,4),
append_to_list(A,7),
writeln(A).
In this example, A becomes [1,2,3,4,7|_].
I have a list contains some elements and now with the help of lists:foreach I am fetching some more records and I want to append each value to my existing list elements without creating new variable as doing in other languages with help of array.
Here is my sample code which I am getting:
exception error: no match of right hand side value [6,7,1].
Sample Code:
listappend() ->
A = [1,2,3,4,5],
B = [6,7],
lists:foreach(fun (ListA) ->
B = lists:append(B, [ListA])
end, A),
B.
I want output like,
B = [6,7,1,2,3,4,5].
First of all, this feature already exists, so you won't need to implement it yourself. In fact, the list can take two lists as arguments:
1> lists:append([1,2,3,4,5], [6,7]).
[1,2,3,4,5,6,7]
Which is actually implemented as:
2> [1,2,3,4,5] ++ [6,7].
[1,2,3,4,5,6,7]
Please bear in mind that the ++ operator will copy the left operand, so this operation can easily lead to quadratic complexity. Said that, you probably want to construct your lists using the "cons" operator (eventually reversing the list at the end of the computation):
3> [1|[2,3,4,5,6,7]].
[1,2,3,4,5,6,7]
In any case, you can have two arguments in your function, which are the two lists to append, instead of defining them in the body of the function. This way, the values of A and B will change every time you call the my_append/2 function.
my_append(A, B) ->
YOUR_CODE_GOES_HERE
As a note and regarding the actual error you're getting, this is due to the following line:
B = lists:append(B, [ListA])
During each iteration, you're binding a new value to the variable B, which is already bound to the value [6,7].
Variables in Erlang are immutable. That means you cannot assign a new value to a variable once it has been bound, and this is why you get the 'no match' error. The old value does simply not match the new value you are trying to assign to the variable. Instead, you can create a new list using e.g lists:append based on the old one. You should probably start by looking at recursion and how you can use it to manipulate lists.