More recent additions, some exercises and huffman encoding chapter
This commit is contained in:
		@@ -108,5 +108,7 @@
 | 
				
			|||||||
    ((null s1) s2)
 | 
					    ((null s1) s2)
 | 
				
			||||||
    ((null s2) s1)
 | 
					    ((null s2) s1)
 | 
				
			||||||
    (t (union-set-tree (union-set-tree (btree-left s1) (btree-right s2))
 | 
					    (t (union-set-tree (union-set-tree (btree-left s1) (btree-right s2))
 | 
				
			||||||
		       (adjoin-set-tree s2 (btree-elt s1))))))
 | 
							  s     (adjoin-set-tree s2 (btree-elt s1))))))
 | 
				
			||||||
;; yeah, okay. this looks good enough, didn't test it at all, but whatevs.
 | 
					;; yeah, okay. this looks good enough, didn't test it at all, but whatevs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										142
									
								
								sec-2-3-4-huffman.lisp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								sec-2-3-4-huffman.lisp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					;; my code for the Huffman Encoding section
 | 
				
			||||||
 | 
					;; 2.3.4 of SICP.
 | 
				
			||||||
 | 
					;; at this point I've started grouping an entire
 | 
				
			||||||
 | 
					;; section together instead of doing exercises in
 | 
				
			||||||
 | 
					;; separate files, because I need to translate a good chunk
 | 
				
			||||||
 | 
					;; of code into common lisp anyway.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; this automatically defines:
 | 
				
			||||||
 | 
					;; make-leaf
 | 
				
			||||||
 | 
					;; leaf-p
 | 
				
			||||||
 | 
					;; leaf-symbol and leaf-weight
 | 
				
			||||||
 | 
					(defstruct leaf symbol weight)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; I decided to use the name trunk
 | 
				
			||||||
 | 
					;; for non-leaves.
 | 
				
			||||||
 | 
					(defstruct trunk left right symbols weight)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun symbols (n)
 | 
				
			||||||
 | 
					  (if (leaf-p n)
 | 
				
			||||||
 | 
					      (list (leaf-symbol n))
 | 
				
			||||||
 | 
					      (trunk-symbols n)))
 | 
				
			||||||
 | 
					(defun weight (n)
 | 
				
			||||||
 | 
					  (if (leaf-p n)
 | 
				
			||||||
 | 
					      (leaf-weight n)
 | 
				
			||||||
 | 
					      (trunk-weight n)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun trunk-new (left right)
 | 
				
			||||||
 | 
					  (make-trunk :left left
 | 
				
			||||||
 | 
						      :right right
 | 
				
			||||||
 | 
						      :symbols (append (symbols left)
 | 
				
			||||||
 | 
								       (symbols right))
 | 
				
			||||||
 | 
						      :weight (+ (weight left) (weight right))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun choose-branch (bit tree)
 | 
				
			||||||
 | 
					  (cond
 | 
				
			||||||
 | 
					    ((= bit 0) (trunk-left tree))
 | 
				
			||||||
 | 
					    ((= bit 1) (trunk-right tree))
 | 
				
			||||||
 | 
					    (t (error "unknown bit lmao"))))
 | 
				
			||||||
 | 
					(defun decode (bits tree)
 | 
				
			||||||
 | 
					  "interesting. I used an iterative style with loop instead.
 | 
				
			||||||
 | 
					funnily enough, this ended up being fairly concise as well.
 | 
				
			||||||
 | 
					usually loop tends to be really dirty."
 | 
				
			||||||
 | 
					  (let ((result nil)
 | 
				
			||||||
 | 
						(branch tree))
 | 
				
			||||||
 | 
					    (loop for i in bits do
 | 
				
			||||||
 | 
					      (setf branch (choose-branch i branch))
 | 
				
			||||||
 | 
					      (when (leaf-p branch)
 | 
				
			||||||
 | 
						(push (leaf-symbol branch) result)
 | 
				
			||||||
 | 
						(setf branch tree)))
 | 
				
			||||||
 | 
					    (reverse result)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defparameter test-coding
 | 
				
			||||||
 | 
					  (trunk-new (make-leaf :symbol 'A :weight 10)
 | 
				
			||||||
 | 
						     (trunk-new (make-leaf :symbol 'B :weight 6)
 | 
				
			||||||
 | 
								(trunk-new (make-leaf :symbol 'C :weight 0)
 | 
				
			||||||
 | 
									   (make-leaf :symbol 'D :weight 0)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					; ex. 2.68
 | 
				
			||||||
 | 
					(defun search-tree (sym tree)
 | 
				
			||||||
 | 
					  (cond
 | 
				
			||||||
 | 
					    ((leaf-p tree)
 | 
				
			||||||
 | 
					     nil)
 | 
				
			||||||
 | 
					    ((member sym (symbols (trunk-left tree)) :test #'eql)
 | 
				
			||||||
 | 
					     (cons 0 (search-tree sym (trunk-left tree))))
 | 
				
			||||||
 | 
					    ((member sym (symbols (trunk-right tree)) :test #'eql)
 | 
				
			||||||
 | 
					     (cons 1 (search-tree sym (trunk-right tree))))
 | 
				
			||||||
 | 
					    (t (error "what the fuck"))))
 | 
				
			||||||
 | 
					(defun encode-symbol (sym tree)
 | 
				
			||||||
 | 
					  (search-tree sym tree))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun encode (message tree)
 | 
				
			||||||
 | 
					  (if (null message)
 | 
				
			||||||
 | 
					      nil
 | 
				
			||||||
 | 
					      (append
 | 
				
			||||||
 | 
							   (encode-symbol (car message) tree)
 | 
				
			||||||
 | 
							   (encode (cdr message) tree))))
 | 
				
			||||||
 | 
					; ex 2.69
 | 
				
			||||||
 | 
					;; okay, not being able to copy/paste actually kinda sucks.
 | 
				
			||||||
 | 
					;; I'm just gonna define `define` as a macro, then copy.
 | 
				
			||||||
 | 
					(defmacro define (n &body body)
 | 
				
			||||||
 | 
					  (if (listp n)
 | 
				
			||||||
 | 
					      `(defun ,(first n) ,(rest n) ,@body)
 | 
				
			||||||
 | 
					      `(defparameter ,n ,@body)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun weight (x)
 | 
				
			||||||
 | 
					  (if (trunk-p x)
 | 
				
			||||||
 | 
					      (trunk-weight x)
 | 
				
			||||||
 | 
					      (leaf-weight x)))
 | 
				
			||||||
 | 
					(define (adjoin-set x set)
 | 
				
			||||||
 | 
					  (cond ((null set) (list x))
 | 
				
			||||||
 | 
						((< (weight x) (weight (car set))) (cons x set))
 | 
				
			||||||
 | 
						(:else (cons (car set)
 | 
				
			||||||
 | 
							    (adjoin-set x (cdr set))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defun make-leaf-set (pairs)
 | 
				
			||||||
 | 
					  (let ((res nil))
 | 
				
			||||||
 | 
					    (loop for i in pairs do
 | 
				
			||||||
 | 
					      (setf res (adjoin-set (make-leaf :symbol (car i) :weight (cadr i))
 | 
				
			||||||
 | 
								    res)))
 | 
				
			||||||
 | 
					    res))
 | 
				
			||||||
 | 
					;; I already got bored of copy-pasting. The code just looks out of place
 | 
				
			||||||
 | 
					;; when it's written with a lot of scheme-isms
 | 
				
			||||||
 | 
					(defun successive-merge (set)
 | 
				
			||||||
 | 
					  (loop while (< 1 (length set)) do
 | 
				
			||||||
 | 
					    (setf set
 | 
				
			||||||
 | 
						  (adjoin-set (apply #'trunk-new (reverse (list (pop set) (pop set))))
 | 
				
			||||||
 | 
							      set)))
 | 
				
			||||||
 | 
					  (car set))
 | 
				
			||||||
 | 
					(defun successive-merge-functional (set)
 | 
				
			||||||
 | 
					  "This is one area where I like the functional solution more than the
 | 
				
			||||||
 | 
					imperative one. Seriously."
 | 
				
			||||||
 | 
					  (cond
 | 
				
			||||||
 | 
					    ((>= 1 (length set)) (car set))
 | 
				
			||||||
 | 
					    (t (successive-merge-functional
 | 
				
			||||||
 | 
						(print (adjoin-set (trunk-new (second set) (first set))
 | 
				
			||||||
 | 
							     (cddr set)))))))
 | 
				
			||||||
 | 
					; ex 2.70
 | 
				
			||||||
 | 
					(define (generate-huffman-tree pairs)
 | 
				
			||||||
 | 
					  (successive-merge-functional (make-leaf-set pairs)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					; ex 2.71
 | 
				
			||||||
 | 
					(defparameter *song-tree* (generate-huffman-tree
 | 
				
			||||||
 | 
								   '((A 2) (GET 2) (SHA 3) (WAH 1) (BOOM 1) (JOB 2) (NA 16) (YIP 9))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defparameter *song* '(Get a job
 | 
				
			||||||
 | 
							       Sha na na na na na na na na
 | 
				
			||||||
 | 
							       Get a job
 | 
				
			||||||
 | 
							       Sha na na na na na na na na
 | 
				
			||||||
 | 
							       Wah yip yip yip yip yip yip yip yip yip
 | 
				
			||||||
 | 
							       Sha boom))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; the song takes 84 bits to encode.
 | 
				
			||||||
 | 
					;; fixed length code would require 8 unique symbols, so 3 bits per element -
 | 
				
			||||||
 | 
					;; which is 108 bits. Pretty good, we saved roughly ~20% with this
 | 
				
			||||||
 | 
					;; though the efficiency does rely on our analysis of the average message.
 | 
				
			||||||
 | 
					;; we could just as easily lose efficiency if we measure wrong.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(defclass mytest1 ()
 | 
				
			||||||
 | 
					  ((a :accessor slota)))
 | 
				
			||||||
 | 
					(defclass mytest2 ()
 | 
				
			||||||
 | 
					  ((a :accessor slota)))
 | 
				
			||||||
		Reference in New Issue
	
	Block a user