sicp-exercises/ex-2-20.rkt
2025-01-02 17:49:30 +03:00

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)))