fabio
fabio

Reputation: 271

remove element from list without removing all duplicates

i want remove elements from a list but if i do this:

deletelist([3,1,2,3,4], [3], [3,1,2,4]) 

how remove only a 3 and not get this answer:

deletelist([3,1,2,3,4], [3], [1,2,4])

Thanks!

Upvotes: 1

Views: 691

Answers (2)

Agnius Vasiliauskas
Agnius Vasiliauskas

Reputation: 11267

If you want to get just unique values in list - use function list_to_set/2:

list_to_set([3,1,2,3,4],X) gives X = [3, 1, 2, 4].

EDIT:

So gnu prolog doesn't have built-in predicate list_to_set. You have to write it yourself. For this you must define set concept. What is set of list ? Set should have these attributes:

  • Set size must be less or equal to List size.
  • All members of Set must be members of List.
  • All members of List must be members of Set.
  • Set must not contain duplicated values.

Based on these assumptions you can write set_from_list predicate like this:

elem_unique(Elem, List) :-
  delete(List, Elem, ListWithoutElem),
  length(List, OrgLength),
  length(ListWithoutElem, DelLength),
  DelLength + 1 =:= OrgLength.

nth_elem_isUnique(N, List) :-
  nth1(N, List, Elem),
  elem_unique(Elem, List).

nth_elemOfList1_isMemberOfList2(N, List1, List2) :-
  nth1(N, List1, Elem),
  member(Elem, List2).

elements_from_nth_areUnique(N, List) :-
  (length(List, Len),
  N > Len)    %stoping condition for recursion
  ;
  (nth_elem_isUnique(N, List),
  M is N + 1,
  elements_from_nth_areUnique(M, List)   %recursion part
  ).

listIsUnique(List) :-
  elements_from_nth_areUnique(1, List).

elements_from_nth_inList1_areMembersOfList2(N, List1, List2) :-
  (length(List1, Len),
  N > Len)    %stoping condition for recursion
  ;
  (nth_elemOfList1_isMemberOfList2(N, List1, List2),
  M is N + 1,
  elements_from_nth_inList1_areMembersOfList2(M, List1, List2)  %recursion part
  ).

list2containsList1(List1, List2) :-
  elements_from_nth_inList1_areMembersOfList2(1, List1, List2).

set_from_list(Set, List) :-
  length(Set, LenSet),
  length(List, LenList),
  LenSet =< LenList,
  list2containsList1(List, Set),
  list2containsList1(Set, List),
  listIsUnique(Set),
  !.

So after calling set_from_list(Set, [3,1,2,3,4]) you will get Set = [3,1,2,4].

Upvotes: 1

CapelliC
CapelliC

Reputation: 60014

select/3 it's an useful builtin, frequently used to generate and test, and you can use to delete an element:

?- select(3,[3,1,2,3,4],L).
L = [1, 2, 3, 4] ;
L = [3, 1, 2, 4] ;
false.

each call removes a match, then you can control the desired behaviour

edit

to delete all elements from the second list:

deletelist(L, [], L).
deletelist(With, [D|Ds], Without) :-
    select(D, With, WithoutD),
    deletelist(WithoutD, Ds, Without).

Note that this will fail if any of elements to be deleted will not be found in list. To avoid this, apply a 'if .. then .. else ..'

deletelist(L, [], L).
deletelist(With, [D|Ds], Without) :-
    (  select(D, With, WithoutD)
    -> deletelist(WithoutD, Ds, Without)
    ;  deletelist(With, Ds, Without)
    ).

Now deletelist/3 will not enumerate all possible deletions. It commits to the first found. To resume the initial behaviour, that give on bactracking all different deletions, a less efficient procedure is required:

deletelist(L, [], L).
deletelist(With, [D|Ds], Without) :-
    select(D, With, WithoutD),
    deletelist(WithoutD, Ds, Without).
deletelist(With, [D|Ds], Without) :-
    \+ select(D, With, _),
    deletelist(With, Ds, Without).

Upvotes: 2

Related Questions