RU beehive logo ITEC dept promo banner
ITEC 380
2009fall
ibarland

homeinfolecturesexamshwsarchive

lect12b
sorting in prolog
and, review N2 soln

- Review N2.ss soln:
  - First we add ifNeg;
    eval and toString are straight copies;
    note the trick used in parsing if0 and ifNegs:
      ((if ... make-is0-expr make-ifNeg-expr) e1 e2 e3)
    for parse we do a bit of thinking.
  - [q: should I have included a 'parse-expect!'?]

  - mod straightforward, though I tweaked the test-case.
    (Too bad check-within can only be used at top-level.)

  - Note how id's are represented by strings, but
    for abstraction we define id?, id=?
  - subst: follows the data def'n
  - Note that for checking subst: 
    it's annoying check-expect must be at top-level.
    I used an editor/macro, to help make test cases based on eval test cases.



Finish up prolog (really!)

  1  %%%%%%%%%%%%%%
  2  % Sorting:
  3  % We'll just provide the 'spec':
  4  % L2 is a sorted version of L1 if:
  5  %  - L1 is a permutation (re-arrangement) of L2, and
  6  %  - L2 is in ascending order (er, non-decreasing).
  7  
  8  scrapSort(L1,L2) :- permutation(L1,L2), nondecreasing(L2).
  9   
 10  % Here're the helper functions:
 11  
 12  nondecreasing([]).
 13  nondecreasing([_]).
 14  nondecreasing([F,S|R]) :- F=<S, nondecreasing([S|R]).
 15  
 16  
 17  badPerm1([],[]).
 18  badPerm1([F|R],Y) :- append2(Y1,[F|Y2],Y), 
 19                       append2(Y1,Y2,PY), 
 20                       badPerm1(R,PY).
 21  % This is a correct *specification* of permutation.
 22  % However, it has several drawbacks:
 23  % (a) written non-intuitively,
 24  % (b) using 'append' is a sledgehammer (poor performance too),
 25  % (c) it only half-works, in Prolog:
 26  %    badPerm1([3,4,5],[5,3,4]) is true
 27  % but
 28  %    badPerm1([3,4,5],X) gives only *one* answer
 29  %    and then loops forever.
 30  % However, it works 'in reverse':
 31  %    badPerm1(X,[3,4,5]) gives all solutions.
 32  
 33  badPerm2([],[]).
 34  badPerm2([F|R],Y) :- remove(F,Y,Z), badPerm2(Z,R).
 35  %
 36  % This is also a correct spec,
 37  % and it fixes drawbacks (a) and (b), 
 38  % but still suffers from (c).
 39  % In fact, it's worse: badPerm2 no longer
 40  % works in reverse -- badPerm2(X,[3,4,5]) gives
 41  % *two* (?!) solutions, and then backtracks
 42  % into la-la land.
 43  
 44  % Here's a fully-working version:
 45  % like badPerm2 except that it destructures the *return* value.
 46  %
 47  permutation([],[]).
 48  permutation(X,[F|R]) :- remove(F,X,Z), permutation(Z,R).
 49  
 50  remove(F,[F|L1],L1).
 51  remove(F,[G|L1],[G|L2]) :- remove(F,L1,L2).
 52  
 53  
 54  
 55  %% Now, back to sorting...
 56  % Reflecting on scrapSort:
 57  %   Consider the *order* of solutions
 58  %   permutation([3,4,5],X) and permutation([5,4,3],X).
 59  % How many permutations does scrapSort([3,4,5],X) look at?
 60  % How about scrapSort ([5,4,3],X) look at?
 61  %
 62  % Now, consider this problem amplified:
 63  %     scrapSort([8,7,6,5,4,3,2,1],X) runs okay. But
 64  % But scrapSort([10,9,8,7,6,5,4,3,2,1],X) takes a while.
 65  %
 66  % The name "scrapSort" was chosen because its performance
 67  % is a piece of scrap: O(n!).
 68  
 69  
 70  
 71  
 72  
 73  
 74  
 75  % Ironically, 'quicksort' is more straightforward than 'scrapsort',
 76  % even though we specify *how* to sort rather than *what sorted means*.
 77  %
 78  
 79  
 80  %  QuickSort
 81  %  Author: Varesano Fabio
 82  %  http://www.varesano.net/blog/fabio/quick+sort+implemented+prolog
 83  
 84  /* [+,-] */
 85  quicksort([], []).
 86  quicksort([HEAD | TAIL], SORTED) :- partition(HEAD, TAIL, LEFT, RIGHT),
 87                                      quicksort(LEFT, SORTEDL),
 88                                      quicksort(RIGHT, SORTEDR),
 89                                      append(SORTEDL, [HEAD | SORTEDR], SORTED).
 90  
 91  /* [+,+,-,-] */
 92  partition(PIVOT, [], [], []).
 93  partition(PIVOT, [HEAD | TAIL], [HEAD | LEFT], RIGHT) :- HEAD @=< PIVOT,
 94                                                           partition(PIVOT, TAIL, LEFT, RIGHT).
 95  partition(PIVOT, [HEAD | TAIL], LEFT, [HEAD | RIGHT]) :- HEAD @> PIVOT,
 96                                                           partition(PIVOT, TAIL, LEFT, RIGHT).
 97  
 98  /* [+,+,-] */
 99  append([], LIST, LIST).
100  append([HEAD | LIST1], LIST2, [HEAD | LIST3]) :- append(LIST1, LIST2, LIST3).
101  
102  
103  
104  
105  
106  
107  
108  % Here is a another, more verbose version
109  % (the version I started writing myself ... 
110  % Note that I'm used to partitioning into three sections,
111  % so that we always *reduce* the size of a partition
112  % (even if our pivot is the maximum value).
113  % Study the above sol'n, to see how the Prolog
114  % makes sure we always recur on smaller lists (even
115  % when the pivot is the maximum value *and* when
116  % it's the minimum value).
117  
118  qsort([],[]).
119  qsort([F|R],L2) :- partition([F|R],F,Sm,Eq,Lr), 
120                     qsort(Sm,SmSorted), 
121  		   qsort(Lr,LrSorted), 
122  	           append3(SmSorted,Eq,LrSorted,L2).
123  
124  partition([],_,[],[],[]).
125  partition([F|R],Pivot,Smaller,Equal,Larger) :- 
126    F<Pivot,
127    Smaller = [F|SmallerRest],
128    partition(R,Pivot,SmallerRest,Equal,Larger).
129  partition([F|R],Pivot,Smaller,Equal,Larger) :- 
130    F=Pivot, 
131    Equal = [F|EqualRest],
132    partition(R,Pivot,Smaller,EqualRest,Larger).
133  partition([F|R],Pivot,Smaller,Equal,Larger) :- 
134    F>Pivot, 
135    Larger = [F|LargerRest],
136    partition(R,Pivot,Smaller,Equal,LargerRest).
137  
138  
139  append3(L1,L2,L3,All) :- append2(L1,L2,L1and2), append2(L1and2,L3,All).
140  append2([],L2,L2).
141  append2([F|R],L2,[F|AllRest]) :- append2(R,L2,AllRest).
142  
143  
144  
145  
146  /*****************
147   Reflections/thoughts on prolog:
148    * The idea of declarative programming is cool:
149      Just by specifying what *constitutes* a correct answer,
150      you can write a program (and often it's efficient).
151    + Sometimes though, it's too inefficient (scrapSort).
152    + Worse though: to really write Prolog, you need to
153      thoroughly understand *how* it implements searching
154      (badPerm).
155  
156    + Seeing how different 'cultures' compute is mind-expanding;
157    * Pattern matching: Way cool!  
158      This is something *any* language can use
159      (ML, Haskell; Scheme has a 'match' library:
160      http://www.radford.edu/itec380/2009fall-ibarland/Lectures/matching.ss
161      
162    * The idea of specifying correctness of an answer
163      (separately from an algorithm)
164      makes me a better [Java, whatever] programmer:
165      often having a clean spec of correctness
166      leads directly to clean, correct code.
167   *****************/

homeinfolecturesexamshwsarchive


©2009, Ian Barland, Radford University
Last modified 2009.Nov.18 (Wed)
Please mail any suggestions
(incl. typos, broken links)
to iba�rlandrad�ford.edu
Powered by PLT Scheme