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?
source to share
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.
source to share
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.
source to share
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.
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
andor
have different precedences, so the type expressionif ( 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 likepred(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 aspred(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).
source to share
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
).
source to share