;;; The functions program
(define (functions)
(read-line)
(show "Welcome to the FUNCTIONS program.")
(functions-loop))
(define (functions-loop)
(let ((fn-name (get-fn)))
(if (equal? fn-name 'exit)
"Thanks for using FUNCTIONS!"
(let ((args (get-args (arg-count fn-name))))
(if (not (in-domain? args fn-name))
(show "Argument(s) not in domain.")
(show-answer (apply (scheme-function fn-name) args)))
(functions-loop)))))
(define (get-fn)
(display "Function: ")
(let ((line (read-line)))
(cond ((empty? line)
(show "Please type a function!")
(get-fn))
((not (= (count line) 1))
(show "You typed more than one thing! Try again.")
(get-fn))
((not (valid-fn-name? (first line)))
(show "Sorry, that's not a function.")
(get-fn))
(else (first line)))))
(define (get-args n)
(if (= n 0)
'()
(let ((first (get-arg)))
(cons first (get-args (- n 1))))))
(define (get-arg)
(display "Argument: ")
(let ((line (read-line)))
(cond ((empty? line)
(show "Please type an argument!")
(get-arg))
((and (equal? "(" (first (first line)))
(equal? ")" (last (last line))))
(let ((sent (remove-first-paren (remove-last-paren line))))
(if (any-parens? sent)
(begin
(show "Sentences can't have parentheses inside.")
(get-arg))
(map booleanize sent))))
((any-parens? line)
(show "Bad parentheses")
(get-arg))
((empty? (bf line)) (booleanize (first line)))
((member? (first (first line)) "\"'")
(show "No quoting arguments in this program. Try again.")
(get-arg))
(else (show "You typed more than one argument! Try again.")
(get-arg)))))
(define (any-parens? line)
(let ((letters (accumulate word line)))
(or (member? "(" letters)
(member? ")" letters))))
(define (remove-first-paren line)
(if (equal? (first line) "(")
(bf line)
(se (bf (first line)) (bf line))))
(define (remove-last-paren line)
(if (equal? (last line) ")")
(bl line)
(se (bl line) (bl (last line)))))
(define (booleanize x)
(cond ((equal? x "#t") #t)
((equal? x "#f") #f)
(else x)))
(define (show-answer answer)
(newline)
(display "The result is: ")
(if (not answer)
(show "#F")
(show answer))
(newline))
(define (scheme-function fn-name)
(cadr (assoc fn-name *the-functions*)))
(define (arg-count fn-name)
(caddr (assoc fn-name *the-functions*)))
(define (type-predicate fn-name)
(cadddr (assoc fn-name *the-functions*)))
(define (in-domain? args fn-name)
(apply (type-predicate fn-name) args))
;; Type predicates
(define (word-or-sent? x)
(or (word? x) (sentence? x)))
(define (not-empty? x)
(and (word-or-sent? x) (not (empty? x))))
(define (two-numbers? x y)
(and (number? x) (number? y)))
(define (two-reals? x y)
(and (real? x) (real? y)))
(define (two-integers? x y)
(and (integer? x) (integer? y)))
(define (can-divide? x y)
(and (number? x) (number? y) (not (= y 0))))
(define (dividable-integers? x y)
(and (two-integers? x y) (not (= y 0))))
(define (trig-range? x)
(and (number? x) (<= (abs x) 1)))
(define (hof-types-ok? fn-name stuff range-predicate)
(and (valid-fn-name? fn-name)
(= 1 (arg-count fn-name))
(word-or-sent? stuff)
(empty? (keep (lambda (element)
(not ((type-predicate fn-name) element)))
stuff))
(null? (filter (lambda (element)
(not (range-predicate element)))
(map (scheme-function fn-name)
(every (lambda (x) x) stuff))))))
(define (member-types-ok? small big)
(and (word? small)
(or (sentence? big) (= (count small) 1))))
;; Names of functions as functions
(define (named-every fn-name list)
(every (scheme-function fn-name) list))
(define (named-keep fn-name list)
(keep (scheme-function fn-name) list))
(define (valid-fn-name? name)
(assoc name *the-functions*))
;; The list itself
(define *the-functions*
(list (list '* * 2 two-numbers?)
(list '+ + 2 two-numbers?)
(list '- - 2 two-numbers?)
(list '/ / 2 can-divide?)
(list '< < 2 two-reals?)
(list '<= <= 2 two-reals?)
(list '= = 2 two-numbers?)
(list '> > 2 two-reals?)
(list '>= >= 2 two-reals?)
(list 'abs abs 1 real?)
(list 'acos acos 1 trig-range?)
(list 'and (lambda (x y) (and x y)) 2
(lambda (x y) (and (boolean? x) (boolean? y))))
(list 'appearances appearances 2 member-types-ok?)
(list 'asin asin 1 trig-range?)
(list 'atan atan 1 number?)
(list 'bf bf 1 not-empty?)
(list 'bl bl 1 not-empty?)
(list 'butfirst butfirst 1 not-empty?)
(list 'butlast butlast 1 not-empty?)
(list 'ceiling ceiling 1 real?)
(list 'cos cos 1 number?)
(list 'count count 1 word-or-sent?)
(list 'equal? equal? 2 (lambda (x y) #t))
(list 'even? even? 1 integer?)
(list 'every named-every 2
(lambda (fn stuff)
(hof-types-ok? fn stuff word-or-sent?)))
(list 'exit '() 0 '())
; in case user applies number-of-arguments to exit
(list 'exp exp 1 number?)
(list 'expt expt 2
(lambda (x y)
(and (number? x) (number? y)
(or (not (real? x)) (>= x 0) (integer? y)))))
(list 'first first 1 not-empty?)
(list 'floor floor 1 real?)
(list 'gcd gcd 2 two-integers?)
(list 'if (lambda (pred yes no) (if pred yes no)) 3
(lambda (pred yes no) (boolean? pred)))
(list 'item item 2
(lambda (n stuff)
(and (integer? n) (> n 0)
(word-or-sent? stuff) (<= n (count stuff)))))
(list 'keep named-keep 2
(lambda (fn stuff)
(hof-types-ok? fn stuff boolean?)))
(list 'last last 1 not-empty?)
(list 'lcm lcm 2 two-integers?)
(list 'log log 1 (lambda (x) (and (number? x) (not (= x 0)))))
(list 'max max 2 two-reals?)
(list 'member? member? 2 member-types-ok?)
(list 'min min 2 two-reals?)
(list 'modulo modulo 2 dividable-integers?)
(list 'not not 1 boolean?)
(list 'number-of-arguments arg-count 1 valid-fn-name?)
(list 'odd? odd? 1 integer?)
(list 'or (lambda (x y) (or x y)) 2
(lambda (x y) (and (boolean? x) (boolean? y))))
(list 'quotient quotient 2 dividable-integers?)
(list 'random random 1 (lambda (x) (and (integer? x) (> x 0))))
(list 'remainder remainder 2 dividable-integers?)
(list 'round round 1 real?)
(list 'se se 2
(lambda (x y) (and (word-or-sent? x) (word-or-sent? y))))
(list 'sentence sentence 2
(lambda (x y) (and (word-or-sent? x) (word-or-sent? y))))
(list 'sentence? sentence? 1 (lambda (x) #t))
(list 'sin sin 1 number?)
(list 'sqrt sqrt 1 (lambda (x) (and (real? x) (>= x 0))))
(list 'tan tan 1 number?)
(list 'truncate truncate 1 real?)
(list 'vowel?
(lambda (x)
(and (word? x)
(= (count x) 1)
(member? x '(a e i o u))))
1
(lambda (x) #t))
(list 'word word 2 (lambda (x y) (and (word? x) (word? y))))
(list 'word? word? 1 (lambda (x) #t))))