Skip to content

Commit

Permalink
Add exercise 2.69 (#145)
Browse files Browse the repository at this point in the history
* Add exercise 2.69

Implemented exercise 2.69 which involves generating a Huffman encoding tree. Updated the make-leaf-set function in the book_2_3.clj file for correct functionality. The README has been updated to reflect the progress of completed exercises and the addition of exercise 2.69.

* Refactor Huffman Tree related tests and logic

Refactored the logic related to Huffman Tree examples and tests. Created constants for symbols, messages, and Huffman Tree in book_2_3.clj and reference these constants in tests to reduce code duplication. This also allows easier changes in future as only the main definition needs to be adjusted.

* Remove unused import and update encode call in test

Removed unused 'huffman-pairs' import from book_2_3_test.clj. Also, updated the call to 'encode' in ex_2_68_test.clj to use the 'huffman-tree' defined in 'b23', providing cleaner and more maintainable code.
  • Loading branch information
SmetDenis authored Jan 31, 2024
1 parent 5337021 commit fcd6263
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 38 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Solving exercises from SICP with Clojure

[![Clojure CI](https://github.com/SmetDenis/Clojure-Sicp/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/SmetDenis/Clojure-Sicp/actions/workflows/main.yml)
![Progress](https://progress-bar.dev/114/?scale=356&title=Solved&width=500&suffix=)
![Progress](https://progress-bar.dev/115/?scale=356&title=Solved&width=500&suffix=)

SICP (Structure and Interpretation of Computer Programs) is the book of Harold Abelson and Gerald
Jay Sussman on basics of computer science and software engineering.
Expand Down Expand Up @@ -42,7 +42,7 @@ Jay Sussman on basics of computer science and software engineering.

### Chapter 2 - Building Abstractions with Data

![Progress](https://progress-bar.dev/68/?scale=97&title=Solved&width=500&suffix=)
![Progress](https://progress-bar.dev/69/?scale=97&title=Solved&width=500&suffix=)

* [2.1](https://sarabander.github.io/sicp/html/Chapter-2.xhtml#Chapter-2) Introduction to Data Abstraction - [Code in book](src/sicp/chapter_2/part_1/book_2_1.clj)
* [2.1.1](https://sarabander.github.io/sicp/html/2_002e1.xhtml#g_t2_002e1_002e1) Example: Arithmetic Operations for Rational Numbers - [2.1](src/sicp/chapter_2/part_1/ex_2_01.clj)
Expand All @@ -58,7 +58,7 @@ Jay Sussman on basics of computer science and software engineering.
* [2.3.1](https://sarabander.github.io/sicp/html/2_002e3.xhtml#g_t2_002e3_002e1) Quotation - [2.53](src/sicp/chapter_2/part_3/ex_2_53.clj), [2.54](src/sicp/chapter_2/part_3/ex_2_54.clj), [2.55](src/sicp/chapter_2/part_3/ex_2_55.clj)
* [2.3.2](https://sarabander.github.io/sicp/html/2_002e3.xhtml#g_t2_002e3_002e2) Example: Symbolic Differentiation - [2.56](src/sicp/chapter_2/part_3/ex_2_56.clj), [2.57](src/sicp/chapter_2/part_3/ex_2_57.clj), [2.58](src/sicp/chapter_2/part_3/ex_2_58.clj)
* [2.3.3](https://sarabander.github.io/sicp/html/2_002e3.xhtml#g_t2_002e3_002e3) Example: Representing Sets - [2.59](src/sicp/chapter_2/part_3/ex_2_59.clj), [2.60](src/sicp/chapter_2/part_3/ex_2_60.clj), [2.61](src/sicp/chapter_2/part_3/ex_2_61.clj), [2.62](src/sicp/chapter_2/part_3/ex_2_62.clj), [2.63](src/sicp/chapter_2/part_3/ex_2_63.clj), [2.64](src/sicp/chapter_2/part_3/ex_2_64.clj), [2.65](src/sicp/chapter_2/part_3/ex_2_65.clj), [2.66](src/sicp/chapter_2/part_3/ex_2_66.clj)
* [2.3.4](https://sarabander.github.io/sicp/html/2_002e3.xhtml#g_t2_002e3_002e4) Example: Huffman Encoding Trees - [2.67](src/sicp/chapter_2/part_3/ex_2_67.clj), [2.68](src/sicp/chapter_2/part_3/ex_2_68.clj)
* [2.3.4](https://sarabander.github.io/sicp/html/2_002e3.xhtml#g_t2_002e3_002e4) Example: Huffman Encoding Trees - [2.67](src/sicp/chapter_2/part_3/ex_2_67.clj), [2.68](src/sicp/chapter_2/part_3/ex_2_68.clj), [2.69](src/sicp/chapter_2/part_3/ex_2_69.clj)
* 2.4 Multiple Representations for Abstract Data
* 2.4.1 Representations for Complex Numbers
* 2.4.2 Tagged data
Expand Down
39 changes: 37 additions & 2 deletions src/sicp/chapter_2/part_3/book_2_3.clj
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,48 @@
(empty? set) (list x)
(< (weight x) (weight (first set))) (cons x set)
:else (cons (first set)
(adjoin-set x (rest set)))))
(adjoin-set-h x (rest set)))))

(defn make-leaf-set [pairs]
(if (empty? pairs)
'()
(let [pair (first pairs)]
(adjoin-set
(adjoin-set-h
(make-leaf (first pair) ; symbol
(second pair)) ; frequency
(make-leaf-set (rest pairs))))))

; Samples
(def huffman-pairs '((:A 4) (:B 2) (:C 1) (:D 1)))

(def huffman-tree
(make-code-tree
(make-leaf :A 4)
(make-code-tree
(make-leaf :B 2)
(make-code-tree
(make-leaf :D 1)
(make-leaf :C 1)))))

(def huffman-tree-as-list
'[[:leaf :A 4]
[[:leaf :B 2]
[[:leaf :D 1]
[:leaf :C 1]
(:D :C) 2]
(:B :D :C) 4]
(:A :B :D :C) 8])

(def huffman-message-decoded '(0 ; A
1 1 0 ; D
0 ; A
1 0 ; B
1 0 ; B
1 1 1 ; C
0)) ; A
(def huffman-message-encoded '(:A :D :A :B :B :C :A))

(def huffman-A '(0))
(def huffman-B '(1 0))
(def huffman-C '(1 1 1))
(def huffman-D '(1 1 0))
35 changes: 35 additions & 0 deletions src/sicp/chapter_2/part_3/ex_2_69.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
(ns sicp.chapter-2.part-3.ex-2-69
(:require [sicp.chapter-2.part-3.book-2-3 :as b23]))

; Exercise 2.69
;
; The following procedure takes as its argument a list of symbol-frequency pairs
; (where no symbol appears in more than one pair) and generates a Huffman encoding tree according
; to the Huffman algorithm.
;
; (define (generate-huffman-tree pairs)
; (successive-merge
; (make-leaf-set pairs)))
;
; Make-leaf-set is the procedure given above that transforms the list of pairs into an ordered set
; of leaves. Successive-merge is the procedure you must write, using make-code-tree to successively
; merge the smallest-weight elements of the set until there is only one element left, which is the
; desired Huffman tree.
;
; (This procedure is slightly tricky, but not really complicated. If you find yourself designing
; a complex procedure, then you are almost certainly doing something wrong.)
;
; You can take significant advantage of the fact that we are using an ordered set representation.)

(defn successive-merge [leaf-set]
(if (= (count leaf-set) 1)
(first leaf-set)
(let [first (first leaf-set)
second (second leaf-set)
rest (drop 2 leaf-set)]
(successive-merge (b23/adjoin-set-h
(b23/make-code-tree first second)
rest)))))

(defn generate-huffman-tree [pairs]
(successive-merge (b23/make-leaf-set pairs)))
11 changes: 8 additions & 3 deletions test/sicp/chapter_2/part_3/book_2_3_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
element-of-set-tree?
element-of-set?
entry
huffman-tree
huffman-tree-as-list
intersection-set
intersection-set-sorted
leaf?
Expand Down Expand Up @@ -163,8 +165,11 @@
(adjoin-set-h (make-leaf :b 4) (list (make-leaf :b 4) (make-leaf :a 8))))))

(deftest make-leaf-set-test
(is (= '([:leaf :A 4]
[:leaf :B 2]
(is (= '([:leaf :D 1]
[:leaf :C 1]
[:leaf :D 1])
[:leaf :B 2]
[:leaf :A 4])
(make-leaf-set '((:A 4) (:B 2) (:C 1) (:D 1))))))

(deftest huffman-examples-tests
(is (= huffman-tree-as-list huffman-tree)))
16 changes: 2 additions & 14 deletions test/sicp/chapter_2/part_3/ex_2_67_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,6 @@
[sicp.chapter-2.part-3.book-2-3 :as b23]
[sicp.chapter-2.part-3.ex-2-67 :refer [decode-message]]))

; [[:leaf :A 4] [[:leaf :B 2] [[:leaf :D 1] [:leaf :C 1] (:D :C) 2] (:B :D :C) 4] (:A :B :D :C) 8]
(def sample-tree
(b23/make-code-tree
(b23/make-leaf :A 4)
(b23/make-code-tree
(b23/make-leaf :B 2)
(b23/make-code-tree
(b23/make-leaf :D 1)
(b23/make-leaf :C 1)))))

(def sample-message '(0 1 1 0 0 1 0 1 0 1 1 1 0))

(deftest decode-message-test
(is (= '(:A :D :A :B :B :C :A)
(decode-message sample-message sample-tree))))
(is (= b23/huffman-message-encoded
(decode-message b23/huffman-message-decoded b23/huffman-tree))))
23 changes: 7 additions & 16 deletions test/sicp/chapter_2/part_3/ex_2_68_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,21 @@
[sicp.chapter-2.part-3.book-2-3 :as b23]
[sicp.chapter-2.part-3.ex-2-68 :refer [encode encode-symbol]]))

(def sample-tree
(b23/make-code-tree
(b23/make-leaf :A 4)
(b23/make-code-tree
(b23/make-leaf :B 2)
(b23/make-code-tree
(b23/make-leaf :D 1)
(b23/make-leaf :C 1)))))

(deftest encode-symbol-test
(is (= '(0) (encode-symbol :A sample-tree)))
(is (= '(1 0) (encode-symbol :B sample-tree)))
(is (= '(1 1 1) (encode-symbol :C sample-tree)))
(is (= '(1 1 0) (encode-symbol :D sample-tree))))
(is (= b23/huffman-A (encode-symbol :A b23/huffman-tree)))
(is (= b23/huffman-B (encode-symbol :B b23/huffman-tree)))
(is (= b23/huffman-C (encode-symbol :C b23/huffman-tree)))
(is (= b23/huffman-D (encode-symbol :D b23/huffman-tree))))

(deftest encode-symbol-exception-test
(is (thrown-with-msg?
Exception
#"Symbol not found in tree :Z"
(encode-symbol :Z sample-tree))))
(encode-symbol :Z b23/huffman-tree))))

(deftest encode-exception-test
(try
(encode-symbol :Z sample-tree)
(encode-symbol :Z b23/huffman-tree)
(is false "Exception not thrown")
(catch Exception e
(is (= (.getMessage e) "Symbol not found in tree :Z")))))
Expand All @@ -39,4 +30,4 @@
1 0 ; B
1 1 1 ; C
0) ; A
(encode '(:A :D :A :B :B :C :A) sample-tree))))
(encode '(:A :D :A :B :B :C :A) b23/huffman-tree))))
14 changes: 14 additions & 0 deletions test/sicp/chapter_2/part_3/ex_2_69_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(ns sicp.chapter-2.part-3.ex-2-69-test
(:require [clojure.test :refer [deftest is]]
[sicp.chapter-2.part-3.book-2-3 :as b23]
[sicp.chapter-2.part-3.ex-2-68 :refer [encode-symbol]]
[sicp.chapter-2.part-3.ex-2-69 :refer [generate-huffman-tree]]))

(deftest generate-huffman-tree-test
(is (= b23/huffman-tree (generate-huffman-tree b23/huffman-pairs))))

(deftest encode-symbol-test
(is (= b23/huffman-A (encode-symbol :A (generate-huffman-tree b23/huffman-pairs))))
(is (= b23/huffman-B (encode-symbol :B (generate-huffman-tree b23/huffman-pairs))))
(is (= b23/huffman-C (encode-symbol :C (generate-huffman-tree b23/huffman-pairs))))
(is (= b23/huffman-D (encode-symbol :D (generate-huffman-tree b23/huffman-pairs)))))

0 comments on commit fcd6263

Please sign in to comment.