0

I have a complex program, a part of which looks somewhat like this:

check_list([]).
check_list([X|List]) :- (try_something(X) -> write(1);write(0)),nl,check_list(List).

The problem is that, when my list gets very large, Prolog gives me "Resource error: insufficient memory". I thought the problem might be somewhere in the if-then-clause. So I tried the following:

check_list([]).
check_list([X|List]) :- (\+try_something(X) -> write(0);write(1)),nl,check_list(List).

With that, the problem is gone (or at least it only appears with much much bigger lists). However, I have no clue why. What is so different in the two versions? Shouldn't both be the same?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
SandraS
  • 41
  • 3
  • 1
    The `X|List` is rather weird since it is not encapsulated with square brackets... – Willem Van Onsem Sep 24 '18 at 20:28
  • 1
    The reason why this probably has a huge impact, is because negation *does not bind*, and only succeeds once: something is *not* true if it is *negation as finite failure*, from the moment it matches once, the `\+ goal(X)` is `false`, and it will not look for more clues.. – Willem Van Onsem Sep 24 '18 at 20:30
  • But I think it will be usefull here to share the `try_something/1` predicate, to know for sure. – Willem Van Onsem Sep 24 '18 at 20:37
  • Sorry, I forgot the [ ] around `X|List`. And the end of the statements should be `check_list(List)`, of course. – SandraS Sep 24 '18 at 20:48
  • I still don't understand. Shouldn't `try_somethings` only succeed once in the first version as well? As soon as it succeeds, `write(1)` is to take place and no more backtracking is neccessary. Or is that not how it is handled? – SandraS Sep 24 '18 at 21:06
  • in an *if* there is no backtracking. Here the main difference is that `X` is *not* binding with negation. – Willem Van Onsem Sep 24 '18 at 21:11
  • OK, so in the first version, for each element of my list, X is (unneccessarily) bound to a value; in the second, it's not - and that's why less memory is needed and I don't get "insufficient memory". Correct? Makes some sense. Thx – SandraS Sep 24 '18 at 21:37
  • yes, but. the recursion is *tail* in both cases, so a *properly* tail-call optimizing *implementation* should have *forgotten* the `X`'s binding *before* making the tail call. yours probably doesn't forget it, which might be considered a bug. which implementation are you using? – Will Ness Sep 25 '18 at 12:43
  • @WillNess: no it is not a bug, since this can be used to *generate* a list: if you call it with `check_list(A)`. then we are building a list, so all "yielded" elements are still there. – Willem Van Onsem Sep 26 '18 at 19:39
  • 1
    @WillemVanOnsem oh-oh. a giant *oops* on my part. Thanks for pointing that out! maybe an implementation could make a distinction when used with ground lists, as it is here...(?) but not a bug for sure. – Will Ness Sep 27 '18 at 00:03

0 Answers0