More progress. Got up to 2.65
This commit is contained in:
		
							
								
								
									
										112
									
								
								ex-2-59.lisp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								ex-2-59.lisp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
 | 
			
		||||
;; not gonna bother copy-pasting the rest of the implementation.
 | 
			
		||||
;; ex 2.59
 | 
			
		||||
(defun adjoin-set (s i)
 | 
			
		||||
  (if (element-of-set? s i)
 | 
			
		||||
      s
 | 
			
		||||
      (cons i s)))
 | 
			
		||||
(defun union-set (s1 s2)
 | 
			
		||||
  (labels ((rec (s1 s2)
 | 
			
		||||
	     (if (null s1)
 | 
			
		||||
		 s2
 | 
			
		||||
		 (rec (cdr s1) (adjoin-set s2 (car s1))))))
 | 
			
		||||
    (rec s1 s2)))
 | 
			
		||||
 | 
			
		||||
;; ex 2.60
 | 
			
		||||
;; element-of-set? doesn't change I'm pretty sure.
 | 
			
		||||
(defun element-of-set? (s e)
 | 
			
		||||
  (if (null s)
 | 
			
		||||
      nil
 | 
			
		||||
      (element-of-set? (cdr s) e)))
 | 
			
		||||
;; adjoin becomes more efficient, as it is now an O(1) operation
 | 
			
		||||
(defun adjoin-set-duppy (s e)
 | 
			
		||||
  (cons e s))
 | 
			
		||||
;; union-set becomes a linear-time algorithm as it relies on adjoin-set.
 | 
			
		||||
;; doesn't really change in logic otherwise, though.x
 | 
			
		||||
(defun union-set-duppy (s1 s2)
 | 
			
		||||
  (labels ((rec (s1 s2)
 | 
			
		||||
	     (if (null s1)
 | 
			
		||||
		 s2
 | 
			
		||||
		 (rec (cdr s1) (adjoin-set s2 (car s1))))))
 | 
			
		||||
    (rec s1 s2)))
 | 
			
		||||
 | 
			
		||||
;; ex 2.61
 | 
			
		||||
;; Note: NOT tail recursive. not gonna work for too long lists.
 | 
			
		||||
;; though the book's given intersection-list is also not tail
 | 
			
		||||
;; recursive, so this is probably intended.
 | 
			
		||||
(defun adjoin-set-ordered (s1 i)
 | 
			
		||||
  (cond
 | 
			
		||||
    ((null s1) (cons i nil))
 | 
			
		||||
    ((= i (car s1)) s1)
 | 
			
		||||
    ((< i (car s1))
 | 
			
		||||
     (cons i s1))
 | 
			
		||||
    (t (cons (car s1) (adjoin-set-ordered (cdr s1) i)))))
 | 
			
		||||
 | 
			
		||||
;; ex 2.62
 | 
			
		||||
;; same with 2.61, not tail recursive
 | 
			
		||||
;; both could be translated fairly easily though
 | 
			
		||||
(defun union-set-ordered (s1 s2)
 | 
			
		||||
  (cond
 | 
			
		||||
    ((null s1) s2)
 | 
			
		||||
    ((null s2) s1)
 | 
			
		||||
    ((= (car s1) (car s2))
 | 
			
		||||
     (cons (car s1)
 | 
			
		||||
	   (union-set-ordered (cdr s1) (cdr s2))))
 | 
			
		||||
    ((> (car s1) (car s2))
 | 
			
		||||
     (cons (car s2)
 | 
			
		||||
	   (union-set-ordered s1 (cdr s2))))
 | 
			
		||||
    ((< (car s1) (car s2))
 | 
			
		||||
     (cons (car s1)
 | 
			
		||||
	   (union-set-ordered (cdr s1) s2)))
 | 
			
		||||
    (t (error "asd"))))
 | 
			
		||||
 | 
			
		||||
;; ex 2.63
 | 
			
		||||
;; a) it looks like the result should be the same.
 | 
			
		||||
;; tried the code, couldn't get them to give different results.
 | 
			
		||||
;; on the figure 2.16 trees, they all give the same results.
 | 
			
		||||
;; b) At first glance they might look equivalent. However,
 | 
			
		||||
;; the first algorithm has a hidden cost in the append operation.
 | 
			
		||||
;; because the fastest way to append two linked lists is an O(N)
 | 
			
		||||
;; operation: so eventually it becomes O(N/2 + 2N/4 + 4N/8 + ... + logN)
 | 
			
		||||
;; or something like that, which is O(N + logn) or O(N).
 | 
			
		||||
;; the second algorithm doesn't waste time with that, and is therefore
 | 
			
		||||
;; a flat O(logN). The second one takes less time.
 | 
			
		||||
 | 
			
		||||
;; damn, raw lisp code can be hard to read. might be a skill issue.
 | 
			
		||||
;; ex 2.64
 | 
			
		||||
;; "short" paragraph. yeah.
 | 
			
		||||
;; Okay, so...
 | 
			
		||||
;; partial-tree takes a list of elements and n.
 | 
			
		||||
;; in the base case, i.e. n = 0, the result is trivially just (cons nil elts)
 | 
			
		||||
;; otherwise, the function calculates the length of the left half of the tree,
 | 
			
		||||
;; recursively calls itself with that "left half". As partial-tree returns
 | 
			
		||||
;; the rest of the elements, we take the "right half" from this rest of the elements
 | 
			
		||||
;; (of course making sure to save and exclude the center element, as that is
 | 
			
		||||
;; the root of the tree).
 | 
			
		||||
;; This way, we create a tree by recursively creating the left subtree, then the right
 | 
			
		||||
;; subtree, then we simply combine these to create the final tree.
 | 
			
		||||
;; b) O(N) each element is visited exactly once.
 | 
			
		||||
 | 
			
		||||
;; ex 2.65
 | 
			
		||||
;; assuming the union and intersection don't have to maintain
 | 
			
		||||
;; balance (or that's handled by something else)
 | 
			
		||||
(defstruct btree elt left right)
 | 
			
		||||
(defun adjoin-set-tree (s i)
 | 
			
		||||
  (cond
 | 
			
		||||
    ((null s) (make-btree :elt i :left nil :right nil))
 | 
			
		||||
    ((= (btree-elt s) i) s)
 | 
			
		||||
    ((< (btree-elt s) i)
 | 
			
		||||
     (make-btree :elt (btree-elt s)
 | 
			
		||||
		 :left (btree-left s)
 | 
			
		||||
		 :right (adjoin-set-tree (btree-right s) i)))
 | 
			
		||||
    ((> (btree-elt s) i)
 | 
			
		||||
     (make-btree :elt (btree-elt s)
 | 
			
		||||
		 :left (adjoin-set-tree (btree-left s) i)
 | 
			
		||||
		 :right (btree-right s)))))
 | 
			
		||||
(defun union-set-tree (s1 s2)
 | 
			
		||||
  (cond
 | 
			
		||||
    ((null s1) s2)
 | 
			
		||||
    ((null s2) s1)
 | 
			
		||||
    (t (union-set-tree (union-set-tree (btree-left s1) (btree-right s2))
 | 
			
		||||
		       (adjoin-set-tree s2 (btree-elt s1))))))
 | 
			
		||||
;; yeah, okay. this looks good enough, didn't test it at all, but whatevs.
 | 
			
		||||
		Reference in New Issue
	
	Block a user