University of Toronto Department of Computer Science CSC324 - Principles of Programming Languages September 1991 John Mylopoulos Programming in Lisp: Tutorial Notes II Prepared by Todd Kelley ;Here are three different implementations of bubble sort on integers. ; They are ; sort1 - functional implementation of bubble sort ; sort2 - fuctnional and tail-recursive implementation ; of bubble sort ; bad-sort - imperative (and destructive) implementation of ; of bubble sort, with side effects ; ;They are designed to illustrate the differences between functional ;programming and imperative programming. Also, the first two implementations ;illustrate the difference between non-tail-recursive functions and tail- ;recursive functions. ; ; *********IMPORTANT********** ; You will notice that all three of these functions are well documented. ; Your own code should also be documented this well. ; **************************** ; ; Read this code, then load it and try evaluating the following forms: ; >(setq my-list '(3 2 6 2 1)) ; >my-list ; >(sort2 my-list) ; ; what does this evaluate to? ; >my-list ; ; what does THIS evaluate to? ; >(bad-sort my-list) ; ; what does this evaluate to? ; >my-list ; ; ACK!! Where did the list go? ; ; Will an imperative implementation always have ; ; side effects? ; ; ; Very brave persons might try the following forms: ; >(setq temp '(3 2 6 2 1)) ; >(bad-sort temp) ; >temp ; ;What happened? Why didn't it happen when we evaluated (bad-sort my-list)? ; ;;;;;;;;;;;;;;;;;;; ;; FUNCTION ;; sort1 -- sort a list of integers in non-decreasing order using bubble sort ;; ;; USAGE ;; (sort1 list-of-int) ;; ;; SIDE EFFECTS ;; none ;; ;; DESCRIPTION ;; Returns a list of integers sorted in non-decreasing order. ;; The argument, list-of-int, must be a list of integers. ;; (Note -- this function is implemented using the functional paradigm) ;; ;; ALGORITHM ;; If the list is empty ;; return it (it's already sorted) ;; else if the list has only one element ;; return it (it's already sorted) ;; else if the first element is less than the second ;; recursively sort the cdr of the list (a smaller list) ;; attach the first element to the sorted cdr ;; (``check-done'' begins here) ;; if the first element of this list is still less than the second ;; return it (it's sorted) ;; else ;; (it is ``more sorted'' now) ;; recursively sort it again ;; else ;; swap the first two elements ;; recursively sort the cdr of this (a smaller list) ;; attach the first element (after the swap) to the sorted cdr ;; (``check-done'' begins here) ;; if the first element of this list is less than the second ;; return it (it's sorted) ;; else ;; (it is ``more sorted'' now) ;; recursively sort it again ;; ;; EXAMPLES ;; (sort1 '(3 2 1)) => (1 2 3) ;; (sort1 '(2 1 2 3)) => (1 2 2 3) ;;;;;;;;;;;;;;;;;; (defun sort1 (list-of-int) (cond ((null list-of-int) nil) ((= (length list-of-int) 1) list-of-int) ((<= (car list-of-int) (cadr list-of-int)) (check-done (cons (car list-of-int) (sort1 (cdr list-of-int))))) (t (check-done (cons (cadr list-of-int) (sort1 (cons (car list-of-int) (cddr ; list-of-int)))))))) ; a helper function for sort1 (defun check-done (list-of-int) (if (<= (car list-of-int) (cadr list-of-int)) list-of-int (sort1 list-of-int))) ;;;;;;;;;;;;;;;;;;; ;; FUNCTION ;; sort2 -- sort a list of integers in non-decreasing order using bubble sort ;; ;; USAGE ;; (sort2 list-of-int) ;; ;; SIDE EFFECTS ;; none ;; ;; DESCRIPTION ;; Returns a list of integers sorted in non-decreasing order. ;; The argument, list-of-int, must be a list of integers. ;; (Note -- this function is implemented using the functional paradigm ;; AND it is tail-recursive.) ;; ;; ALGORITHM ;; In a bubble sort, we repeatedly traverse the list. Here we ;; maintain 3 variables: ;; 1. list-of-int : the part of the list traversed so far, initially nil ;; 2. current-list : the part of the list we have yet to traverse ;; 3. done-now : a boolean to indicate if no swaps were performed ;; : during the traversal, initially t (no swaps) ;; We are finished when done-now is t at the end of a traversal. ;; ;; loop ;; if current-list is empty ;; if done-now ;; return list-of-int ;; else ;(begin a new traversal) ;; current-list <- list-of-int ;; list-of-int <- nil ;; done-now <- t ;; else if current-list has only one element ;(begin a new traversal) ;; list-of-int <- (append list-of-int current-list) ;; current-list<- nil ;; done-now is unchanged ;; else if the first element of current-list is less than the second ;; ;add the first element of current-list to the end of list-of-int ;; list-of-int <- (append list-of-int (car current-list)) ;; current-list <- (cdr current-list) ;; done-now is unchanged ;; else ;; ;perform a swap ;; ;add the second element of current-list to the end of list-of-int ;; list-of-int <- (append list-of-int (cadr current-list)) ;; current-list <- (cons (car current-list) (cddr current-list)) ;; done-now <- nil ;; endloop ;; ;; EXAMPLES ;; (sort2 '(3 2 1)) => (1 2 3) ;; (sort2 '(2 1 2 3)) => (1 2 2 3) ;;;;;;;;;;;;;;;;;; (defun sort2 (list-of-int) (do-sort nil list-of-int t)) ;a helping function for sort2 (defun do-sort (list-of-int current-list done-now) (cond ((null current-list) (if done-now list-of-int (do-sort nil list-of-int t))) ((null (cdr current-list)) (do-sort (append list-of-int current-list) nil done-now)) ((<= (car current-list) (cadr current-list)) (do-sort (append list-of-int (list (car current-list))) (cdr current-list) done-now)) (t (do-sort (append list-of-int (list (cadr current-list))) (cons (car current-list) (cddr current-list)) nil)))) ;;;;;;;;;;;;;;;;;;; ;; FUNCTION ;; bad-sort -- sort a list of integers in non-decreasing order (destructive) ;; using bubble sort ;; ;; USAGE ;; (bad-sort list-of-int) ;; ;; SIDE EFFECTS ;; The argument, list-of-int, may be destroyed (by permuting elements ;; in place). ;; The following symbols are bound to values, with any previous binding ;; destroyed: ;; done current-list temp ;; ;; DESCRIPTION ;; Returns a list of integers sorted in non-decreasing order. ;; The argument, list-of-int, must be a list of integers. ;; (Note -- this function is implemented using the imperative paradigm. ;; It is an example of the type of programming that is frowned ;; upon in LISP.) ;; ;; ALGORITHM ;; The algorithm is basically the same as for sort2 above, but ;; since the implementation of the algorithm is not functional, ;; unexpected results are possible. ;; ;; EXAMPLES ;; (bad-sort '(3 2 1)) => (1 2 3) ;; (bad-sort '(2 1 2 3)) => (1 2 2 3) ;;;;;;;;;;;;;;;;;; (defun bad-sort (list-of-int) (loop (setq done t) (setq current-list list-of-int) (loop (cond ((null current-list) (return)) ((= (length current-list) 1) (return)) ((> (car current-list) (cadr current-list)) ;the following 3 lines exchange the first and second elements (setq temp (car current-list)) (rplaca current-list (cadr current-list)) (rplaca (cdr current-list) temp) (setq done nil))) (setq current-list (cdr current-list))) (if done (return))) list-of-int)