0

How can I call a predicate if all calls to another predicate failed?

I have:

foo(A, B, C, D) :-
    (bar1(Y,Z); bar2(L, K, M)),
    foo(A, B, C, D).

What I want :

  • at any time, If bar1/2 has succeded, bar2/3 will never be executed.
  • if all bar1/2 calls failed, then bar2/3 will eventually be executed.

Sample Backtracting tree

            root                                           root                 
                                                            |
           /   \                                            |
          /     \          all bar1 failed                  |
         /       \                                          |
        / | \     \        ===>>>======>>>>                 |
       /  |  \     \                                        |
      F   F  F     F                                        E
time  0   1  2     3                                        4 

Abbreviation:
       Bar1 failed : F
       Bar2 executed : E
m09
  • 7,490
  • 3
  • 31
  • 58
user1428237
  • 49
  • 1
  • 6
  • 2
    I guess you are looking for "if bar1 then true else bar2". The rest should be easy with the answer to your previous question... – twinterer Jun 04 '12 at 09:29

1 Answers1

1

You're looking for what's known as "soft cut",

A *-> B ; C.

This is equivalent to (A,B) ; (\+A,C): if A succeeds at least once, the above is equivalent to A,B. If not, it's equivalent to just C. The goal A is not retried.

Simple if construct allows the test predicate to succeed only once:

A -> B ; C.

is equivalent (almost - see you manual for details) to (once(A),B) ; (\+A,C), except that the goal A isn't retried.

Thus your case shall be

foo(A, B, C, D) :-
    (bar1(Y,Z) *-> true ; bar2(L, K, M)),
    foo(A, B, C, D).

addition: Some Prolog implementations might not have this construct *-> available (e.g. gprolog). In that case I see two possibilities. Either

(A , B) ; (\+A , C)

although it would retry A, or (writing goal(X) for A)

bagof(g(X), goal(X), L) -> ( member(g(X), L), B ) ; C 

Of course the order of side-effects will be changed by this. The name L of the variable should be chosen such that it does not appear free in B.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • When I have tried your comment on gprolog, it gives syntax error. Are there any way, I can catch same effect ? –  Jun 07 '12 at 06:36
  • I guess you can write that equivalent code, `(A,B) ; (\+A,C)`. It just will re-try `A`, so any side-effects will occur twice. – Will Ness Jun 07 '12 at 07:09
  • Or try this `( bagof(g(Y,Z), bar1(Y,Z), L) -> member(g(Y,Z),L) , true ; bar2(L,K,M) ), foo(A,B,C,D)`. Works in SWI Prolog. `true` is there just as a placeholder; it isn't needed by itself. – Will Ness Jun 07 '12 at 07:35
  • Sorry for late reply, I have tried *->, but gprolog cannot compile it. I do not understand why. –  Jun 07 '12 at 09:36
  • what is bagof ? Is it built in feature or Is it declared by you ? ( I could not found any explanation in google ) –  Jun 07 '12 at 10:05
  • @gcc it's in [the manual](http://www.gprolog.org/manual/html_node/gprolog033.html), although I prefer [SWI documentation](http://www.swi-prolog.org/pldoc/man?predicate=bagof/3). – Will Ness Jun 07 '12 at 10:07
  • (A,B) ; ( \+ A, C) === > infinite loop –  Jun 07 '12 at 10:08
  • I know that A,b,C are just place holder. For your second advice ( bagoff ), How can I change it to my case ? ( I am using bar(X,Y), bar1(X,Y) ) . What should I write the place which is holded by L ? –  Jun 07 '12 at 10:13
  • @gcc `( bagof( g(X,Y), ( bar(X,Y),bar1(X,Y) ) , L) -> (member(g(X,Y),L), stuff) ; otherstuff )`. "stuff" will be tried for each solution binding of `X,Y` of the conjunction `( bar(X,Y),bar1(X,Y) )`. "otherstuff" will be tried if there were no solutions for the conjunction. "stuff" should ***not*** have any free variable named L. – Will Ness Jun 07 '12 at 10:23
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12245/discussion-between-gcc-and-will-ness) I have some misunderstand. Can you help me ? –  Jun 07 '12 at 10:30
  • What you *really* want is `if_/3`, not `(*->)/2`! – repeat Oct 16 '15 at 09:33
  • @repeat yes, `if_` seems a very important development. do you know about some paper or blog entry about it? – Will Ness Oct 16 '15 at 09:38
  • Not yet, but soon. In the meantime, I'll write an answer to http://stackoverflow.com/questions/2849045/if-in-prolog. – repeat Oct 16 '15 at 11:15