home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / PASCAL / MISCTI10.ZIP / TI299.ASC < prev    next >
Encoding:
Text File  |  1988-04-18  |  28.3 KB  |  677 lines

  1. PRODUCT : TURBO PROLOG     NUMBER : 299
  2. VERSION : ALL
  3.      OS : PC-DOS
  4.    DATE : June 13, 1986
  5.  
  6.   TITLE : EXPRESSING PROCEDURAL ALGORITHMS
  7.  
  8. This  information is available on CompuServe in Data Library 6 of 
  9. the Borland Forum.
  10.  
  11. *****************************************************************
  12.  
  13.             Expressing Procedural Algorithms in Prolog
  14.  
  15.                      Michael A. Covington
  16.  
  17.                     Research Report 01-0012
  18.               Advanced Computational Methods Center
  19.                      University of Georgia
  20.                      Athens, Georgia 30602
  21.  
  22.                     May 26, 1986 - 4:25 p.m.
  23.  
  24. *****************************************************************
  25.  
  26. This  work was supported by PACER Grant 85PCR18 from Control Data 
  27. Corporation  and by Grant Number INS-85-02477 from  the  National 
  28. Science Foundation.  The opinions expressed here are solely those 
  29. of the author. The assistance of Donald Nute and Andre Vellino is 
  30. gratefully  acknowledged.   Printed  copies  of  this  and  other 
  31. research reports are available free on request.
  32.  
  33. With  the  increasing  popularity  of Prolog  as  an  application 
  34. programming  language,  it is often necessary to  develop  Prolog 
  35. equivalents  for  algorithms  that were originally  expressed  in 
  36. procedure-oriented  languages.  This paper presents a  number  of 
  37. strategies  for  doing so.  I assume that the reader  is  already 
  38. familiar  with  the  basic mechanisms  of  Prolog  -- unification 
  39. (matching), backtracking, and the like.
  40.  
  41. Unless otherwise noted, the examples given here work both in full 
  42. "Edinburgh" or "DEC-10" Prolog (Clocksin and Mellish 1984) and in 
  43. Turbo Prolog (1986),  which is a subset of the full language. The 
  44. examples   are   written  in  Turbo  Prolog  syntax,   but   with 
  45. declarations  omitted  for brevity.  Full Prolog is  used  -- and 
  46. noted as such -- when its features are needed.
  47.  
  48. The procedural interpretation of logic:
  49.  
  50. Prolog  is  often  described as  a  non-procedural  language.  In 
  51. reality, it is a semi-procedural language -- a compromise between 
  52. procedural  and non-procedural programming,  giving some  of  the 
  53. advantages  of  each.  In a truly  non-procedural  language,  the 
  54. programmer  would  provide  only  a  logically  rigorous  set  of 
  55. conditions that the program must fulfill; the computer would then 
  56. automatically generate an algorithm from them. Such things as the 
  57. order in which the conditions were written would have no effect.
  58.  
  59. In  the interest of efficiency,  Prolog contains some  procedural 
  60. elements.  The  Prolog programmer specifies not only the rules of 
  61. inference to be used in solving a problem,  but also the order in 
  62. which they are to be tried.  Crucially,  the programmer can  even 
  63. specify  that  some potential paths to a solution should  not  be 
  64. tried at all.  This makes it possible to perform efficiently many 
  65. computations   that  would  be  severely  inefficient,   or  even 
  66. impossible, in pure Prolog.
  67.  
  68. The  key principle of Prolog is the procedural interpretation  of 
  69. logic. Consider the following Prolog rule set:
  70.  
  71.     [1]         in_north_america(X) :- in_usa(X).
  72.                 in_usa(X) :- in_georgia(X).
  73.                 in_georgia(atlanta).
  74.  
  75. This can be interpreted as a set of facts ([2] below) or as a set 
  76. of procedure definitions ([3] below).
  77.  
  78.     [2]         X is in North America if X is in the USA.
  79.                 X is in the USA if X is in Georgia.
  80.                 Atlanta is in Georgia.
  81.  
  82.     [3]         To prove that X is in North America,
  83.                      prove that X is in the USA.
  84.                 To prove that X is in the USA,
  85.                      prove that X is in Georgia.
  86.                 To prove that Atlanta is in Georgia,
  87.                      do nothing.
  88.  
  89. The  unfamiliar task of drawing inferences from data  is  thereby 
  90. reduced to the very familiar task of calling procedures.
  91.  
  92. In  what  follows  I will unashamedly refer to  Prolog  predicate 
  93. definitions as procedures.
  94.  
  95. Conditional execution:
  96.  
  97. One  of the most important differences between Prolog  and  other 
  98. programming languages is that, in general, Prolog procedures have 
  99. multiple  definitions  (clauses),  each applying under  different 
  100. conditions.  In Prolog,  conditional execution is expressed,  not 
  101. with   if  or  case  statements,   but  with  these   alternative 
  102. definitions of procedures.
  103.  
  104. Consider  for  example  how we might translate  into  Prolog  the 
  105. following Pascal procedure:
  106.  
  107.     [4]         procedure writename(X:integer);
  108.                 begin
  109.                   case X of
  110.                     1: write('One');
  111.                     2: write('Two');
  112.                     3: write('Three')
  113.                   end
  114.                 end;
  115.  
  116. This is done by giving writename three definitions:
  117.  
  118.     [5]         writename(1) :- write("One").
  119.                 writename(2) :- write("Two").
  120.                 writename(3) :- write("Three").
  121.  
  122. Each  definition  matches in exactly one of the  three  cases.  A 
  123. common mistake is to write the clauses as follows:
  124.  
  125.  
  126.                 writename(X) :- X=1, write("One").
  127.                 writename(X) :- X=2, write("Two").
  128.                 writename(X) :- X=3, write("Three").
  129.  
  130. This gives correct results but is needlessly inefficient. It is a 
  131. waste of time to go into an inapplicable clause,  perform a  test 
  132. that fails,  backtrack out,  and try another clause, when the use 
  133. of  the inapplicable clause could have been avoided in the  first 
  134. place.
  135.  
  136. unit  of the program into a separate procedure.  Each if or  case 
  137. statement  should,  in  general,  become a  procedure  call.  For 
  138. example, the hypothetical Pascal procedure:
  139.  
  140.     [7]         procedure a(X:integer);
  141.                 begin
  142.                   b;
  143.                   if X=0 then c else d;
  144.                   e
  145.                 end;
  146.  
  147.     should go into Prolog as follows:
  148.  
  149.     [8]         a(X) :- b,
  150.                         c_or_d(X),
  151.                         e.
  152.  
  153.                 c_or_d(0) :- c.
  154.                 c_or_d(X) :- X<>0, d.
  155.  
  156. This  imposes  a disciplined organization on the program that  is 
  157. even more rigorous than the structured ("go-to-less") programming 
  158. that serves as the basis of Pascal, Ada, and C.
  159.  
  160. Guarded command sets:
  161.  
  162. As  Kluzniak and Szpakowicz (1985) have pointed  out,  Dijkstra's 
  163. "guard" concept (Dijkstra 1975) is easily expressed in Prolog.  A 
  164. guarded command set is a structure of the form
  165.  
  166.     [9]         if
  167.                   C1 -> A1
  168.                   C2 -> A2
  169.                   C3 -> A3
  170.                 fi
  171.  
  172. where C1,  C2,  and C3 are conditions and A1,  A2, and A3 are the 
  173. corresponding  actions.   During  program  execution,   all   the 
  174. conditions are evaluated. If all of the conditions are false, the 
  175. program terminates.  Otherwise,  one of the actions corresponding 
  176. to a true condition is selected for execution.
  177.  
  178. If  exactly  one condition is true,  the guarded command  set  is 
  179. equivalent to an if-then or case statement.  In fact,  the Prolog 
  180. procedure "c_or_d" in [8] can be expressed in more  Dijkstra-like 
  181. (though less efficient) form as:
  182.  
  183.    [10]        c_or_d(X) :- /* condition */ X=0,  /* action */ c.
  184.                c_or_d(X) :- /* condition */ X<>0, /* action */ d.
  185.  
  186. Guarded  command  sets  were developed as part of  a  system  for 
  187. deriving  algorithms  from formal specifications of the  problems 
  188. they must solve. In Dijkstra's system, if more than one condition 
  189. is  true,  no  assumptions are made about which  action  will  be 
  190. taken.   In  Prolog,  all  possible  actions  will  be  taken  on 
  191. successive  backtracking passes,  in the order in which they  are 
  192. given in the program.
  193.  
  194. The "cut" operator:
  195.  
  196. Let's  consider  another version of "writename" ([4] above)  that 
  197. includes  a "catch-all" clause to deal with numbers  whose  names 
  198. are  not  given.  In many extended forms of Pascal,  this can  be 
  199. expressed as:
  200.  
  201.     [11]        procedure writename(X:integer);
  202.                 begin
  203.                   case X of
  204.                     1: write('One');
  205.                     2: write('Two');
  206.                     3: write('Three')
  207.                   else
  208.                     write('Out of range')
  209.                   end
  210.                 end;
  211.  
  212. One way to express this in Prolog is the following:
  213.  
  214.     [12]        writename(1) :- write("One").
  215.                 writename(2) :- write("Two").
  216.                 writename(3) :- write("Three").
  217.                 writename(X) :- X<1, write("Out of range").
  218.                 writename(X) :- X>3, write("Out of range").
  219.  
  220. This  gives correct results but lacks conciseness.  In  order  to 
  221. make  sure that only one clause is applicable to each number,  we 
  222. have  had to add two more clauses.  What we would like to say  is 
  223. that the program should print "Out of range" for any number  that 
  224. has  not matched any of the first three clauses.  We could try to 
  225. express this as follows, with some lack of success:
  226.  
  227.     [13]        /* incorrect */
  228.                 writename(1) :- write("One").
  229.                 writename(2) :- write("Two").
  230.                 writename(3) :- write("Three").
  231.                 writename(_) :- write("Out of range").
  232.  
  233. (Recall  that  the  anonymous  variable,   written   _,   matches 
  234. anything.) The problem here is that the goal "writename(1)",  for 
  235. example,  matches both the first clause and the last clause. If a 
  236. subsequent  goal fails and causes backtracking through this  one, 
  237. the goal "writename(1)" will have two solutions,  one that prints 
  238. "One" and one that prints "Out of range."
  239.  
  240. We want "writename" to be deterministic, that is, to give exactly 
  241. one  solution  for  any given set of  parameters,  and  not  give 
  242. alternative solutions upon backtracking.  We would therefore like 
  243. to  specify  somehow  that  if any of  the  first  three  clauses 
  244. succeeds,  the computer should not try the last clause.  This can 
  245. be done with the "cut" operator (written as an exclamation mark).
  246.  
  247. The  cut  operator  commits  the computer to  take  a  particular 
  248. solution  (or  potential solution) without  trying  alternatives. 
  249. Suppose for example that we have the following rule set:
  250.  
  251.     [14]        b :- c, d, !, e, f.
  252.                 b :- g, h.
  253.  
  254. and that the current goal is "b". If we take the first clause and 
  255. execute  the  cut,   then  it  becomes  impossible  to  look  for 
  256. alternative solutions to "c" and "d" (the goals that precede  the 
  257. cut  in  the  same clause) or to "b" (the goal that  invoked  the 
  258. clause containing the cut).  It remains possible,  of course,  to 
  259. backtrack  all the way past "b" and look for alternatives to  the 
  260. clause that caused "b" to be invoked.
  261.  
  262. What  we need to do in [13] is to put a cut in each of the  first 
  263. three clauses.  This changes their meaning slightly,  so that the 
  264. first  clause (for example) says,  "If the parameter is  1,  then 
  265. write 'One' and do not try any other clauses."
  266.  
  267.     [15]        writename(1) :- !, write("One").
  268.                 writename(2) :- !, write("Two").
  269.                 writename(3) :- !, write("Three").
  270.                 writename(_) :- write("Out of range").
  271.  
  272. Since  "write" is deterministic,  it does not matter whether  the 
  273. cut  is  written before or after the call  to  "write".  However, 
  274. programs  are usually more readable if cuts are made as early  as 
  275. possible.
  276.  
  277. Making procedure calls always succeed or always fail:
  278.  
  279. In  order to control the flow of program execution,  it is  often 
  280. necessary to guarantee that a goal will always succeed regardless 
  281. of the results of the computation that it performs. Occasionally, 
  282. it is necessary to guarantee that a goal will always fail.
  283.  
  284. An easy way to make any procedure succeed is to add an additional 
  285. clause  to  it that succeeds with any parameters,  and  is  tried 
  286. last, thus:
  287.  
  288.     [16]        f(X,Y) :- X < Y, !, write("X less than Y").
  289.                 f(_,_).
  290.  
  291. A  call  to "f" succeeds with any parameters;  it may or may  not 
  292. print its message,  but it will certainly not return failure  and 
  293. hence  will not cause backtracking in the procedure that  invoked 
  294. it.  Moreover,  because of the cut, "f" is deterministic. The cut 
  295. prevents  the second clause from being used to generate a  second 
  296. solution  with  parameters that have already succeeded  with  the 
  297. first clause.
  298.  
  299. Similarly,  a  procedure can be guaranteed to fail by adding  cut 
  300. and fail at the end of each of its definitions, thus:
  301.  
  302.     [17]        g(X,Y) :- X<Y, write("X less than Y"), !, fail.
  303.                 g(X,Y) :- Y<X, write("Y less than X"), !, fail.
  304.  
  305. Any  call  to  "g" ultimately returns failure for either  of  two 
  306. reasons:  either  it  doesn't match any of  the  clauses,  or  it 
  307. matches one of the clauses and ends with cut and fail. The cut is 
  308. written  next to last so that it won't be executed unless all the 
  309. other  steps of the clause have succeeded;  as a  result,  it  is 
  310. still possible to backtrack from one clause of "g" to another.
  311.  
  312. In full Prolog, but not in Turbo Prolog, we can define procedures 
  313. "make_succeed"  and "make_fail" that take a goal as a  parameter, 
  314. thus:
  315.  
  316.     [18]        make_succeed(Goal) :- call(Goal), !.
  317.                 make_succeed(_).
  318.  
  319.                 make_fail(Goal) :- call(Goal), !, fail.
  320.  
  321. In some implementations, "call(Goal)" is written simply as
  322. "Goal".
  323.  
  324. Likewise,  in  full Prolog but not in Turbo Prolog we can  define 
  325. the procedure "once" which allows a goal to succeed exactly once, 
  326. thus making any goal deterministic:
  327.  
  328.     [19]        once(Goal) :- call(Goal), !.
  329.  
  330. This  procedure  backtracks  as  much as  necessary  to  get  one 
  331. successful solution to "Goal",  then stops.  Thus,  no matter how 
  332. many   possible   solutions  there  are  to  "f(p)",   the   goal 
  333. "once(f(p))" will return only the first solution.  If "f(p)"  has 
  334. no solutions, "once(f(p))" fails.
  335.  
  336. Repetition through backtracking:
  337.  
  338. Prolog  offers  two  ways to perform  computations  repetitively: 
  339. backtracking and recursion.  Of the two,  recursion is by far the 
  340. more  versatile.  However,  there are some interesting  uses  for 
  341. backtracking, among them the construction of "repeat-fail" loops. 
  342. In  Prolog  implementations that lack tail recursion  elimination 
  343. (see below),  "repeat-fail" looping is the only kind of iteration 
  344. that  can  be  performed ad infinitum  without  causing  a  stack 
  345. overflow.
  346.  
  347. The predicate "repeat" is built into some Prolog implementations. 
  348. In most others, it can be defined as follows:
  349.  
  350.     [20]             repeat.
  351.                      repeat :- repeat.
  352.  
  353. (The  built-in  version of "repeat" should be used if  available, 
  354. since there are a few implementations in which the definition  in 
  355. [20] does not prevent stack overflow.)
  356.  
  357. "Repeat" always succeeds and has an infinite number of solutions. 
  358. Thus  any  procedure call bracketed between "repeat"  and  "fail" 
  359. will be tried over and over again,  even if it only generates one 
  360. solution.  For instance,  the following goal displays an infinite 
  361. number of asterisks:
  362.  
  363.     [21]             repeat, write("*"), fail.
  364.  
  365. The  following  Turbo Prolog procedure turns the computer into  a 
  366. typewriter, accepting characters from the keyboard and displaying 
  367. them  ad  infinitum,  until the user hits "Break"  to  abort  the 
  368. program:
  369.  
  370.     [22]             typewriter :- repeat,
  371.                                    readchar(C),
  372.                                    write(C),
  373.                                    fail.
  374.  
  375. The  loop  can  be made to terminate by allowing  it  to  succeed 
  376. eventually,  so that backtracking stops. The following version of 
  377. "typewriter"  stops when the user types a carriage return  (ASCII 
  378. code 13):
  379.  
  380.     [23]             typewriter :- repeat,
  381.                                    readchar(C),
  382.                                    write(C),
  383.                                    C = '\13'.
  384.  
  385. If  C  is  equal to code  13,  execution  terminates;  otherwise, 
  386. execution  backtracks  to  "repeat" and  proceeds  forward  again 
  387. through "readchar(C)" and "write(C)".
  388.  
  389. The  looping  in  [23]  can be restarted  by  the  failure  of  a 
  390. subsequent  goal (as in the compound goal "typewriter,fail").  To 
  391. prevent the loop from restarting unexpectedly,  we need to add  a 
  392. cut as follows:
  393.  
  394.     [24]             typewriter :- repeat,
  395.                                    readchar(C),
  396.                                    write(C),
  397.                                    C = '\13',
  398.                                    !.
  399.  
  400. In  effect,  this  forbids looking for alternative  solutions  to 
  401. "typewriter."
  402.  
  403. There  is  a  crucial difference between "repeat-fail"  loops  in 
  404. Prolog and repeat-until loops in Pascal.  In Pascal, iteration is 
  405. always accomplished by executing all the statements in the  loop, 
  406. then  jumping  from  the end back to the  beginning.  In  Prolog, 
  407. backtracking may cause control to jump backward from any goal  to 
  408. any  earlier goal that has alternative solutions.  (The  limiting 
  409. case  is  "repeat",  which  always  has  alternative  solutions.) 
  410. Moreover,  if  any goal in a Prolog loop fails,  subsequent goals 
  411. will not be attempted.
  412.  
  413. A  serious limitation of "repeat-fail" loops is that there is  no 
  414. convenient  way  to pass information from one  iteration  to  the 
  415. next. Prolog variables lose their values upon backtracking. Thus, 
  416. there  is  no easy way to make a "repeat-fail" loop accumulate  a 
  417. count  or total.  (Information can be preserved by storing it  in 
  418. the  knowledge base,  using "assert" and "retract",  but this  is 
  419. usually  a  slow process.) With  recursion,  information  can  be 
  420. transmitted from one pass to the next through the parameter list. 
  421. This  is  the main reason for preferring recursion as  a  looping 
  422. mechanism.
  423.  
  424. Recursion:
  425.  
  426. Most  programmers  are  familiar  with recursion as  a  means  of 
  427. implementing some very specialized, task-within-a-task algorithms 
  428. such as tree searching and Quicksort. Indeed, Prolog lends itself 
  429. well to expressing recursive algorithms developed in  Lisp.  What 
  430. is  not generally appreciated is that any iterative algorithm can 
  431. be expressed recursively.
  432.  
  433. Here is the classic recursive algorithm for computing factorials, 
  434. expressed in Pascal and in Turbo Prolog.  (Change "=" to "is"  to 
  435. get standard Prolog.)
  436.  
  437.     [25]        function factorial(N:integer):integer;
  438.                 begin
  439.                   if N=0 then
  440.                     factorial:=1
  441.                   else
  442.                     factorial:=N*factorial(N-1);
  443.                 end;
  444.  
  445.     [26]        factorial(0,1).
  446.  
  447.  
  448.                 factorial(N,FactN) :- N > 0,
  449.                                       M = N-1,
  450.                                       factorial(M,FactM),
  451.                                       FactN = N*FactM.
  452.  
  453. This  is straightforward;  the procedure "factorial" calls itself 
  454. to compute the factorial of the next smaller integer,  then  uses 
  455. the result to compute the factorial of the integer in question.
  456.  
  457. Now consider an iterative algorithm to do the same thing:
  458.  
  459.     [27]        function factorial(N:integer):integer;
  460.                 var I,J:integer;
  461.                 begin
  462.                   I:=0;           /* Initialize */
  463.                   J:=1;
  464.                   while I<N do
  465.                     begin         /* Loop */
  466.                       I:=I+1;
  467.                       J:=J*I
  468.                     end;
  469.                   factorial:=J        /* Return result */
  470.                 end;
  471.  
  472. In  Pascal,  this  procedure  does not call  itself.  Its  Prolog 
  473. counterpart  is  a procedure that calls itself as its  very  last 
  474. step -- a procedure that is said to be tail recursive:
  475.  
  476.     [28]        factorial(N,FactN) :- fact_iter(N,FactN,0,1).
  477.  
  478.                 fact_iter(N,FactN,N,FactN).
  479.  
  480.                 fact_iter(N,FactN,I,J) :-
  481.                                  I < N,
  482.                                  NewI = I+1,
  483.                                  NewJ = J*NewI,
  484.                                  fact_iter(N,FactN,NewI,NewJ).
  485.  
  486. Here  the  third and fourth parameters of "fact_iter"  are  state 
  487. variables that pass values from one iteration to the next.  State 
  488. variables  in  Prolog correspond to variables that  change  their 
  489. values repeatedly in Pascal.
  490.  
  491. Let's  start  by examining the recursive clause  of  "fact_iter". 
  492. This  clause  checks that I is still less than  N,  computes  new 
  493. values  for  I  and  J,  and finally calls itself  with  the  new 
  494. parameters.
  495.  
  496. The recursive call is the very last step in this clause,  and  in 
  497. addition, this whole clause is placed last so that, when it calls 
  498. itself,  there will be no untried alternatives to be saved on the 
  499. stack.  This  ensures  that  the stack will not grow  during  the 
  500. iteration. (We will return to this point below.)
  501.  
  502. Because  Prolog  variables  cannot  change  their   values,   the 
  503. additional  variables  NewI and NewJ have to  be  introduced.  In 
  504. Prolog (as in arithmetic, but not in most programming languages), 
  505. the statement
  506.  
  507.                      X = X+1
  508.  
  509. is  always false.  So NewI and NewJ contain the values that  will 
  510. replace I and J in the next iteration.
  511.  
  512. The first clause of "fact_iter" serves to end the iteration  when 
  513. the state variables reach their final values.  A more Pascal-like 
  514. but less efficient way of writing this clause would be: 
  515.  
  516.     [29]             fact(N,FactN,I,J) :- I = N, FactN = J.
  517.  
  518. That is,  if I is equal to N,  then FactN (so far uninstantiated) 
  519. should be given the value of J.  By writing this same clause more 
  520. concisely in [28], we make Prolog's unification mechanism do work 
  521. that   would  require  explicit  computational  steps  in   other 
  522. languages.
  523.  
  524. Most iterative algorithms can be expressed in Prolog by following 
  525. this  pattern  shown here.  First transform other types of  loops 
  526. (e.g.,  for and repeat-until) into Pascal while loops. Then break 
  527. the computation into three stages:  the initialization,  the loop 
  528. itself, and any subsequent computation needed to return a result.
  529.  
  530. Express  the  loop as a tail recursive clause  (like  the  second 
  531. clause of "fact_iter") with the while-condition at the beginning. 
  532. Computations to be performed after the loop terminates are placed 
  533. into another,  non-recursive, clause of the same procedure, which 
  534. is set up so that it executes only after the loop is finished. 
  535.  
  536. Finally,  hide  the  whole thing behind a  "front-end"  procedure 
  537. ("factorial"  in  this  example) which is what the  rest  of  the 
  538. program  actually  calls.  The  front-end  procedure  passes  its 
  539. parameters  into the tail recursive procedure along with  initial 
  540. values  of the state variables.  The art of expressing  iteration 
  541. through tail recursion is dealt with extensively, in Lisp, in the 
  542. first chapter of Abelson and Sussman (1985).
  543.  
  544. Why tail recursion is special:
  545.  
  546. Whenever one Prolog procedure calls another, two things are saved 
  547. on  a  pushdown stack:  (1) a record of what remains to  be  done 
  548. after return (the continuation of the calling procedure), and (2) 
  549. a  record  of what alternative solutions remain to be tried  (the 
  550. alternative set).  Normally, the continuation and the alternative 
  551. set  are  represented  by pointers  into  already  existing  data 
  552. structures,  so that it does not take any more space to represent 
  553. a large set than a small one.
  554.  
  555. Since every procedure call places information onto the stack,  it 
  556. would  seem  that  recursion  would  lead  inevitably  to   stack 
  557. overflow.  However,  most Prolog implementations (including Turbo 
  558. Prolog)  recognize  a special case:  if the continuation and  the 
  559. alternative  list are both empty,  nothing need be placed on  the 
  560. stack.  In this case, instead of calling itself, such a procedure 
  561. can simply place new values into its parameters and jump back  to 
  562. the  beginning of itself.  In effect,  this transforms  recursion 
  563. into iteration. 
  564.  
  565. A  procedure  that  calls itself with an empty  continuation  and 
  566. empty  alternative  list  is described  as  tail  recursive;  the 
  567. process  of  executing such a call without adding  items  to  the 
  568. stack  is called tail recursion elimination.  (The elimination of 
  569. tail recursion does not mean that it should be banished from  the 
  570. program; on the contrary, it should be used liberally because the 
  571. implementation   transforms   it  into  an  efficient   iterative 
  572. process.)
  573.  
  574. A  quick  way to verify that a particular  Prolog  implementation 
  575. performs  tail  recursion  elimination is to  try  the  following 
  576. predicate:
  577.  
  578.     [30]             test(N) :- write(N),
  579.                                 nl,
  580.                                 M = N+1,
  581.                                 test(M).
  582.  
  583. (Again,  this  is Turbo Prolog syntax;  change "=" to "is" to get 
  584. standard Prolog.) Start with the goal "test(1)" and see how  long 
  585. execution  continues.  If  the program runs for more than  10,000 
  586. iterations,  it is a safe bet that tail recursion elimination  is 
  587. taking place.
  588.  
  589. Controlling the growth of the stack:
  590.  
  591. It  is  easy  to  recognize a recursive call that  has  an  empty 
  592. continuation:  the recursive call is the very last subgoal in the 
  593. clause that contains it.  Determining whether the alternative set 
  594. is also empty takes somewhat more thought.
  595.  
  596. One  way to get an empty alternative set is to put the  recursive 
  597. call  in  the last clause of a predicate,  as  in  the  iterative 
  598. factorial program ([28], repeated here for convenience): 
  599.  
  600.     [31]        factorial(N,FactN) :- fact_iter(N,FactN,0,1).
  601.  
  602.                 fact_iter(N,FactN,N,FactN).
  603.  
  604.                 fact_iter(N,FactN,I,J) :-
  605.                                  I < N,
  606.                                  NewI = I+1,
  607.                                  NewJ = J*NewI,
  608.                                  fact_iter(N,FactN,NewI,NewJ).
  609.  
  610. The  recursive call only takes place when all other  alternatives 
  611. have been exhausted.
  612.  
  613. The  alternative set can also be made empty by using cut to  rule 
  614. out   alternatives,   as   in  the  following   replacement   for 
  615. "fact_iter":
  616.  
  617.     [32]        fact_iter(N,FactN,I,J) :-
  618.                                  I < N,
  619.                                  NewI = I+1,
  620.                                  NewJ = J*NewI,
  621.                                  !,
  622.                                  fact_iter(N,FactN,NewI,NewJ).
  623.  
  624.                 fact_iter(N,FactN,N,FactN).
  625.  
  626. Here  the recursive call occurs in the first clause,  but the cut 
  627. guarantees  that the second clause need not be considered  as  an 
  628. alternative.
  629.  
  630. cut can equally well be placed before them, as follows:
  631.  
  632.     [33]        fact_iter(N,FactN,I,J) :-
  633.                                  I < N,
  634.                                  !,
  635.                                  NewI = I+1,
  636.                                  NewJ = J*NewI,
  637.                                  fact_iter(N,FactN,NewI,NewJ).
  638.  
  639.                 fact_iter(N,FactN,N,FactN).
  640.  
  641. Note that [32] and [33] ought to be more efficient than [31]. The 
  642. first  clause of [31] succeeds only on the  last  iteration;  the 
  643. rest of the time,  the first clause fails and control proceeds to 
  644. the  second clause.  Thus,  almost every iteration must try  both 
  645. clauses.  In [32] and [33], the order of the clauses is reversed, 
  646. so  that  the  first clause is the one that  will  almost  always 
  647. succeed. Moreover, this clause contains a cut so that whenever it 
  648. succeeds,  no other clause will be tried.  The result is that, in 
  649. [32]  and  [33],  only one clause -- the last one -- is tried  on 
  650. every iteration except the last.
  651.  
  652. In actual tests using Turbo Prolog on an IBM PC,  the times taken 
  653. to  compute  the factorial of 200 were 0.58 second for  [31]  and 
  654. 0.54 second for [32] and [33].  The gain in efficiency was  small 
  655. because  the time saved by omitting the unnecessary test was only 
  656. slightly  greater than the time needed to execute the extra  cut. 
  657. The  gain would have been much larger if the  unnecessary  clause 
  658. had contained time-consuming computations.
  659.  
  660. References:
  661.  
  662. Abelson,  Harold,  and  Sussman,  Gerald Jay (1985) Structure and 
  663. Interpretation of Computer  Programs.  Cambridge,  Massachusetts: 
  664. MIT Press.
  665.  
  666. Clocksin, W. F., and Mellish, C. S. (1984) Programming in Prolog. 
  667. Second edition. Berlin: Springer-Verlag.
  668.  
  669. Dijkstra,  E.  W.  (1975)  Guarded commands,  nondeterminacy  and 
  670. formal   derivation  of  programs.   Communications  of  the  ACM 
  671. 18.8:453-457.
  672.  
  673. Kluzniak,  Feliks,  and Szpakowicz,  Stanislaw (1985) Prolog  for 
  674. Programmers. London: Academic Press.
  675.  
  676. Turbo  Prolog  (1986) Version  1.0.  Scotts  Valley,  California: 
  677. Borland International.