; The Little Schemer in Clojure – Chapter 7 – Shadows
; This is the code behind the page here:
; http://j...content-available-to-author-only...e.com/blog/2012/08/31/the-little-schemer-in-clojure-chapter-7-shadows/
(ns Chapter7Shadows)
;Big idea is taking another bash at our numeric tower
(def atom?
(fn [a]
(println "atom? " a)
(not (seq? a))))
(def null?
(fn [a]
(or
(nil? a)
(= () a))))
;we need to define number_? to handle true/false
(def number_?
(fn [a]
(println "number_? " a)
(cond
(null? a) false
(number? a) true
true false)))
(println "")
(println "number_?")
(println (number_? 3))
;//=> true
(println (number_? 'a))
;//=> false
; numbered is all about working out whether this S-expression is a
; mathematical operation or just a list
;note we also rely on a scheme and clojure primitive number? (add to list)
(def numbered?
(fn [aexp]
(println "numbered? " aexp)
(cond
(atom? aexp) (number_? aexp)
(= (first (rest aexp)) '+)
(and
(numbered? (first aexp))
(numbered? (first (rest (rest aexp)))))
(= (first (rest aexp)) '*)
(and
(numbered? (first aexp))
(numbered? (first (rest (rest aexp)))))
(= (first (rest aexp)) 'exp) (and
(numbered? (first aexp))
(numbered? (first (rest (rest aexp)))))
true false)))
(println "")
(println "numbered?")
(println (numbered? '(a b c)))
;//=> false
(println (numbered? '(3 + (4 * 5))))
;//=> true
(println (numbered? '(3 a (4 * 5))))
;//=> false
; refactor numbered?
(def numbered?
(fn [aexp]
(cond
(atom? aexp) (number_? aexp)
true (and
(numbered? (first aexp))
(numbered? (first (rest (rest aexp))))))))
(println "")
(println "numbered? refactored")
(println (numbered? '(a b c)))
;//=> false
(println (numbered? '(3 + (4 * 5))))
;//=> true
(println (numbered? '(3 a (4 * 5))))
;//=> true
; This means that you need to categorise your own 'types'
(println (numbered? '(3 a (4 * 5))))
; The eighth commandment -
; Recur on all subparts that are of the same nature
; - On all sublists of a list
; - On all the subexpressions of a representation of an arithmetic expression
;(use 'clojure.math.numeric-tower)
; we can't use this in ideone because it doesn't have the library
; note use of new primitive expt
;before we were just checking the values
;now we're looking at the operators and running those
;note that value or eval - is at the core of the metacircular evaluator
(def value
(fn [aexp]
(cond
(number_? aexp) aexp
(= (first (rest aexp)) '+) (+ (value (first aexp)) (value (first (rest (rest aexp)))))
(= (first (rest aexp)) '*) (* (value (first aexp)) (value (first (rest (rest aexp)))))
;(= (first (rest aexp)) 'exp) (expt
(value
(first aexp
)) (value
(first
(rest
(rest aexp
))))) )))
(println "")
(println "value")
(println (value '(1 + 1)))
;//=> 2
(println (value '(2 + 2)))
;//=> 4
;(println (value '(3 exp 3)))
;//=> 27
(println (value '(4 b 4)))
;//=> nil
;Now we're going to take the function above and move from prefix to infix - just
; to illustrate how trivial the distinction is from an implementation point of view
(def value
(fn [aexp]
(println "value " aexp)
(cond
(number_? aexp) aexp
(= (first aexp) '+) (+ (value (rest aexp)) (value (rest (rest aexp))))
(= (first (rest aexp)) '*) (* (value (first aexp)) (value (first (rest (rest aexp)))))
;(= (first (rest aexp)) 'exp) (expt
(value
(first aexp
)) (value
(first
(rest
(rest aexp
))))) )))
; note that the book includes a deliberate incorrect implementation to
; illustrate a violation of the Eight Commandment
(println "")
;(println (expt 2 3))
;(println "value prefix not infix")
;(println (value '(+ 1 1)))
;(println (value '(* 2 2)))
;(println (value '(exp 3 3)))
;(println (value '(b 4 4)))
;Not testing as deliberately broken
(fn [aexp]
(first (rest aexp))))
(fn [aexp]
(first (rest (rest aexp)))))
(def operator
(fn [aexp]
(first aexp)))
(def value
(fn [aexp]
;(println "value " aexp)
(cond
(number_? aexp) aexp
(= (operator aexp) '+) (+ (value (first-sub-exp aexp)) (value (second-sub-exp aexp)))
(= (operator aexp) '*) (* (value
(first
-sub
-exp aexp
)) (value
(second
-sub
-exp aexp
))) ;(= (operator aexp) 'exp) (expt (value (first-sub-exp aexp)) (value (second-sub-exp aexp)))
)))
(println "")
(println "value prefix not infix - using helper functions")
(println (value '(+ 1 1)))
;//=> 2
(println (value '(* 2 2)))
;//=> 4
;(println (value '(exp 3 3))) ;//=> 27
(println (value '(b 4 4)))
;//=> nil
(println "swapping helper functions from prefix to infix notation")
(def first-sub-exp
(fn [aexp]
(first aexp)))
(def operator
(fn [aexp]
(first (rest aexp))))
(println (value '(1 + 1)))
;//=> 2
(println (value '(2 * 2)))
;//=> 4
;(println (value '(3 exp 3))) ;//=> 27
(println (value '(4 b 4)))
; Time for another Commandment - Use helper functions to abstract from representations
; Part of this is the java concept of eclipse's extract method
; Part of it is the power this gives you to change the function across the whole
; application and have it impact many parts of the
system
; Now we're testing for null?
; Although we had to write our own helper method for this in Chapter 1
; Now as part of implementing the metacircular interpreter - we're re-implementing it in
; terms of other functions so keep our api minimal
;This is what we had
(def null?
(fn [a]
(or
(nil? a)
(= () a))))
;This is what they're proposing
;(def null?
; (fn [s]
; (and
; (atom? s)
; (= s '()))))
; now I think this is dumb - because something can't be an atom and a list
; at the same time
(println "")
(println "null?")
(println (null? '()))
(println (null? ()))
; The benefit of this is that we're reusing our atom? function
; which uses (not (seq? - so push the nil? test to the seq? function
; The disadvantage is that we're more tied to the seq? function when
; we want to bootstrap the
system in itself
; But what's significant about this is that we're going to build up our own number
; representation out of parentheses - admittedly a strange exercise - but going to
; prove that we can do maths in a lisp implemented in itself
; so let's start with zero in a number system based on parentheseses
(def zero_?
(fn [n]
(null? n)))
(println "")
(println "zero_? - new number system")
(println (zero_? '()))
;//=>true
(println (zero_? 0))
;=>false
; note that zero is ()
; now one is (())
; and two is (()())
;now we're going to look at a representation of addition in this system
(def add1
(fn [n]
(cons '() n)))
(println "")
(println "add1")
(println (add1 '()))
;//=>(()) ie 1
(println (add1 '(())))
;//=> (() ()) ie 2
(def sub1
(fn [n]
(rest n)))
; note that subtracting 1 from zero will remain zero
(println "")
(println "sub1")
(println (sub1 '(()())))
;//=>(()) ie 1
(println (sub1 '(())))
;//=>() ie 0
(println (sub1 '()))
;//=> () unfortunately with our implementation sub1 of zero returns zero
; doesn't seem right - always returning a single set of parentheses
(def +_
(fn [n m]
(cond
(zero_? m) n
true (add1 (+_ n (sub1 m))))))
(println "")
(println "+_")
(println (+_ '() '()))
;//=> ()
;// zero plus zero is zero
(println (+_ '(()) '(())))
;//=> (()()) ie 1 plus 1 is 2
(def number_?
(fn [n]
(cond
(null? n) true
true (and
(null? (first n))
(number_? (rest n))))))
(println "")
(println "number_?")
(println (number_? '() ))
;//=>true
(println (number_? '(()) ))
;//=>true
(println (number_? '((())) ))
;//=>false
(println (number_? '(()()) ))
;//=>true
(println "done")