Add solutions to exercises from subsection 2.2.3
Notes: The exercise 2.37 on Hexlet's site has an error, noted in the comments. Also, their page for exercise 2.38 should probably have 0 tests. And finally, I did not calculate the exact number in the final exercise 2.43, but I included a relevant discussion.
This commit is contained in:
parent
8f4c2125bc
commit
2ca5e73a27
11 changed files with 368 additions and 0 deletions
21
ex-2.33.scm
Normal file
21
ex-2.33.scm
Normal file
|
@ -0,0 +1,21 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (map p sequence)
|
||||
(accumulate (lambda (x y) (cons (p x)
|
||||
y))
|
||||
nil
|
||||
sequence))
|
||||
|
||||
(define (append seq1 seq2)
|
||||
(accumulate cons seq2 seq1))
|
||||
|
||||
(define (inc x y) (+ y 1))
|
||||
|
||||
(define (length sequence)
|
||||
(accumulate inc 0 sequence))
|
13
ex-2.34.scm
Normal file
13
ex-2.34.scm
Normal file
|
@ -0,0 +1,13 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (horner-eval x coefficient-sequence)
|
||||
(accumulate (lambda (this-coeff higher-terms)
|
||||
(+ (* higher-terms x) this-coeff))
|
||||
0
|
||||
coefficient-sequence))
|
14
ex-2.35.scm
Normal file
14
ex-2.35.scm
Normal file
|
@ -0,0 +1,14 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (count-leaves t)
|
||||
(accumulate + 0 (map (lambda (sub-t)
|
||||
(cond ((pair? sub-t) (count-leaves sub-t))
|
||||
((null? sub-t) 0)
|
||||
(else 1)))
|
||||
t)))
|
13
ex-2.36.scm
Normal file
13
ex-2.36.scm
Normal file
|
@ -0,0 +1,13 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (accumulate-n op init seqs)
|
||||
(if (null? (car seqs))
|
||||
nil
|
||||
(cons (accumulate op init (map car seqs))
|
||||
(accumulate-n op init (map cdr seqs)))))
|
36
ex-2.37.scm
Normal file
36
ex-2.37.scm
Normal file
|
@ -0,0 +1,36 @@
|
|||
#lang sicp
|
||||
|
||||
;;;;
|
||||
;; Error in the hexlet exercise: the matrix-*-vector line should say
|
||||
;; m_ij*v_j not m_ij*v_i
|
||||
;;;;
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (accumulate-n op init seqs)
|
||||
(if (null? (car seqs))
|
||||
'()
|
||||
(cons (accumulate op init (map car seqs))
|
||||
(accumulate-n op init (map cdr seqs)))))
|
||||
|
||||
(define (dot-product v w)
|
||||
(accumulate + 0 (map * v w)))
|
||||
|
||||
(define (matrix-*-vector m v)
|
||||
(map (lambda (row)
|
||||
(dot-product row v))
|
||||
m))
|
||||
|
||||
(define (matrix-*-matrix m n)
|
||||
(let ((cols (transpose n)))
|
||||
(map (lambda (row-m)
|
||||
(map (lambda (col)
|
||||
(dot-product row-m col))
|
||||
cols))
|
||||
m)))
|
||||
|
||||
(define (transpose m)
|
||||
(accumulate-n cons '() m))
|
22
ex-2.38.scm
Normal file
22
ex-2.38.scm
Normal file
|
@ -0,0 +1,22 @@
|
|||
#lang sicp
|
||||
|
||||
(define (fold-right op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(fold-right op initial (cdr sequence)))))
|
||||
|
||||
(define (fold-left op initial sequence)
|
||||
(define (iter result rest)
|
||||
(if (null? rest)
|
||||
result
|
||||
(iter (op result (car rest))
|
||||
(cdr rest))))
|
||||
(iter initial sequence))
|
||||
|
||||
; (fold-right / 1 (list 1 2 3)) -> (1/(2/(3/1))) = 3/2
|
||||
; (fold-left / 1 (list 1 2 3)) -> (((1/1)/2)/3) = 1/6
|
||||
; (fold-right list nil (list 1 2 3)) -> (1 (2 (3 ())))
|
||||
; (fold-left list nil (list 1 2 3)) -> (((() 1) 2) 3)
|
||||
; in order to produce the same results, the operation
|
||||
; must be associative.
|
21
ex-2.39.scm
Normal file
21
ex-2.39.scm
Normal file
|
@ -0,0 +1,21 @@
|
|||
#lang sicp
|
||||
|
||||
(define (fold-right op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(fold-right op initial (cdr sequence)))))
|
||||
|
||||
(define (fold-left op initial sequence)
|
||||
(define (iter result rest)
|
||||
(if (null? rest)
|
||||
result
|
||||
(iter (op result (car rest))
|
||||
(cdr rest))))
|
||||
(iter initial sequence))
|
||||
|
||||
(define (reverse-right sequence)
|
||||
(fold-right (lambda (x y) (append y (list x))) nil sequence))
|
||||
|
||||
(define (reverse-left sequence)
|
||||
(fold-left (lambda (x y) (cons y x)) nil sequence))
|
55
ex-2.40.scm
Normal file
55
ex-2.40.scm
Normal file
|
@ -0,0 +1,55 @@
|
|||
#lang sicp
|
||||
|
||||
; a) Define unique-pairs
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (flatmap proc seq)
|
||||
(accumulate append nil (map proc seq)))
|
||||
|
||||
(define (enumerate-interval a b)
|
||||
(if (> a b)
|
||||
'()
|
||||
(cons a (enumerate-interval (+ a 1) b))))
|
||||
|
||||
(define (unique-pairs n)
|
||||
(flatmap (lambda (i)
|
||||
(map (lambda (j) (list i j))
|
||||
(enumerate-interval 1 (- i 1))))
|
||||
(enumerate-interval 1 n)))
|
||||
|
||||
; b) use it to define prime-sum-pairs
|
||||
|
||||
(define (filter predicate sequence)
|
||||
(cond ((null? sequence) nil)
|
||||
((predicate (car sequence))
|
||||
(cons (car sequence)
|
||||
(filter predicate (cdr sequence))))
|
||||
(else (filter predicate (cdr sequence)))))
|
||||
|
||||
(define (square x) (* x x))
|
||||
|
||||
(define (smallest-divisor n)
|
||||
(find-divisor n 2))
|
||||
(define (find-divisor n test-divisor)
|
||||
(cond ((> (square test-divisor) n) n)
|
||||
((divides? test-divisor n) test-divisor)
|
||||
(else (find-divisor n (+ test-divisor 1)))))
|
||||
(define (divides? a b)
|
||||
(= (remainder b a) 0))
|
||||
|
||||
(define (prime? n)
|
||||
(= n (smallest-divisor n)))
|
||||
|
||||
(define (prime-sum? pair)
|
||||
(prime? (+ (car pair) (cadr pair))))
|
||||
|
||||
(define (prime-sum-pairs n)
|
||||
(map (lambda (pair)
|
||||
(list (car pair) (cadr pair) (+ (car pair)
|
||||
(cadr pair))))
|
||||
(filter prime-sum?
|
||||
(unique-pairs n))))
|
40
ex-2.41.scm
Normal file
40
ex-2.41.scm
Normal file
|
@ -0,0 +1,40 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (flatmap proc seq)
|
||||
(accumulate append nil (map proc seq)))
|
||||
|
||||
(define (filter predicate sequence)
|
||||
(cond ((null? sequence) nil)
|
||||
((predicate (car sequence))
|
||||
(cons (car sequence)
|
||||
(filter predicate (cdr sequence))))
|
||||
(else (filter predicate (cdr sequence)))))
|
||||
|
||||
(define (enumerate-interval a b)
|
||||
(if (> a b)
|
||||
'()
|
||||
(cons a (enumerate-interval (+ a 1) b))))
|
||||
|
||||
(define (sum l) (accumulate + 0 l))
|
||||
|
||||
; returns a function which checks given list for
|
||||
; specific sum
|
||||
(define (sum-is-num s)
|
||||
(lambda (list)
|
||||
(= (sum list) s)))
|
||||
|
||||
(define (ordered-triples-with-sum n s)
|
||||
(filter (sum-is-num s)
|
||||
(flatmap (lambda (i)
|
||||
(flatmap (lambda (j)
|
||||
(map (lambda (k)
|
||||
(list i j k))
|
||||
(enumerate-interval 1 (- j 1))))
|
||||
(enumerate-interval 1 (- i 1))))
|
||||
(enumerate-interval 1 n))))
|
71
ex-2.42.scm
Normal file
71
ex-2.42.scm
Normal file
|
@ -0,0 +1,71 @@
|
|||
#lang sicp
|
||||
|
||||
(define (accumulate op initial sequence)
|
||||
(if (null? sequence)
|
||||
initial
|
||||
(op (car sequence)
|
||||
(accumulate op initial (cdr sequence)))))
|
||||
|
||||
(define (flatmap proc seq)
|
||||
(accumulate append nil (map proc seq)))
|
||||
|
||||
(define (filter predicate sequence)
|
||||
(cond ((null? sequence) nil)
|
||||
((predicate (car sequence))
|
||||
(cons (car sequence)
|
||||
(filter predicate (cdr sequence))))
|
||||
(else (filter predicate (cdr sequence)))))
|
||||
|
||||
(define (enumerate-interval a b)
|
||||
(if (> a b)
|
||||
'()
|
||||
(cons a (enumerate-interval (+ a 1) b))))
|
||||
|
||||
; the board will be implemented as a list, which will, in a reversed order, list
|
||||
; the rows of the queens, who are sorted by columns, for example:
|
||||
; (5 3 1 4) means there are four queens with coordinates:
|
||||
; (1,4), (2,1), (3,3), (4,5)
|
||||
; this implementation is mostly chosen because of its efficiency and ease of use
|
||||
(define empty-board '())
|
||||
|
||||
; with this implementation, k is unnecessary
|
||||
(define (adjoin-position new-row k rest-of-queens)
|
||||
(cons new-row rest-of-queens))
|
||||
|
||||
(define (all-false lst)
|
||||
(cond ((eq? lst '()) #t)
|
||||
((not (car lst)) (all-false (cdr lst)))
|
||||
(else #f)))
|
||||
|
||||
(define (diagonals-from-row row len)
|
||||
(list (enumerate-interval (+ row 1) (+ row len))
|
||||
(reverse (enumerate-interval (- row len) (- row 1)))))
|
||||
|
||||
; same here
|
||||
(define (safe? k positions)
|
||||
(let* ((first (car positions))
|
||||
(rest (cdr positions))
|
||||
(diags (diagonals-from-row first (length rest)))
|
||||
(upper-diag (car diags))
|
||||
(lower-diag (cadr diags))
|
||||
(match-row (map (lambda (x) (= x first))
|
||||
rest))
|
||||
(match-diag1 (map = rest upper-diag))
|
||||
(match-diag2 (map = rest lower-diag)))
|
||||
(and (all-false match-row)
|
||||
(all-false match-diag1)
|
||||
(all-false match-diag2))))
|
||||
|
||||
(define (queens board-size)
|
||||
(define (queen-cols k)
|
||||
(if (= k 0)
|
||||
(list empty-board)
|
||||
(filter
|
||||
(lambda (positions) (safe? k positions))
|
||||
(flatmap
|
||||
(lambda (rest-of-queens)
|
||||
(map (lambda (new-row)
|
||||
(adjoin-position new-row k rest-of-queens))
|
||||
(enumerate-interval 1 board-size)))
|
||||
(queen-cols (- k 1))))))
|
||||
(queen-cols board-size))
|
62
ex-2.43.txt
Normal file
62
ex-2.43.txt
Normal file
|
@ -0,0 +1,62 @@
|
|||
The normal function presented here:
|
||||
(define (queens board-size)
|
||||
(define (queen-cols k)
|
||||
(if (= k 0)
|
||||
(list empty-board)
|
||||
(filter
|
||||
(lambda (positions) (safe? k positions))
|
||||
(flatmap
|
||||
(lambda (rest-of-queens)
|
||||
(map (lambda (new-row)
|
||||
(adjoin-position
|
||||
new-row k rest-of-queens))
|
||||
(enumerate-interval 1 board-size)))
|
||||
(queen-cols (- k 1))))))
|
||||
(queen-cols board-size))
|
||||
|
||||
gets executed in roughly the following way: Before any of the calls to filter
|
||||
or flatmap are performed (queen-cols (- k 1)) will be run. This will happen
|
||||
recursively, so it will recursively call until (queen-cols 0), which will
|
||||
return a list with a single empty-board.
|
||||
Then, in the (queens-col 1) call, eight new boards, each with a single queen,
|
||||
will be returned, then in the (queens-col 2), 64 will be created, then
|
||||
filtered, and so forth, each time we exit the current call in the stack, we
|
||||
have already filtered the results of the previous call.
|
||||
|
||||
On the other hand Louis Reasoner's function looks like this:
|
||||
(define (queens board-size)
|
||||
(define (queen-cols k)
|
||||
(if (= k 0)
|
||||
(list empty-board)
|
||||
(filter
|
||||
(lambda (positions) (safe? k positions))
|
||||
(flatmap
|
||||
(lambda (new-row)
|
||||
(map (lambda (rest-of-queens)
|
||||
(adjoin-position new-row k rest-of-queens))
|
||||
(queen-cols (- k 1))))
|
||||
(enumerate-interval 1 board-size)))))
|
||||
(queen-cols board-size))
|
||||
|
||||
And it first calls enumerate-interval, and generates a list of 8 numbers.
|
||||
After that, each of these numbers get the outer lambda applied to them, which
|
||||
then calls (queen-cols 7) eight times. It will then adjoin, and afterwards
|
||||
filter the results. It should be noted, these sub-calls WILL generate filtered
|
||||
lists of queens, it's not as if all possible cases will be generated, and only
|
||||
then filtered, however, each of these recursive subcalls will generate a list
|
||||
of (queen-cols (- k 1)) eight times, instead of once, and so on recursively.
|
||||
|
||||
In other words, for the older version of the function, the execution time T(n)
|
||||
(assuming board-size is 8), is roughly equal to:
|
||||
T(n) = T(n-1) + O(8*Q(n-1))
|
||||
^adjoining and filtering ^
|
||||
(Q(n-1) is length of result of (queen-cols n))
|
||||
unraveling this we get: O(Q(n-1)+Q(n-2)+Q(n-3)+...+Q(0))
|
||||
|
||||
Whereas the new function's time looks like this:
|
||||
T(n) = 8*T(n-1) + O(8*Q(n-1))
|
||||
unraveling, we get, very roughly:
|
||||
O(8*Q(n-1) + 8*8*Q(n-2) + ... + 8^8*Q(0))
|
||||
|
||||
I haven't tried to run the exact numbers, because it would involve learning
|
||||
exactly what Q(0), ... ,Q(8) are, which I didn't care to do.
|
Loading…
Add table
Reference in a new issue