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