Cut (!) Against Return

I am developing a predicate in Prolog and it is possible to complete it to the end. For this reason I was looking for a command similar to return;

(C ++). I have used cut !

but I doubt what this literally means and if it does exactly what it does return;

. Example:

pred(X) :- 
           X = 1 -> do this, !; else do that,
           write('x =/= 1').

void pred(int X) {
       if (X = 1) {do this; return;} else do that;
       cout << "x =/= 1";
}

      

Are the functions above the same?

+3


source to share


4 answers


There is no direct correspondence between the Prolog execution engine and the mechanisms of traditional imperative languages. Thus, any analogy is more likely to lead you on a dead path.

In your example, the cut has no effect: it only (->)/2

excludes the branch already Else

. In a sense, he makes a "tiny" cut into If

and an alternative. Would there be another suggestion pred/1

, your cut excludes that branch too.

Prolog's execution mechanism is much more complex. If you insist on analogies in imperative languages, consider iterators. Cutting causes all iterators in the cut region to be performed on the next one next

. So it's a bit like break

. Somewhat. But only in a language that supports iterators in the first place.



If you want to learn Prolog, don't try to develop your ideas from these (dilapidated) counterparts.

It is better to start by understanding what relations the Prolog predicate describes and brings the meaning of the predicate closer to it. The procedural concepts will fit in one after the other.

+7


source


So you have the procedural code:

def foo():
  if cond1:
    handle_cond1()
    return

  if cond2:
    handle_cond2()
    return

  do_other_stuff()

      

You can convert this to a procedural domain so that you don't have explicit returns by first doing this:

def foo():
  if cond1:
    handle_cond1()
    return

  if cond2:
    handle_cond2()
  else:
    do_other_stuff()

      

And then do the following:



def foo():
  if cond1:
    handle_cond1()
  else:
    if cond2:
      handle_cond2()
    else:
      do_other_stuff()

      

After removing the statements, return

you can convert this to Prolog:

foo :-
  cond1 -> 
    handle_cond1
  ; (cond2 ->
       handle_cond2
     ; do_other_stuff 
     ).

      

There is no way in Prolog to be successful immediately. (You can get out of order immediately with fail

). To achieve a similar flow, you will have to perform such a conversion. Your best bet would be to follow @false's advice and learn Prolog on your own terms.

+3


source


As stated, Prolog does not portray procedural thoughts well.

I find a better way to think of Prolog and its "database" as a tree (forest?). The analogy is a bit rough, as the graph contains loops (recursion).

When you ask the prolog engine to determine whether a particular statement (predicate) is true or false, it starts to traverse depth in the first, left-to-right tree, using union (pattern matching) to guide the ECP. When the traversal reaches the leaf node, the predicate true

. Upon his return, he ... retreats and continues to walk the tree. When there are no more leaf nodes available, the predicate fails.

Prolog is a descriptive language: you describe the conditions for success in terms of predicate calculus. Then you just let the Prolog engine find the right solutions. If you try to train procedural, imperative thinking in a model, in addition to making things more complicated than they should otherwise be, in my experience, you pretty much guarantee poor performance.

I found Leon Sterling and Eliot Shapiro's tutorial, The Art of Prolog , to be invaluable and far more instructive and enlightening than Clocksin and Mellish's Programming in Prolog.

Art of Prolog Cover

Edited for note: Your pattern predicate

pred(X) :- 
   X = 1 -> do this , ! ; else do that ,
   write('x =/= 1')
   .

      

has some problems.

  • First, just like C or C # or other procedural languages ​​where operators and

    and or

    have different precedences, so the type expression if ( a && b || c && d ) ...

    probably doesn't bind what you think to operator precedence, your example predicate probably doesn't do what you think what it does: as written, it links like

    pred(X) :-
      X=1 ->
        ( do_this , ! )
      ;
        ( do_that , write( 'x =/= 1' ) )
      .
    
          

    When you might want,

    pred(X) :-
      ( X=1 ->
        ( do_this , ! )
      ;
        do_that ,
      ) ,
      write( 'x =/= 1' )
      .
    
          

    You need to use parentheses to wash your intended binding explicitly:

    pred(X) :- 
       ( X=1 ->
           ( do_this , ! )
       ;
           do_that
       ),
       write('x =/= 1')
       .
    
          

  • Also, !

    you don't need to cut ( ) in your example, since the implication operator ->

    acts as if a cut were involved. It:

    foo(X) :-
      truthy(X) ->
        writeln('truthy!')
      ;
        writeln('falsy')
      .
    
          

    is pretty much like

    foo(X) :- truthy(X) , ! ,
              writeln( 'truthy' ) .
    foo(_) :- writeln( 'falsy'  ) .
    
          

  • Third, you have to use pattern unification and matching in your head. Ignoring it write/1

    , your example might make sense as

    pred(1) :- do_this , ! .
    pred(X) :- do_that . 
    
          

And in general, if you're learning prologue, I'd say avoid the implication operator (and alternation (logical OR, ';'/2

) in general. Prefer explicit multiple-clause predicates for expressing choices. Instead of something like

foo(X) :- ( try_this(X) ; try_that(X) ; finally(X) ) .

      

prefer

foo(X) :- try_this(X) .
foo(X) :- try_that(X) .
foo(X) :- finally(X) .

      

And instead of implication:

foo(X) :- X=1 -> try_this(X) ; try_that(X) .

      

prefer something like this:

foo(1) :- ! , try_this(X) .
foo(X) :- try_that(X) .

      

I think it makes it easier to find what's going on as it makes explicit selections (and excludes them).

+3


source


I'd like to add that having solid coding rules can also help improve the readability of your code a bit and avoid the fragility of your code.

Starting from @DanielLyons code:

foo :-
  cond1 -> 
    handle_cond1
  ; (cond2 ->
       handle_cond2
     ; do_other_stuff 
     ).

      

In practice, there are cascades with several nested if-then-else constructs: "if-then-elseif-then-elseif-then-elseif-then -...- else".

To improve readability, you can clean up your code layout and adjust the indentation level:

foo :-
   (  cond1 -> handle_cond1
   ;  cond2 -> handle_cond2
   ;           do_other_stuff 
   ).

      

Whenever lines of code get too wide, a slightly wider and taller style may be preferable:

foo :-
   (  cond1
   -> handle_cond1
   ;  cond2 
   -> handle_cond2
   ;  do_other_stuff 
   ).

      

+2


source







All Articles