Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>Reason for non-termination</h2> <p>First, let us try to understand why your definition is not reversible. I will use a <a href="https://stackoverflow.com/questions/tagged/failure-slice">failure-slice</a> to better explain it. So consider the query <code>tree_len(X,1).</code> At first sight, everything is perfect, you get even a nice answer!</p> <pre><code>?- tree_len(T,1). T = n(_G267, leaf, leaf) </code></pre> <p>But never ask for another answer, for it will loop:</p> <pre><code>?- tree_len(T,1). T = n(_G267, leaf, leaf) ; ** LOOPS ** </code></pre> <p>So the answer we got was a bit of a distraction. It at first glance looked like everything was OK, and only on backtracking the real problem was encountered. This is something to get accustomed to in Prolog. Evidently, your query using 3 was better chosen. But that was luck.</p> <p>There is an easy way to ensure this in general. Simply add the extra goal <code>false</code> to the query. Adding <code>false</code> means, that we are no longer interested in any answers, since it can no longer succeed. In this manner all distraction is removed, and we face the problem directly:</p> <pre><code>?- tree_len(T,1), false. ** LOOPS ** </code></pre> <p>So, where did this loop come from? </p> <p>In pure, monotonic Prolog programs (such as this one), we can localize a reason for non-termination by adding some goals <code>false</code> into our program. If the resulting program (called <a href="https://stackoverflow.com/questions/tagged/failure-slice">failure-slice</a>) does not terminate, then the original program does not terminate either. This is the minimal failure-slice for our query:</p> <pre> ?- tree_len(T,1), <b>false</b>. <s>tree_len(n(_,leaf,leaf),1) :- <b>false</b></s>. tree_len(n(op,G,D),N) :- tree_len(G,TG), <b>false</b>, <s>tree_len(D,TD)</s>, <s>N is TG+TD</s>. </pre> <p>There is not much left of our program! It is this tiny fragment that is responsible for the non-termination. If we want to fix the problem, we have to do something in that tiny part. Everything else is futile.</p> <p>So what we need to do is to <em>somehow</em> change the program such that this fragment no longer loops.</p> <p>Actually, we have two choices, we could use <a href="https://stackoverflow.com/questions/tagged/successor-arithmetics">successor arithmetics</a> or <a href="https://stackoverflow.com/questions/tagged/clpfd">constraints like clpfd</a>. The former is available in any Prolog system, the latter is provided only in some like SWI, YAP, SICStus, GNU, B.</p> <h1>Using <a href="https://stackoverflow.com/questions/tagged/successor-arithmetics">successor-arithmetics</a></h1> <p>Now, 3 is represented by <code>s(s(s(0)))</code>.</p> <pre> tree_lensx(T, s(N)) :- tree_lendiff(T, N,0). tree_lendiff(n(_,leaf,leaf), N,N). tree_lendiff(n(op,G,D), s(N0),N) :- tree_lendiff(G, N0,N1), tree_lendiff(D, N1,N). </pre> <p>I used here several common coding techniques.</p> <h2>Differences</h2> <p>The actual relation is <code>tree_lendiff/3</code> which represents a natural number not by one argument, but rather using two. The actual number is the difference between both. In this manner it is possible to retain the definition reversible.</p> <h2>Avoiding left-recursion</h2> <p>The other technique was to avoid left-recursion. The length described by <code>tree_lendiff/3</code> actually is the length <em>minus</em> one. Remember the failure-slice we got first? The very same failure-slice would be present here too! However by "shifting" the length by one, the head of the recursive rule now ensures termination.</p> <h1>Using <a href="https://stackoverflow.com/questions/tagged/clpfd"><code>library(clpfd)</code></a></h1> <p>Originally, constrains over finite domains were developed to solve combinatorial problems. But you can use them also to get reversible arithmetics. The implementation in SWI and YAP even goes that far that it compiles into code which often equals in efficiency to the traditional non-reversible <code>(is)/2</code> while still being reversible.</p> <pre><code>:- use_module(library(clpfd)). tree_fdlen(n(_,leaf,leaf),1). tree_fdlen(n(op,G,D),N) :- N #= TG+TD, TG #&gt;= 1, TD #&gt;= 1, tree_fdlen(G,TG), tree_fdlen(D,TD). </code></pre> <p>This program more closely corresponds to your original definition. Nevertheless, remark the two goals <code>TG #&gt;= 1</code> and <code>TD #&gt;= 1</code> which added redundant information to ensure the termination of this program.</p> <p>We can now enumerate all trees of a certain range like so:</p> <pre><code>?- Size in 0..4, tree_fdlen(T, Size). Size = 1, T = n(_A,leaf,leaf) ; Size = 2, T = n(op,n(_A,leaf,leaf),n(_B,leaf,leaf)) ; Size = 3, T = n(op,n(_A,leaf,leaf),n(op,n(_B,leaf,leaf),n(_C,leaf,leaf))) ; Size = 4, ... ; Size = 4, ... ; Size = 3, ... ; Size = 4, ... ; Size = 4, ... ; Size = 4, ... ; false. </code></pre> <p>Note the exact order of the answer substitutions! It's not just going 1,2,3,4! Rather, answers are found in <em>some</em> order. It does not matter which one, as long as we are only interested in finding all solutions!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload