1

I want to use the predicate subtract to remove all list items that are not lists.

Sample query showing the expected answer:

?- subtract([1,2,[3],[],4,5,[8]], ..., Xs).
Xs = [[3],[],[8]].

How can I do that? Thanks!

repeat
  • 18,496
  • 4
  • 54
  • 166
Human
  • 47
  • 1
  • 6
  • 2
    If a variable, `X`, can *unify* with `[_|_]` or `[]`, then `X` is a list. SWI prolog also has a built-in, `is_list(X)` which succeeds if `X` is a list. You should use this information to figure out how you an write this predicate. – lurker Oct 24 '15 at 17:12

2 Answers2

1

Use tfilter/3 together with the reified test predicate nil_or_cons_t/2: it's monotonic!

nil_or_cons_t(Xs,T) :-
   (  nonvar(Xs)
   -> (  nil_or_cons(Xs)
      -> T = true
      ;  T = false
      )
   ;  T = true , nil_or_cons(Xs)
   ;  T = false, freeze(Xs,\+nil_or_cons(Xs))
   ).

nil_or_cons([]).
nil_or_cons([_|_]).

Sample query:

?- tfilter(nil_or_cons_t, [1,2,[3],[],4,5,[8]], Lss).
Lss = [[3],[],[8]].

Edit

This answer is incorrect (too general = "test predicate too simplistic").

Please see my follow up answer for details.

repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    This `freeze(Xs, \+nil_or_cons(Xs))` would deserve to be expressed in a cleaner manner, but for the time being, that is the best we can get. – false Oct 24 '15 at 21:32
1

My old answer was clearly incorrect.

Why? It classified terms like [_|non_list] as lists... Bad!

How come? The non-recursive nil_or_cons_t/2 can be too shallow.

What to do? Use the recursive reified predicate list_t/2 instead!

repeat
  • 18,496
  • 4
  • 54
  • 166
  • 1
    Not necessarily so. We often accept generalizations, think of differences, think of the second and third argument of `append/3.` The precise circumstances for admissible generalizations are not that evident. An easy way to check is to ask why `nil_or_cons_t(_,_).` is an inadmissible generalization. – false Mar 08 '20 at 12:59