Skip to content

Latest commit

 

History

History
382 lines (282 loc) · 9.21 KB

trees.org

File metadata and controls

382 lines (282 loc) · 9.21 KB

Functional programming on Trees

Tree

The binary tree consists of nodes each with a number key, and two child subtrees called left-parent and right-parent.

(require eopl)

(define-datatype tree tree?
  [leaf (key number?)]
  [node (key number?) (left-parent tree?) (right-parent tree?)])

A sample tree using the data structure can be as follows. Here, it has 2 leaf nodes (1, 2), and 1 parent node (3).

(define node-1
  (leaf 1))
(define node-2
  (leaf 2))
(define root
  (node 3 node-1 node-2))

Map

This function tree/map returns a new tree by applying unary function f to each node of tree tr. The base case is a tree with a single leaf, in which case it applies f to its key. In the recursive case, the tree is a node with 2 subtrees, so it maps the 2 substrees and applies f to its key.

tree is leaf? -> leaf(f(key))
tree is node? -> node(f(key), map(left), map (right))
; (tree/map f tr): F X TR -> TR
; returns a new tree by applying each node to tr
(define tree/map
  (lambda (f tr)
    (cases tree tr
      (leaf (key)
            (leaf (f key)))
      (node (key left-parent right-parent)
            (node (f key) (tree/map f left-parent) (tree/map f right-parent))))))

Reduce

This function tree/reduce reduces a tree of values to a single value by applying binary function f to each node of the tree and the reduced value of its subtree. It takes an initial value init and the tree itself tr.

tree is leaf? -> leaf(f(key, init))
tree is node? -> node(f(key, f(map(left), map(right))))
; (tree/reduce f init tr): F X V X TR -> V
; reduces tree of values to a single value
(define tree/reduce
  (lambda (f init tr)
    (cases tree tr
      (leaf (key)
            (f key init))
      (node (key left-parent right-parent)
            (f key (f (tree/reduce f init left-parent) (tree/reduce f init right-parent)))))))

(define treeduce tree/reduce)
(define reduce tree/reduce)

Filter

This function tree/filter returns a tree with only those nodes, who themselves as well as all their parents satisfy the function f, applied to their key.

if a node's key does not satisfy f, both its subtrees are removed and its key set to 0
if a leaf's key does not satisfy f, its key is set to 0
; (tree/filter f tr): F X TR -> TR
; filter part of tree which satisfies f
(define tree/filter
  (lambda (f tr)
    (cases tree tr
      (leaf (key)
            (if (f key) (leaf key) (leaf 0)))
      (node (key left-parent right-parent)
            (if (f key) (node key (tree/filter f left-parent) (tree/filter f right-parent)) (leaf 0))))))

Path

This function tree/path returns a list of lefts, rights showing a path to n in tree tr. It returns #f if no such number is found.

in case of leaf, key=n? -> () else #f
in case of node, key=n? -> (), in left subtree -> (left subtree), ...right, else #f
; (tree/path n tr): N X TR -> L
; returns list of lefts, rights showing path to n in tree tr, #f if not found
(define tree/path
  (lambda (n tr)
    (cases tree tr
      (leaf (key)
            (if (= key n) (list) #f))
      (node (key left-parent right-parent)
            (cond
              [(= key n) (list)]
              [(tree/path n left-parent) (cons `left (tree/path n left-parent))]
              [(tree/path n right-parent) (cons `right (tree/path n right-parent))]
              [else #f])))))

(define path tree/path)

List

Reverse

This function is supposed to be able to reverse a list using reduce for lists. Let a basic reduce implementation for list reduce be as follows:

(base case) lst=null? -> init
(indc case) else      -> f(lst[0], reduce(lst[1..end]))
; (list/reduce f init lst): F X V X L -> V
; reduces list of values to a single value
(define list/reduce
  (lambda (f init lst)
    (if (null? lst)
        init
        (f (car lst) (list/reduce f init (cdr lst))))))

How can list/reduce be used to implement list/reverse? Even though the reduce function picks the elements of the list from the end one by one, using cons simply results back into the original list. If instead of plain cons, the picked value is somehow appended to the end of the working list, the result should be a reversed list.

reduce with plain cons
lst=(1 2 3 `4)  acc=(4)
lst=(1 2 `3 4)  acc=(3 4)
lst=(1 `2 3 4)  acc=(2 3 4)
reduce with append
lst=(1 2 3 `4)  acc=(4)
lst=(1 2 `3 4)  acc=(4 3)
lst=(1 `2 3 4)  acc=(4 3 2)

Say, list append is a function which takes a number n, and a list lst and appends the value n to the end of the list. Think of this as applying reduce with cons to a list which only contains n.

construct using each value of list to the value as list
; (list/append n lst): N X L -> L
; appends a value to end of list
(define list/append
  (lambda (n lst)
    (list/reduce cons (list n) lst)))

Now this append function can be used in place of “plain” cons with reduce and it would be able to reverse the list.

for each value from the end, append it to the list
; (list/reverse lst): L -> L
; reverses the order of elements in a list
(define list/reverse
  (lambda (lst)
    (list/reduce list/append (list) lst)))

(define reverse list/reverse)

Number elements

The objective here is to write a procedure g such that number-elements could be defined as:

(define number-elements
  (lambda (lst)
    (if (null? lst)
        '()
        (g (list 0 (car lst))
            (number-elements (cdr lst))))))

It appears that g is behaving as a cons function as well as incrementing the first value of each pair in list. Let there be a function to increment the first value of a pair.

; (pair/add1 p): P -> P
; increments first value of pair only
(define pair/add1
  (lambda (p)
    (cons (add1 (car p)) (cdr p))))

A list map function can be used to apply pair/add1 to every pair in the list.

; (list/map f lst): F X L -> L
; applies a function to every element of list
(define list/map
  (lambda (f lst)
    (if (null? lst)
        (list)
        (cons (f (car lst)) (list/map f (cdr lst))))))

The above map function can be used to increment the first value of all pairs in the list, and cons the desired value to it finally. This would enable element numbering.

; (g el lst): E X L -> L
; increment 1st value of all pairs, and return (el . lst)
(define g
  (lambda (el lst)
    (cons el (list/map pair/add1 lst))))

Bubble sort

The objective here is to write the bubble sort algorithm without using any assignment. To begin with, lets make sure that the list has atleast 2 elements (or the inverted case, atmost 1 element).

; (atmost1? lst): L -> B
; return #t if list has atmost 1 element(s).
(define atmost1?
  (lambda (lst)
    (or (null? lst) (null? (cdr lst)))))

Now lets use the desired swap function to swap the first 2 elements of the list.

; (swap lst): L -> L
; swaps the first two elements of list
(define swap
  (lambda (lst)
    (if (atmost1? lst)
        lst
        (cons (cadr lst) (cons (car lst) (cddr lst))))))
; (swap-by lst f): L X F -> L
; swaps the first 2 elements of list using given function
(define swap-by
  (lambda (lst f)
    (if (or (atmost1? lst) (f (car lst) (cadr lst)))
        lst
        (swap lst))))

Now that we have the ability to conditionally swap two elements in a list, lets write a single pass on bubble sort through the list. This would have the effect of bringing the largest value to the bottom of the list, in case of an ascending sort order.

; (bubble-once-by lst f): L X F -> L
; runs a single pass of bubble sort on list
(define bubble-once-by
  (lambda (lst f)
    (if (atmost1? lst)
        lst
        (let ([lst (swap-by lst f)])
          (cons (car lst) (bubble-once-by (cdr lst) f))))))

Finally, multiple bubblings lead to a completely sorted list.

; (bubble-sort-by lst f): L X F -> L
; bubble sorts a list with given predicate f
(define bubble-sort-by
  (lambda (lst f)
    (if (atmost1? lst)
        lst
        (bubble-once-by (cons (car lst) (bubble-sort-by (cdr lst) f)) f))))

; (bubble-sort lst): L -> L
; bubble sorts a list in ascending order
(define bubble-sort
  (lambda (lst)
    (bubble-sort-by lst <=)))

This is where you put it all together

<<tree_defn>>

<<tree_ex>>

<<tree_map>>

<<tree_reduce>>

<<tree_filter>>

<<tree_path>>



<<list_reduce>>

<<list_append>>

<<list_reverse>>



<<pair_add1>>

<<list_map>>

<<g>>



<<atmost1>>

<<swap>>

<<swap_by>>

<<bubble_once_by>>

<<bubble_sort>>