![]() |
![]() |
|
home—info—lectures—exams—archive
nth(0,[F|_],F). nth(I,[_|R],Answer) :- Iless1 is I-1, nth(Iless1,R,Answer). |
% Review: mem(X,[X|_]). mem(X,[_|R]) :- mem(X,R). % A version of 'reverse' which requires 'removeLast' and 'last': reverse([],[]). reverse([F|R],L2) :- last(L2,F), removeLast(L2,L2less), reverse(R,L2less). % removeLast, Last -- not included in notes, % since it's part of hw05 !! %% Alternate version: An accumulator variable: reverse(L1,L2) :- reverseHelp( L1, [], L2 ). reverseHelp([],Acc,Acc). reverseHelp([F|R],Acc,Out) :- reverseHelp(R,[F|Acc],Out). filterSmallerThan(Threshold,[],[]). filterSmallerThan(Threshold,[F|R],SmallerRest) :- F >= Threshold, filterSmallerThan(Threshold,R,SmallerRest). filterSmallerThan(Threshold,[F|R],[F|SmallerRest]) :- F < Threshold, filterSmallerThan(Threshold,R,SmallerRest). %%%%%%%%%%%%%% % Sorting: % We'll just provide the 'spec': % L2 is a sorted version of L1 if: % - L1 is a permutation (re-arrangement) of L2, and % - L2 is in ascending order (er, non-decreasing). scrapSort(L1,L2) :- permutation(L1,L2), nondecreasing(L2). % Here're the helper functions: nondecreasing([]). nondecreasing([_]). nondecreasing([F,S|R]) :- F=<S, nondecreasing([S|R]). badPerm1([],[]). badPerm1([F|R],Y) :- append2(Y1,[F|Y2],Y), append2(Y1,Y2,PY), badPerm1(R,PY). % This is a correct *specification* of permutation. % However, it has several drawbacks: % (a) written non-intuitively, % (b) using is a sledgehammer (poor performance too), % (c) it only half-works, in Prolog: % badPerm1([3,4,5],[5,3,4]) is true % but % badPerm1([3,4,5],X) gives only *one* answer % and then loops forever. % However, it works 'in reverse': % badPerm1(X,[3,4,5]) gives all solutions. badPerm2([],[]). badPerm2([F|R],Y) :- remove(F,Y,Z), badPerm2(Z,R). % % This is also a correct spec, % and it fixes drawbacks (a) and (b), % but still suffers from (c). % In fact, it's worse: badPerm2 no longer % works in reverse -- badPerm2(X,[3,4,5]) gives % *two* (?!) solutions, and then backtracks % into la-la land. % Here's a fully-working version: % like badPerm2 except that it destructures the *return* value. % permutation([],[]). permutation(X,[F|R]) :- remove(F,X,Z), permutation(Z,R). remove(F,[F|L1],L1). remove(F,[G|L1],[G|L2]) :- remove(F,L1,L2). % Reflecting on scrapSort: % Consider the *order* of solutions % permutation([3,4,5],X) and permutation([5,4,3],X). % How many permutations does scrapSort([3,4,5],X) look at? % How about scrapSort ([5,4,3],X) look at? % % Now, consider this problem amplified: % scrapSort([8,7,6,5,4,3,2,1],X) runs okay. But % But scrapSort([10,9,8,7,6,5,4,3,2,1],X) takes a while. % % The name "scrapSort" was chosen because its performance % is a piece of scrap: O(n!). % Ironically, 'quicksort' is more straightforward than 'scrapsort', % even though we specify *how* to sort rather than *what sorted means*. % % QuickSort % Author: Varesano Fabio % http://www.varesano.net/blog/fabio/quick+sort+implemented+prolog /* [+,-] */ quicksort([], []). quicksort([HEAD | TAIL], SORTED) :- partition(HEAD, TAIL, LEFT, RIGHT), quicksort(LEFT, SORTEDL), quicksort(RIGHT, SORTEDR), append(SORTEDL, [HEAD | SORTEDR], SORTED). /* [+,+,-,-] */ partition(PIVOT, [], [], []). partition(PIVOT, [HEAD | TAIL], [HEAD | LEFT], RIGHT) :- HEAD @=< PIVOT, partition(PIVOT, TAIL, LEFT, RIGHT). partition(PIVOT, [HEAD | TAIL], LEFT, [HEAD | RIGHT]) :- HEAD @> PIVOT, partition(PIVOT, TAIL, LEFT, RIGHT). /* [+,+,-] */ append([], LIST, LIST). append([HEAD | LIST1], LIST2, [HEAD | LIST3]) :- append(LIST1, LIST2, LIST3). % Here is a another, more verbose version % (the version I started writing myself ... % Note that I'm used to partitioning into three sections, % so that we always *reduce* the size of a partition % (even if our pivot is the maximum value). % Study the above sol'n, to see how the Prolog % makes sure we always recur on smaller lists (even % when the pivot is the maximum value). qsort([],[]). qsort([F|R],L2) :- partition([F|R],F,Sm,Eq,Lr), qsort(Sm,SmSorted), qsort(Lr,LrSorted), append3(SmSorted,Eq,LrSorted,L2). partition([],_,[],[],[]). partition([F|R],Pivot,Smaller,Equal,Larger) :- F<Pivot, Smaller = [F|SmallerRest], partition(R,Pivot,SmallerRest,Equal,Larger). partition([F|R],Pivot,Smaller,Equal,Larger) :- F=Pivot, Equal = [F|EqualRest], partition(R,Pivot,Smaller,EqualRest,Larger). partition([F|R],Pivot,Smaller,Equal,Larger]) :- F>Pivot, Larger = [F|LargerRest], partition(R,Pivot,Smaller,Equal,LargerRest). append3(L1,L2,L3,All) :- append2(L1,L2,L1and2), append2(L1and2,L3,All). append2([],L2,L2). append2([F|R],L2,[F|AllRest]) :- append2(R,L2,AllRest). /***************** Reflections/thoughts on prolog: * The idea of declarative programming is cool: Just by specifying what *constitutes* a correct answer, you can write a program (and often it's efficient). + Sometimes though, it's too inefficient (scrapSort). + Worse though: to really write Prolog, you need to thoroughly understand *how* it implements searching (badPerm). + Seeing how different 'cultures' compute is mind-expanding; * Pattern matching: Way cool! This is something *any* language can use (ML, Haskell; Scheme has a 'match' library). * The idea of specifying correctness of an answer (separately from an algorithm) makes me a better [Java, whatever] programmer: often having a clean spec of correctness leads directly to clean, correct code. *****************/ |
home—info—lectures—exams—archive
©2008, Ian Barland, Radford University Last modified 2008.Dec.01 (Mon) |
Please mail any suggestions (incl. typos, broken links) to ibarland ![]() |
![]() |