From 1b2b2dfec1d1ed4403024c85f82c91a3f7057459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kapri=C5=A1?= Date: Tue, 4 Feb 2025 22:16:43 +0100 Subject: [PATCH] Add solutions to exercises from section 2.1 To be noted: the last two exercises, 2.15 and 2.16 were left unfinished, and will be left for another time. --- ex-2.01.scm | 8 +++++ ex-2.02.scm | 24 ++++++++++++++ ex-2.03.scm | 34 ++++++++++++++++++++ ex-2.04.scm | 4 +++ ex-2.05.scm | 29 +++++++++++++++++ ex-2.06.scm | 14 +++++++++ ex-2.07.scm | 7 +++++ ex-2.08.scm | 15 +++++++++ ex-2.09.scm | 31 ++++++++++++++++++ ex-2.10.scm | 25 +++++++++++++++ ex-2.11.scm | 75 ++++++++++++++++++++++++++++++++++++++++++++ ex-2.12.scm | 23 ++++++++++++++ ex-2.13.txt | 10 ++++++ ex-2.14.scm | 72 ++++++++++++++++++++++++++++++++++++++++++ ex-2.15-NOT-DONE.txt | 15 +++++++++ ex-2.16-NOT-DONE.txt | 5 +++ 16 files changed, 391 insertions(+) create mode 100644 ex-2.01.scm create mode 100644 ex-2.02.scm create mode 100644 ex-2.03.scm create mode 100644 ex-2.04.scm create mode 100644 ex-2.05.scm create mode 100644 ex-2.06.scm create mode 100644 ex-2.07.scm create mode 100644 ex-2.08.scm create mode 100644 ex-2.09.scm create mode 100644 ex-2.10.scm create mode 100644 ex-2.11.scm create mode 100644 ex-2.12.scm create mode 100644 ex-2.13.txt create mode 100644 ex-2.14.scm create mode 100644 ex-2.15-NOT-DONE.txt create mode 100644 ex-2.16-NOT-DONE.txt diff --git a/ex-2.01.scm b/ex-2.01.scm new file mode 100644 index 0000000..3d9402c --- /dev/null +++ b/ex-2.01.scm @@ -0,0 +1,8 @@ +#lang sicp + +(define (make-rat n d) + (let ((g (gcd n d))) + (cond ((= d 0) + (error "Cannot make-rat with denominator 0")) + ((< d 0) (cons (- (/ n g)) (- (/ d g)))) + (else (cons (/ n g) (/ d g)))))) diff --git a/ex-2.02.scm b/ex-2.02.scm new file mode 100644 index 0000000..aa4e90d --- /dev/null +++ b/ex-2.02.scm @@ -0,0 +1,24 @@ +#lang sicp + +(define (make-segment a b) + (cons a b)) +(define (start-segment s) + (car s)) +(define (end-segment s) + (cdr s)) + +(define (make-point x y) + (cons x y)) +(define (x-point p) + (car p)) +(define (y-point p) + (cdr p)) + +(define (average a b) + (/ (+ a b) 2.0)) + +(define (midpoint-segment s) + (make-point (average (x-point (start-segment s)) + (x-point (end-segment s))) + (average (y-point (start-segment s)) + (y-point (end-segment s))))) diff --git a/ex-2.03.scm b/ex-2.03.scm new file mode 100644 index 0000000..18e8282 --- /dev/null +++ b/ex-2.03.scm @@ -0,0 +1,34 @@ +#lang sicp + +(define (make-point x y) + (cons x y)) +(define (x-point p) + (car p)) +(define (y-point p) + (cdr p)) + +; top-left point, bottom-right point representation +(define (tlbr-rect a b) + (cons 'tlbr (cons a b))) + +; center point, width, height representation +(define (cpwh-rect p w h) + (cons 'cpwh + (cons p + (cons w h)))) + +(define (height rect) + (cond ((eq? 'cpwh (car rect)) (cdddr rect)) + (else (abs (- (y-point (cadr rect)) + (y-point (cddr rect))))))) + + +(define (width rect) + (cond ((eq? 'cpwh (car rect)) (caddr rect)) + (else (abs (- (x-point (cadr rect)) + (x-point (cddr rect))))))) + +(define (perimeter rect) + (* 2 (+ (width rect) (height rect)))) +(define (area rect) + (* (width rect) (height rect))) diff --git a/ex-2.04.scm b/ex-2.04.scm new file mode 100644 index 0000000..8a6223a --- /dev/null +++ b/ex-2.04.scm @@ -0,0 +1,4 @@ +#lang sicp + +(define (cdr z) + (z (lambda (p q) q))) diff --git a/ex-2.05.scm b/ex-2.05.scm new file mode 100644 index 0000000..f2c408f --- /dev/null +++ b/ex-2.05.scm @@ -0,0 +1,29 @@ +#lang sicp + +(define (cons a b) + (* (expt 2 a) + (expt 3 b))) + +(define (car pair) + (if (= (remainder pair 3) 0) + (car (/ pair 3)) + (log2 pair))) + +(define (cdr pair) + (if (= (remainder pair 2) 0) + (cdr (/ pair 2)) + (log3 pair))) + +(define (log2 num) + (define (aux num acc) + (if (<= num 1) + acc + (aux (/ num 2) (+ acc 1)))) + (aux num 0)) + +(define (log3 num) + (define (aux num acc) + (if (<= num 1) + acc + (aux (/ num 3) (+ acc 1)))) + (aux num 0)) diff --git a/ex-2.06.scm b/ex-2.06.scm new file mode 100644 index 0000000..aebf447 --- /dev/null +++ b/ex-2.06.scm @@ -0,0 +1,14 @@ +#lang sicp + +(define one (lambda (f) + (lambda (x) + (f x)))) + +(define two (lambda (f) + (lambda (x) + (f (f x))))) + +(define (+ a b) + (lambda (f) + (lambda (x) + ((a f) ((b f) x))))) diff --git a/ex-2.07.scm b/ex-2.07.scm new file mode 100644 index 0000000..3300196 --- /dev/null +++ b/ex-2.07.scm @@ -0,0 +1,7 @@ +#lang sicp + +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) diff --git a/ex-2.08.scm b/ex-2.08.scm new file mode 100644 index 0000000..054ae5b --- /dev/null +++ b/ex-2.08.scm @@ -0,0 +1,15 @@ +#lang sicp + +(define (make-interval a b) (cons a b)) + +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) + +(define (sub-interval a b) + (make-interval (- (lower-bound a) + (upper-bound b)) + (- (upper-bound a) + (lower-bound b)))) diff --git a/ex-2.09.scm b/ex-2.09.scm new file mode 100644 index 0000000..b516fc0 --- /dev/null +++ b/ex-2.09.scm @@ -0,0 +1,31 @@ +#lang sicp +#| +(define (add-interval x y) + (make-interval (+ (lower-bound x) (lower-bound y)) + (+ (upper-bound x) (upper-bound y)))) + +(define (sub-interval a b) + (make-interval (- (lower-bound a) (upper-bound b)) + (- (upper-bound a) (lower-bound b)))) + +Notice that in add-interval, the lower-bound is low-x + low-y. +Meanwhile the higher-bound is high-x + high-y = low-x + width-x ++ low-y + width-y = low-sum + (width-x + width-y). +So width-sum = width-x + width-y. + +Similar logic applies to sub-interval. + +For mul-interval it's enough to see that +(mul-interval (make-interval 1 2) (make-interval 3 4)) = +(3 . 8) -> width = 5 +doesn't have the same width as: +(mul-interval (make-interval 3 4) (make-interval 3 4)) = +(9 . 16) -> width = 7 +despite the fact that in both cases, the intervals have widths: +1 and 1. +|# + +; This code is only here to pass hexlet's test, which I'm not sure is +; necessary. +(define (width x) (/ (- (upper-bound x) (lower-bound x)) + 2)) diff --git a/ex-2.10.scm b/ex-2.10.scm new file mode 100644 index 0000000..ec34ca0 --- /dev/null +++ b/ex-2.10.scm @@ -0,0 +1,25 @@ +#lang sicp + +(define (make-interval a b) (cons a b)) + +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) + +(define (mul-interval x y) + (let ((p1 (* (lower-bound x) (lower-bound y))) + (p2 (* (lower-bound x) (upper-bound y))) + (p3 (* (upper-bound x) (lower-bound y))) + (p4 (* (upper-bound x) (upper-bound y)))) + (make-interval (min p1 p2 p3 p4) + (max p1 p2 p3 p4)))) + +(define (div-interval x y) + (if (<= (* (lower-bound y) (upper-bound y)) 0) + (error "division by zero") + (mul-interval + x + (make-interval (/ 1.0 (upper-bound y)) + (/ 1.0 (lower-bound y)))))) diff --git a/ex-2.11.scm b/ex-2.11.scm new file mode 100644 index 0000000..463d1a9 --- /dev/null +++ b/ex-2.11.scm @@ -0,0 +1,75 @@ +#lang sicp + +(define (make-interval a b) (cons a b)) + +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) + + +; explanation for sgn-interval: +; an interval has sign 0 if it has negative and non-negative +; values (ie. it spans over zero) +; a sign of -1 if all of it's values are negative +; and a sign of 1 if all of it's values are non-negative +(define (sgn-interval a) + (let ((low-a (lower-bound a)) + (upp-a (upper-bound a))) + (cond ((< upp-a 0) -1) + ((>= low-a 0) 1) + (else 0)))) + +(define (mul-interval x y) + (let* ((sgn-x (sgn-interval x)) + (sgn-y (sgn-interval y)) + (sgn-pair (cons sgn-x sgn-y))) + (cond ((equal? sgn-pair '(-1 . -1)) + (make-interval (* (upper-bound x) + (upper-bound y)) + (* (lower-bound x) + (lower-bound y)))) + ((equal? sgn-pair '(-1 . 0)) + (make-interval (* (lower-bound x) + (upper-bound y)) + (* (lower-bound x) + (lower-bound y)))) + ((equal? sgn-pair '(-1 . 1)) + (make-interval (* (lower-bound x) + (upper-bound y)) + (* (upper-bound x) + (lower-bound y)))) + ((equal? sgn-pair '(0 . -1)) + (make-interval (* (upper-bound x) + (lower-bound y)) + (* (lower-bound x) + (lower-bound y)))) + ; the difficult case + ((equal? sgn-pair '(0 . 0)) + (let ((uu (* (upper-bound x) (upper-bound y))) + (ul (* (upper-bound x) (lower-bound y))) + (lu (* (lower-bound x) (upper-bound y))) + (ll (* (lower-bound x) (lower-bound y)))) + (make-interval (min ul lu) + (max uu ll)))) + ((equal? sgn-pair '(0 . 1)) + (make-interval (* (lower-bound x) + (upper-bound y)) + (* (upper-bound x) + (upper-bound y)))) + ((equal? sgn-pair '(1 . -1)) + (make-interval (* (upper-bound x) + (lower-bound y)) + (* (lower-bound x) + (upper-bound y)))) + ((equal? sgn-pair '(1 . 0)) + (make-interval (* (upper-bound x) + (lower-bound y)) + (* (upper-bound x) + (upper-bound y)))) + ((equal? sgn-pair '(1 . 1)) + (make-interval (* (lower-bound x) + (lower-bound y)) + (* (upper-bound x) + (upper-bound y))))))) diff --git a/ex-2.12.scm b/ex-2.12.scm new file mode 100644 index 0000000..ead1ad3 --- /dev/null +++ b/ex-2.12.scm @@ -0,0 +1,23 @@ +#lang sicp + +(define (make-interval a b) (cons a b)) + +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) + + +(define (make-center-percent c p) + (make-interval (- c (* c p 1/100)) + (+ c (* c p 1/100)))) + +(define (center i) + (/ (+ (lower-bound i) (upper-bound i)) 2)) + +(define (width i) + (/ (- (upper-bound i) (lower-bound i)) 2)) + +(define (percent i) + (* (/ (width i) (center i)) 100)) diff --git a/ex-2.13.txt b/ex-2.13.txt new file mode 100644 index 0000000..3f617c9 --- /dev/null +++ b/ex-2.13.txt @@ -0,0 +1,10 @@ +Let's limit our argument for positive numbers: +a+-wa * b+-wb = [(a-wa)*(b-wb), (a+wa)*(b+wb)] +[a*b - a*wb - b*wb + wa*wb, a*b + a*wb + b*wa + wa*wb] +if we assume wa*wb is insignificantly small, we get: +a*b+-(a*wb+b*wa) + +tolerances are the following: +ta = wa/a +tb = wb/b +tab = (a*wb+b*wa)/(a*b) = (a*b*tb + a*b*ta)/(a*b) = ta+tb diff --git a/ex-2.14.scm b/ex-2.14.scm new file mode 100644 index 0000000..4a59e31 --- /dev/null +++ b/ex-2.14.scm @@ -0,0 +1,72 @@ +#lang sicp +(define make-interval cons) +(define (lower-bound i) + (car i)) + +(define (upper-bound i) + (cdr i)) + +(define (sub-interval a b) + (make-interval (- (lower-bound a) + (upper-bound b)) + (- (upper-bound a) + (lower-bound b)))) + +(define (mul-interval x y) +(let ((p1 (* (lower-bound x) (lower-bound y))) + (p2 (* (lower-bound x) (upper-bound y))) +(p3 (* (upper-bound x) (lower-bound y))) +(p4 (* (upper-bound x) (upper-bound y)))) +(make-interval (min p1 p2 p3 p4) +(max p1 p2 p3 p4)))) + +(define (add-interval x y) +(make-interval (+ (lower-bound x) (lower-bound y)) +(+ (upper-bound x) (upper-bound y)))) + +(define (div-interval x y) + (if (<= (* (lower-bound y) (upper-bound y)) 0) + (error "division by zero") + (mul-interval + x + (make-interval (/ 1.0 (upper-bound y)) + (/ 1.0 (lower-bound y)))))) + + + + +(define (par1 r1 r2) + (div-interval (mul-interval r1 r2) + (add-interval r1 r2))) +(define (par2 r1 r2) + (let ((one (make-interval 1 1))) + (div-interval + one (add-interval (div-interval one r1) + (div-interval one r2))))) + +(define example-1 (make-interval 1 2)) +(define example-2 (make-interval 0.999 1.001)) + +(div-interval example-1 example-1) +;(0.5 . 2.0) +(div-interval example-2 example-2) +;(0.9980019980019982 . 1.002002002002002) + +; It's simplest to explain this using the previous result +; that the tolerance of a product of a*b, is roughly the +; sum of the tolerances (if the tolerances are small +; relative to the center point) +; We can also derive that the tolerance of 1/a is equal to +; the tolerance of a. +; Proof: +; a = [x-w,x+w], t_a = w/x +; 1/a = [1/(x+w), 1/(x-w)] = +; [(1/(x+w)) * (x-w)/(x-w), (1/(x-w)) * (x+w)/(x+w)] = +; [(x-w)/(x^2-w^2), (x+w)/(x^2-w^2)] = +; x/(x^2-w^2) +- w/(x^2-w^2) +; the tolerance of the product is then: +; (w/(x^2-w^2)) / (x/(x^2-w^2)) = w/x, same as t_a. +; +; This means, using the previous approximation, that the +; expression a/a = 1, rather than having a tolerance of 0 +; is going to have a tolerance doubling that of a. \ No newline at end of file diff --git a/ex-2.15-NOT-DONE.txt b/ex-2.15-NOT-DONE.txt new file mode 100644 index 0000000..197c79c --- /dev/null +++ b/ex-2.15-NOT-DONE.txt @@ -0,0 +1,15 @@ +This is mostly explained in the previous exercise. + +When it comes to par1 and par2 specifically, we should examine +the formulas themselves: +((R1+-t1*R1)*(R2+-t2*R2)) / (R1+-t1*R1 + R2+-t2*R2) = +(R1*R2 +-t1*R1*R2 +-t2*R2*R1 +-t1*t2*R1*R2) / + (R1+R2+-t1*R1+-t2*R2) = +(R1*R2 +-t1*R1*R2 +-t2*R2*R1 +-t1*t2*R1*R2) / + (R1+R2+-t1*R1+-t2*R2) * (R1+R2-+t1*R1-+t2*R2)/(R1+R2-+t1*R1-+t2*R2) = +(R1*R2 +-t1*R1*R2 +-t2*R2*R1 +-t1*t2*R1*R2) / + (R1+R2+-t1*R1+-t2*R2) * (R1+R2-+t1*R1-+t2*R2)/(R1+R2-+t1*R1-+t2*R2) = + + +1 / (1/(R1+-t1)+1/(R2+-t2)) = +1 / ((R1-+t1)/(R1^2-t1^2) + (R2-+t2)/(R2^2-t2^2)) = diff --git a/ex-2.16-NOT-DONE.txt b/ex-2.16-NOT-DONE.txt new file mode 100644 index 0000000..8ec7d67 --- /dev/null +++ b/ex-2.16-NOT-DONE.txt @@ -0,0 +1,5 @@ +One issue is the afformention reuse of intervals, another is +floating point arithmetic. In order to solve the problem of making +all expressions maximally precise, we would need to make a package that can +take a given arithmetic expression and minimize it. This is +probaly very hard but not impossible.