; The Little Schemer in Clojure – Chapter 8 – Friends and Relations
; This is the code behind the page here:
; http://j...content-available-to-author-only...e.com/blog/2012/09/07/the-little-schemer-in-clojure-chapter-8-friends-and-relations/
(ns Chapter8FriendsAndRelations)
; Big idea is set operations
(def atom?
(fn [a]
(not (seq? a))))
(def null?
(fn [a]
(or
(nil? a)
(= () a))))
; from Chapter 5
(def member?
(fn [a lat]
(cond
(null? lat) false
true (or
(= (first lat) a)
(member? a (rest lat)))) ))
; assumes implementation of member
(def set_?
(fn [lat]
(cond
(null? lat) true
true (cond
(member? (first lat) (rest lat)) false
true (set_? (rest lat))))))
(println "")
(println "set_?")
(println (set_? '(toasted banana bread with butter for breakfast)))
;//=>true
(println (set_? '(breakfast toasted banana bread with butter for breakfast)))
;//=>false
;simplified set
(def member
(fn [lat]
(cond
(null? lat) true
(member? (first lat) (rest lat)) false
true (set? (rest lat)))))
(println "")
(println "set_? refactored")
(println (set_? '(toasted banana bread with butter for breakfast)))
;//=>true
(println (set_? '(breakfast toasted banana bread with butter for breakfast)))
;//=>false
(def makeset
(fn [lat]
(cond
(null? lat) '()
(member? (first lat) (rest lat)) (makeset (rest lat))
true (cons (first lat) (makeset (rest lat))))))
(println "")
(println "makeset")
(println (makeset '(breakfast toasted banana bread with butter for breakfast)))
;//=> (toasted banana bread with butter for breakfast)
(println (set_? (makeset '(breakfast toasted banana bread with butter for breakfast))))
;//=>true
; now we'll refactor makeset using multirember from Chapter 5
(def multirember
(fn [a lat]
(cond
(null? lat) '()
true (cond
(= (first lat) a) (multirember a (rest lat))
true (cons (first lat) (multirember a (rest lat)))))))
(def makeset
(fn [lat]
(cond
(null? lat) '()
true (cons (first lat) (makeset (multirember (first lat) (rest lat)))))))
(println "")
(println "makeset - refactored with multirember")
(println (makeset '(breakfast toasted banana bread with butter for breakfast)))
;//=> (breakfast toasted banana bread with butter for)
; note other way around
(println (set_? (makeset '(breakfast toasted banana bread with butter for breakfast))))
;//=>true
(def subset?
(fn [set1 set2]
(cond
(null? set1) true
true (cond
(member? (first set1) set2) (subset? (rest set1) set2)
true false))))
(println "")
(println "subset ")
(println (subset? '(banana butter) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(banana butter) '(toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(peanut butter) '(toasted banana bread with butter for breakfast)))
;//=>false
;refactor subset
? to
remove redundant second conditional
(def subset?
(fn [set1 set2]
(cond
(null? set1) true
(member? (first set1) set2) (subset? (rest set1) set2)
true false)))
(println "")
(println "subset - refactored to remove redundant second cond")
(println (subset? '(banana butter) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(banana butter) '(toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(peanut butter) '(toasted banana bread with butter for breakfast)))
;//=>false
; now we'll refactor subset? to better use the final trailing true condition
(def subset?
(fn [set1 set2]
(cond
(null? set1) true
true (and
(member? (first set1) set2)
(subset? (rest set1) set2)))))
(println "")
(println "subset - refactored to use trailing true condition")
(println (subset? '(banana butter) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(banana butter) '(toasted banana bread with butter for breakfast)))
;//=>true
(println (subset? '(peanut butter) '(toasted banana bread with butter for breakfast)))
;//=>false
(def eqset?
(fn [set1 set2]
(cond
(subset? set1 set2) (subset? set2 set1)
true false)))
(println "")
(println "eqset?")
(println (eqset? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>false
(println (eqset? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (subset? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>false
;refactor eqset? to have only one condition line
(def eqset?
(fn [set1 set2]
(cond
true (and
(subset? set1 set2)
(subset? set2 set1)))))
(println "")
(println "eqset? - refactored for only one condition line")
(println (eqset? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>false
(println (eqset? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (subset? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>false
;refactor eqset? to have no condition line
(def eqset?
(fn [set1 set2]
(and
(subset? set1 set2)
(subset? set2 set1))))
(println "")
(println "eqset? - refactored for no condition line")
(println (eqset? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>false
(println (eqset? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (subset? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>false
(def intersect?
(fn [set1 set2]
(cond
(null? set1) false
true (cond
(member? (first set1) set2) true
true (intersect? (rest set1) set2)))))
(println "")
(println "intersect? ")
(println (intersect? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (intersect? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (intersect? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>true
(println (intersect? '(strawberry yoghurt) '(toasted banana bread )))
;//=>false
; now we'll refactor intersect
? to
remove the redundant second cond
(def intersect?
(fn [set1 set2]
(cond
(null? set1) false
(member? (first set1) set2) true
true (intersect? (rest set1) set2))))
(println "")
(println "intersect? refactored to remove redundant second cond")
(println (intersect? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (intersect? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (intersect? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>true
(println (intersect? '(strawberry yoghurt) '(toasted banana bread )))
;//=>false
; now we'll refactor intersect to use or to reduce the number of conditions
(def intersect?
(fn [set1 set2]
(cond
(null? set1) false
true (or
(member? (first set1) set2)
(intersect? (rest set1) set2)))))
(println "")
(println "intersect? refactored to remove redundant second cond")
(println (intersect? '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>true
(println (intersect? '(toasted banana bread) '(toasted banana bread)))
;//=>true
(println (intersect? '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>true
(println (intersect? '(strawberry yoghurt) '(toasted banana bread )))
;//=>false
; now we'll actually extract out the intersection of the sets
(def intersect
(fn [set1 set2]
(cond
(null? set1) '()
(member? (first set1) set2) (cons (first set1) (intersect (rest set1) set2))
true (intersect (rest set1) set2))))
(println "")
(println "intersect")
(println (intersect '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>(toasted banana bread)
(println (intersect '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>(toasted)
(println (intersect '(strawberry yoghurt) '(toasted banana bread )))
;//=>()
;now we'll refactor intersect to focus on filtering out non-members first
(def intersect
(fn [set1 set2]
(cond
(null? set1) '()
(not (member? (first set1) set2)) (intersect (rest set1) set2)
true (cons (first set1) (intersect (rest set1) set2)))))
(println "")
(println "intersect")
(println (intersect '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>(toasted banana bread)
(println (intersect '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>(toasted)
(println (intersect '(strawberry yoghurt) '(toasted banana bread )))
;//=>()
(def union
(fn [set1 set2]
(cond
(null? set1) set2
(member? (first set1) set2) (union (rest set1) set2)
true (cons (first set1) (union (rest set1) set2)))))
(println "")
(println "union")
(println (union '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>(breakfast toasted banana bread with butter for breakfast)
;// note not a set since not given a set
(println (union '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>(peanut butter for breakfast toasted banana bread)
(println (union '(strawberry yoghurt) '(toasted banana bread )))
;//=>(strawberry yoghurt toasted banana bread)
(def complement_
(fn [set1 set2]
(cond
(null? set1) '()
(member? (first set1) set2) (complement_ (rest set1) set2)
true (cons (first set1) (complement_ (rest set1) set2)))))
(println "")
(println "complement_")
(println (complement_ '(toasted banana bread) '(breakfast toasted banana bread with butter for breakfast)))
;//=>()
(println (complement_ '(toasted peanut butter for breakfast) '(toasted banana bread )))
;//=>(peanut butter for breakfast)
(println (complement_ '(strawberry yoghurt) '(toasted banana bread )))
;//=>(strawberry yoghurt)
(def intersect-all
(fn [l-set]
(cond
(null? (rest l-set)) (first l-set)
true (intersect (first l-set) (intersect-all (rest l-set))))))
(println "")
(println "intersect-all")
(println (intersect-all
'(
(toasted banana bread)
(breakfast toasted banana bread with butter for breakfast)
(toasted peanut butter for breakfast)
(toasted banana bread ))))
;//=>(toasted)
;now we'll start with some functions for examining pairs
(def first_
(fn [p]
(cond
true (first p))))
(println "")
(println "first_")
(println (first_ '(a b)))
;//=>a
(def second_
(fn [p]
(cond
true (first (rest p)))))
(println "")
(println "second_")
(println (second_ '(a b)))
;//=>b
(def build
(fn [a b]
(cond
true (cons a (cons b '())))))
(println "")
(println "build")
(println (build 'a 'b))
;//=>(a b)
(def third_
(fn [p]
(cond
true (first (rest (rest p))))))
(println "")
(println "third_")
(println (third_ '(a b c)))
;//=>c
(def member*
(fn [a l]
(cond
(null? l) '()
(atom? (first l))
(or
(= (first l) a)
(member* a (rest l)))
true (or
(member* a (first l))
(member* a (rest l))))))
; now we'll test to see if this is a function
(comment waste of
time if this doesnt
' work (def fun?
(fn [rel]
(println "fun? " rel)
(cond
(null? rel) true
(member* (first_ (first rel)) (rest rel)) false
true (fun? (rest rel)))))
(println "")
(println "fun?")
(println (fun? '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (fun? '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false - not quite correct yet
(println (fun? '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>false - not quite correct yet
)
(def firsts
(fn [l]
(cond
(empty? l) '()
true (cons (first (first l))
(firsts (rest l))))))
(println "")
(println "firsts")
(println (firsts '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>(8 4 7 6 3)
(def fun?
(fn [rel]
(cond
(null? rel) true
(member? (first_ (first rel)) (firsts (rest rel))) false
true (fun? (rest rel)))))
(println "")
(println "fun? - corrections")
(println (fun? '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (fun? '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>true
(println (fun? '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>true
(def fun?
(fn [rel]
(set? (firsts rel))))
(println "")
(println "fun? - refactored to use set? and firsts")
(println (fun? '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (fun? '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>true
(println (fun? '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>true
(def revrel
(fn [rel]
(cond
(null? rel) '()
true (cons
(build
(second_ (first rel))
(first_ (first rel)))
(revrel (rest rel))))))
(println "")
(println "revrel ")
(println (revrel '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>((3 4) (2 4) (6 7) (2 6) (4 3))
(println (revrel '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>((3 8) (2 4) (6 7) (2 6) (4 3))
(println (revrel '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>((3 8) (2 4) (1 7) (0 6) (5 9))
;note there is a question about whether we've introduced seconds here or it was already somewhere earlier...
(def seconds_
(fn [l]
(cond
(null? l) '()
true (cons (first (rest (first l)))
(seconds_ (rest l))))))
(println "")
(println "seconds")
(println (seconds_ '((large burger)(fries coke)(chocolate sundae))))
;//=>(burger coke sundae)
(println (seconds_ '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>(3 2 1 0 5)
(def fullfun?
(fn [fun]
(set_? (seconds_ fun))))
(println "")
(println "fullfun?")
(println (fullfun? '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (fullfun? '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (fullfun? '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>true
(def one-to-one?
(fn [fun]
(fun? (revrel fun))))
(println "")
(println "one-to-one?")
(println (one-to-one? '((4 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (one-to-one? '((8 3)(4 2)(7 6)(6 2)(3 4))))
;//=>false
(println (one-to-one? '((8 3)(4 2)(7 1)(6 0)(9 5))))
;//=>true