168 lines
5.1 KiB
Racket
168 lines
5.1 KiB
Racket
#lang sicp
|
|
;; 2.17 : while I was having fun with this,
|
|
;; I came up with the following monstrosity
|
|
;; yes, it works, and is completely equivalent
|
|
;; the second, less horrible implementation
|
|
(define (last-pair l)
|
|
(or (and (or (null? l) (null? (cdr l))) l)
|
|
(last-pair (cdr l))))
|
|
(define (last-pair2 l)
|
|
(cond
|
|
((null? l) l)
|
|
((null? (cdr l)) l)
|
|
(#t (last-pair2 (cdr l)))))
|
|
|
|
;; 2.18
|
|
(define (reverse l)
|
|
(define (iter acc l)
|
|
(if (null? l)
|
|
acc
|
|
(iter (cons (car l) acc)
|
|
(cdr l))))
|
|
(iter '() l))
|
|
|
|
(equal? '(4 3 2 1) (reverse '(1 2 3 4)))
|
|
|
|
;; 2.19
|
|
|
|
(define (cc amount coin-values)
|
|
(define (first-denomination cv)
|
|
(car cv))
|
|
(define (except-first-denomination cv)
|
|
(cdr cv))
|
|
(define no-more? null?)
|
|
(cond ((= amount 0) 1)
|
|
((or (< amount 0) (no-more? coin-values)) 0)
|
|
(else
|
|
(+ (cc amount
|
|
(except-first-denomination
|
|
coin-values))
|
|
(cc (- amount
|
|
(first-denomination
|
|
coin-values))
|
|
coin-values)))))
|
|
|
|
;; 2.20
|
|
(define (filter f l)
|
|
(cond
|
|
((null? l) l)
|
|
((f (car l)) (cons (car l) (filter f (cdr l))))
|
|
(#t (filter f (cdr l)))))
|
|
(define (same-parity a . b)
|
|
(cons a
|
|
(filter (if (even? a) even? odd?)
|
|
b)))
|
|
(equal? (same-parity 1 2 3 4 5 6) '(1 3 5))
|
|
|
|
;; 2.21
|
|
(define (square x) (* x x))
|
|
(define (square-list-direct items)
|
|
(if (null? items)
|
|
'()
|
|
(cons (square (car items)) (square-list-direct (cdr items)))))
|
|
(define (square-list-map items)
|
|
(map square items))
|
|
|
|
;; 2.22 this exercise asks for an explanation on why the
|
|
;; accumulated result is in reverse order.
|
|
;; This is because, at each iteration, the newest item
|
|
;; is added to the head of the list - always.
|
|
;; this means that the first processed item will be added
|
|
;; to the head, then the second, then the third. So
|
|
;; the list '(1 2 3) will be processed as such:
|
|
|
|
;; '(1 2 3) '()
|
|
;; '(2 3) '(1)
|
|
;; '(3) '(4 1)
|
|
;; '() '(9 4 1)
|
|
|
|
;; So the lists behave like stacks, i.e. FILO ADTs.
|
|
|
|
;; the "solution" proposed by Louis Reasoner
|
|
;; does not work, because that doesn't change the order in
|
|
;; which the items are processed or where the items are
|
|
;; inserted. It only changes the positions of car and cdr,
|
|
;; and in practice this will ruin the structure of the list,
|
|
;; nothing more.
|
|
|
|
;; 2.23
|
|
;; a trivial implementation: (just use map and discard result)
|
|
(define (trivial-for-each f l)
|
|
(map f l)
|
|
#t)
|
|
;; but this isn't much useful, as the memory is still allocated
|
|
;; (if promptly recollected by the GC) for map, defeating the purpose.
|
|
;; Here's an implementation that simply ignores the results:
|
|
(define (my-for-each f l)
|
|
(if (null? l)
|
|
#t
|
|
(begin (f (car l))
|
|
(my-for-each f (cdr l)))))
|
|
;; no consing is done, and this generates an iterative process,
|
|
;; so no stack allocations either. Equivalent to a for-loop in
|
|
;; a language like C or C++.
|
|
|
|
|
|
;; 2.25 - can also be written as: (car (cdaddr l))
|
|
(car (cdr (car (cdr (cdr '(1 3 (5 7) 9))))))
|
|
(car (car '((7))))
|
|
; third one's too long. I'll be using cadr to mean (car (cdr x))
|
|
(cadr (cadr (cadr (cadr (cadr (cadr '(1 (2 (3 (4 (5 (6 7))))))))))))
|
|
|
|
;; 2.26 - doesn't require doing anything, just see what they do lol
|
|
|
|
|
|
;; 2.27
|
|
;; similar to regular reverse, with the addition that
|
|
;; if the current element is itself a list, we cons
|
|
;; its reverse instead.
|
|
;; O(n) time complexity (where n = leaf count)
|
|
;; O(k) space complexity (where k = branch depth? not sure about the correct terminology)
|
|
(define (deep-reverse l)
|
|
(define (iter acc l)
|
|
(cond
|
|
((null? l) acc)
|
|
((list? (car l)) (iter (cons (deep-reverse (car l)) acc)
|
|
(cdr l)))
|
|
(#t (iter (cons (car l) acc)
|
|
(cdr l)))))
|
|
(iter '() l))
|
|
|
|
;; 2.28
|
|
;; ooh, this one's spicy
|
|
;; okay, I think I'll first use a recursive process for this,
|
|
;; then translate it to an iterative one.
|
|
;; Note: iterative process will require a reverse at the end,
|
|
;; unless mutable conses are in play. That's why I used sicp lang for this.
|
|
(define (fringe l)
|
|
(cond
|
|
((null? l) l)
|
|
((list? (car l)) (append (fringe (car l))
|
|
(fringe (cdr l))))
|
|
(#t (cons (car l)
|
|
(fringe (cdr l))))))
|
|
|
|
(define (fringe-iter l)
|
|
"This is mostly the same actually. We just change the mechanism for adding stuff.
|
|
Efficiency-wise, the only difference is that this impl only goes another level
|
|
deep for every level of nested lists (instead of going another level deep *at every iteration*)
|
|
So, better, but still not perfect if you have a tree a billion levels deep.
|
|
|
|
The difference here from a typical iterative process is the mutation: instead
|
|
of using pure functions I just had the iterator keep track of the end of the list."
|
|
(define (add end x)
|
|
(let ((c (cons x '())))
|
|
(set-cdr! end c)
|
|
c))
|
|
(define (iter acc-end l)
|
|
(cond
|
|
((null? l) acc-end)
|
|
((list? (car l)) (iter (iter acc-end (car l))
|
|
(cdr l)))
|
|
(#t (iter (add acc-end (car l))
|
|
(cdr l)))))
|
|
(let ((sentinel (cons #f '())))
|
|
(iter sentinel l)
|
|
(cdr sentinel)))
|
|
|