;;; Problem 1-4. Sequences and Higher-order Functions. (set 1> "1>") (set 1= "1=") ; Hack, hack. ;;; 1-4-a NEW-APPEND and NEW-REVERSE. ;;; (NEW-APPEND s1 s2) appends the sequences S1 and S2 together. (define NEW-APPEND (lambda [seq1 seq2] (if (null seq1) seq2 (cons (first seq1) (new-append (rest seq1) seq2))))) ; Tests: 1> (new-append [1 2 3] [4 5 6]) 1= [1 2 3 4 5 6] 1> (new-append [] [1]) 1= [1] 1> (new-append [1] []) 1= [1] ;;; (NEW-REVERSE s) reverses the sequence S. (define NEW-REVERSE (lambda [seq] (if (null seq) seq (append (new-reverse (rest seq)) (list (first seq)))))) ; Tests: 1> (new-reverse []) 1= [] 1> (new-reverse [1 2 3 4]) 1= [4 3 2 1] ;;; 1-4-b The lunatic FRINGE. ;;; (FRINGE s) returns a simple sequence of numbers at the ;;; leaves of S, a sequence of numbers and sequences. ; Version 1: (define FRINGE (lambda [seq] (cond [(null seq) []] [(number (first seq)) (cons (first seq) (fringe (rest seq)))] [$true (append (fringe (first seq)) (fringe (rest seq)))]))) ; Tests: 1> (fringe []) 1= [] 1> (fringe [1 2 3]) 1= [1 2 3] 1> (fringe [[[1]]]) 1= [1] 1> (fringe [1 [2] [[3]]]) 1= [1 2 3] ; Version 2: Generalize FRINGE slightly by decreeing ; that FRINGE of a number is the sequence containing that ; number. This permits the following slightly simpler ; formulation. (define FRINGE-2 (lambda [x] (cond [(number x) [x]] [(null x) []] [$true (append (fringe-2 (first x)) (fringe-2 (rest x)))]))) ; Tests: 1> (fringe-2 10) 1= [10] 1> (fringe-2 []) 1= [] 1> (fringe-2 [1 2 3]) 1= [1 2 3] 1> (fringe-2 [[[1]]]) 1= [1] 1> (fringe-2 [1 [2] [[3]]]) 1= [1 2 3] ;;; 1-4-c Objectified arguments. ; Examples from the preamble to 1-4-c: 1> (+ . (rest (rest [1 2 3 4]))) 1= 7 1> (let [[x [10 20 30]]] (+ . (rest x))) 1= 50 1> (** 2 3) 1= 8 1> (let [[x [2 3]]] (** . x)) 1= 8 1> (let [[x [2 3]]] (** . (reverse x))) 1= 9 (define TEST (lambda args args)) 1> (test 1) 1= [1] 1> (test (+ 1 1) (+ 2 2)) 1= [2 4] 1> (test . (* 3 3)) 1= 9 ;;; (HOW-MANY a1 a2 ... an) returns the number or arguments ;;; passed in. (define HOW-MANY (lambda args (if (sequence args) (length args) (error "objectified arguments" ^args)))) ; Tests: 1> (how-many) 1= 0 1> (how-many 10 20 30) 1= 3 1> (how-many (+ 2 2)) 1= 1 1> (how-many . 1) *** Error: objectified arguments Error structure: (error "objectified arguments" ^args) Datum 1: 1 ;;; 1-4-d Multi-argument version of +. ;;; (+! n1 n2 n3 ...) designates the sum of the numbers ;;; N1, N2, etc. For this version, there must be at least ;;; 2 arguments. (define +! (lambda args (if (= (length args) 2) (+ . args) (+ (first args) (+! . (rest args)))))) ; Tests: 1> (+! 2 3) 1= 5 1> (+! 1 2 3 4 5) 1= 15 ;;; 1-4-e Multi-argument version of *. ;;; (*! n1 n2 n3 ...) designates the product of the numbers ;;; N1, N2, etc. For this version, there must be at least ;;; 2 arguments. (define *! (lambda args (if (= (length args) 2) (* . args) (* (first args) (*! . (rest args)))))) ; Tests: 1> (*! 2 3) 1= 6 1> (*! 1 2 3 4 5) 1= 120 ;;; Do multi-argument versions of subtraction and division ;;; make sense? "Sense" could be made out of them (indeed APL ;;; does so), but its a much trickier matter dealing with ;;; non-associative and non-comutative operators. ; Correction: The problem set uses the term "fence-post error" ; in the context of a discussion of the more general class of ; errors that arise when dealing with limiting cases. The term ; normally refers to the subclass of "off by one errors" exemplified ; by the math problem "You have just purchased 100 feet of fencing ; material that you are going to attach to fence posts uniformly ; spaced 10 feet apart. How many fence posts will you going to ; need to build a 100 foot straight fence? [Think quickly.] ; What if you were building a square fence to enclose a yard? ; A triangular fence?" ;;; 1-4-f Multi-argument + and * revisited. ;;; The above definitions of +! and !* blow up if given no ;;; arguments. The natural extensions have (+!) nd (*!) returning ;;; 0 and 1, respectively. Here are the revised definitions: ;;; (+! n1 n2 n3 ...) designates the sum of the numbers ;;; N1, N2, etc. Any number of arguments are permitted. (define +! (lambda args (if (null args) 0 (+ (first args) (+! . (rest args)))))) ; Tests: 1> (+!) 1= 0 1> (+! 100) 1= 100 1> (+! 2 3) 1= 5 1> (+! 1 2 3 4 5) 1= 15 ;;; (*! n1 n2 n3 ...) designates the product of the numbers ;;; N1, N2, etc. Any number of arguments are permitted. (define *! (lambda args (if (null args) 1 (* (first args) (*! . (rest args)))))) ; Tests: 1> (*!) 1= 1 1> (*! 66) 1= 66 1> (*! 2 3) 1= 6 1> (*! 1 2 3 4 5) 1= 120 ;;; (MAXIMUM! n1 n2 n3 ...) designates the maximum of the numbers ;;; N1, N2, etc. There must be at least one argument. (define MAXIMUM! (lambda args (if (null (rest args)) (first args) (maximum (first args) (maximum! . (rest args)))))) (define MAXIMUM (lambda [n1 n2] (if (< n1 n2) n2 n1))) ; Tests: 1> (maximum! 1) 1= 1 1> (maximum! 1 2 3) 1= 3 1> (maximum! 3 2 1) 1= 3 ; (MAXIMUM!) would make sense if we had a designator for ; negative infinity (something smaller than all numbers). ; This is how APL handles it. ;;; 1-4-g Objectified arguments. ;;; Yes. It is quite simple to define a multi-argument ;;; addition procedure without using objectified arguments. ;;; To crucial capability is to be able to define a ;;; procedure that can *accept* an arbitrary number of ;;; arguments. (letrec [[sum-sequence (lambda [seq] (if (null seq) 0 (+ (first seq) (sum-sequence (rest seq)))))]] (define +! (lambda args (sum-sequence args)))) ; Tests: 1> (+!) 1= 0 1> (+! 100) 1= 100 1> (+! 2 3) 1= 5 1> (+! 1 2 3 4 5) 1= 15 ;;; 1-4-h and 1-4-i MAP. ;;; Here is the definition of MAP that 3-Lisp actually uses: (define map (letrec [[map-1-sequence (lambda [fn seq] (if (null seq) seq (cons (fn (first seq)) (map-1-sequence fn (rest seq)))))] [map-all-sequences (lambda [fn seqs] (if (null (first seqs)) (first seqs) (cons (fn . (map-1-sequence first seqs)) (map-all-sequences fn (map-1-sequence rest seqs)))))]] (lambda args (map-all-sequences (first args) (rest args))))) ; Tests: 1> (map +! [1 2 3]) 1= [1 2 3] 1> (map +! [1] [2] [3]) 1= [6] 1> (map +! []) 1= [] 1> (map +! [1 2 3] [4 5 6] [7 8 9]) 1= [12 15 18] 1> (map +!) *** Error: non-null sequence (or rail) expected Error structure: (first seqs) Datum 1: {first closure} Datum 2: [[]] ;;; It's difficult to ascribe any reasonable behaviour to ;;; (map !+). ;;; 1-4-j SPEAD ;;; ((SPEAD fn) e1 e2 ... en) is roughly the same as ;;; (fn e1 (f2 e2 ... (fn en-1 en)...)). FN must designate ;;; a function of two argument. The resulting function will ;;; accept two or more arguments. (define SPREAD (lambda [fn] (lambda args (if (= (length args) 2) (fn . args) (fn (first args) ((spread fn) . (rest args))))))) ; Tests: 1> ((spread +) 1 2 3) 1= 6 1> ((spread *) 4 5) 1= 20 ; ((SPREAD +)) is an error. ;;; (SPREAD fn identity) is an improved version of SPREAD. (define SPREAD (lambda [fn identity] (letrec [[spreader (lambda args (if (null args) identity (fn (first args) (spreader . (rest args)))))]] spreader))) (define +! (spread + 0)) (define *! (spread * 1)) ; Tests: 1> (+!) 1= 0 1> (+! 1 2 3 4 5) 1= 15 1> (*!) 1= 1 1> (*! 1 2 3 4 5) 1= 120 ;;; 1-4-k APPEND! (define NEW-LIST (spread cons [])) (define APPEND! (spread append [])) (define CONSTANT (lambda [constant-value] (lambda args constant-value))) (define NEW-LENGTH (lambda [seq] ((spread + 0) . (map (constant 1) seq)))) ; Tests: 1> (new-list 1 2 3) 1= [1 2 3] 1> (new-list) 1= [] 1> (append! [] [] []) 1= [] 1> (append! [1 2 3] [4 5 6]) 1= [1 2 3 4 5 6] 1> (append!) 1= [] 1> ((constant 100) 200) 1= 100 1> (new-length []) 1= 0 1> (new-length [10 20 30]) 1= 3 ;;; End of solution to 1-4.