CPL 2016, week 11 Clojure immutable data structures Oleg Batrashev - - PowerPoint PPT Presentation
CPL 2016, week 11 Clojure immutable data structures Oleg Batrashev - - PowerPoint PPT Presentation
CPL 2016, week 11 Clojure immutable data structures Oleg Batrashev Institute of Computer Science, Tartu, Estonia April 25, 2016 Overview Last week Clojure language core This week Immutable data structures Next weeks Clojure
Overview
Last week
◮ Clojure language core
This week
◮ Immutable data structures
Next weeks
◮ Clojure simple state and design ◮ Software transactional memory ◮ Agents in Clojure
Immutable data structures 27/74
- Trees
◮ (defrecord tree [value left right]) – Java class with 3
immutable fields
◮ Having (tree. 3 :leaf (tree. 33 :leaf :leaf))
◮ want to replace 3 with 45
tree 3 leaf tree 33 leaf leaf
Immutable data structures 27/74
- Trees
◮ (defrecord tree [value left right]) – Java class with 3
immutable fields
◮ Having (tree. 3 :leaf (tree. 33 :leaf :leaf))
◮ want to replace 3 with 45
tree 3 leaf tree 33 leaf leaf tree 45
◮ need to create new node and point to the subtrees ◮ in general, path from root to node must be reconstructed ◮ whoever references (part of) the tree never sees any changes
Immutable data structures 28/74
- Immutable data structures
◮ “Purely functional data structures” (book)
◮ immutable – whoever has reference never sees any changes ◮ structural sharing – old instance shares data with the new one ◮ assymptotically (almost) as efficient as transient
implementations
◮ O(log32 n) for unsorted collections (vector,hash-map,hash-set) ◮ O(log2 n) for sorted collections (sorted-map,sorted-set) ◮ in Clojure referred as persistent collections
◮ transient counterparts
◮ handy for local,temporary operations, like StringBuffer
Immutable data structures 29/74
- Benefits
◮ may be safely
◮ passed to functions ◮ used as keys in maps and entries in sets
◮ enabling concurrency
◮ sharing collections between threads is safe
◮ free versioning
◮ old versions of the instance are untouched – may store them to
a sequence and use for undo
Immutable data structures 30/74 Binary Tree -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 31/74 Binary Tree -
Definition
◮ A node in binary tree can be either
◮ a leaf – :leaf ◮ an internal node – with left and right subtrees
(defrecord Node [value left right ]) ◮ Ex: (Node. 15 :leaf (Node. 33 :leaf :leaf))
tree 15 leaf tree 33 leaf leaf
◮ More compactly
15 33
Immutable data structures 32/74 Binary Tree -
Depth-first traversal
◮ Traverse left subtree then right subtree (defn dft [node] (if (= node :leaf) (println :leaf) (do (println (. value node )) (dft (. left node )) (dft (. right node ))))) ◮ by itself not specifically useful
◮ add accumulator
Immutable data structures 33/74 Binary Tree -
DFT with accumulator
(defn - dft -acc -impl [node acc] (if (= node :leaf) acc (let [lAcc (dft -acc -impl (. left node) acc) cAcc (cons (. value node) lAcc) rAcc (dft -acc -impl (. right node) cAcc )] rAcc ))) (defn dft -acc [node] (reverse (dft -acc -impl node '()))) ◮ List accumulator
◮ grow from list head ◮ reverse at the end
Immutable data structures 34/74 Binary Tree -
Breadth-first traversal
- 1. put tree root node to a queue
- 2. iteratively
2.1 take tree node from the queue 2.2 process node 2.3 put child nodes to the queue
◮ queue could be immutable (see below)
Immutable data structures 35/74 Working with immutable data -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 36/74 Working with immutable data -
Accessing
◮ Having collection/object like (def d {:somekey 42}) access (d :somekey) (: somekey d) (. somekey d) (get d :somekey) (get -in d [: somekey ])
- 1. works for lists and maps
- 2. works for maps and records, may return nil
- 3. works for records/types/objects, may throw
NullPointerException
- 4. works for lists, maps and records
- 5. multilevel get, may specify list of keys for several levels
Immutable data structures 37/74 Working with immutable data -
“Update” immutable state
(assoc d :newkey 43 :key3 44) (conj d [: newkey 43]) (into d {: newkey 43}) (assoc -in d [: newkey 2 3] 43) ; -> {: somekey 42, :newkey {2 {3 43}}}
- 1. assign new values, works with lists, maps, and records
- 2. update elements, works with lists, maps
- 3. update several elements (from another collection)
- 4. multilevel update, works with maps and records
All of them return new object, because you are working on immutable structures!
Immutable data structures 38/74 Binary search tree -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 39/74 Binary search tree -
Definition
◮ binary search tree (ordered binary tree)
◮ values in ◮ left subtree are smaller ◮ right subtree are larger
15 6 3 8 33
Immutable data structures 40/74 Binary search tree -
Lookup
(defn lookup [node v] (if (= node :leaf) :not -found (let [nodeV (. value node )] (cond (= v nodeV) v (< v nodeV) (lookup (. left node) v) (> v nodeV) (lookup (. right node) v))))) ◮ this is set, should have key&value to act as map
Immutable data structures 41/74 Binary search tree -
Insertion
(defn insert -1 [node v] (if (= :leaf node) (Node. v :leaf :leaf) (let [{nv :value nl :left nr :right} node] (if (< v nv) (Node. nv (insert -1 nl v) nr) (Node. nv nl (insert -1 nr v)) ))))
15 6 3 8 33
Immutable data structures 41/74 Binary search tree -
Insertion
(defn insert -1 [node v] (if (= :leaf node) (Node. v :leaf :leaf) (let [{nv :value nl :left nr :right} node] (if (< v nv) (Node. nv (insert -1 nl v) nr) (Node. nv nl (insert -1 nr v)) ))))
15 6 3 8 7 33
Immutable data structures 42/74 Binary search tree -
Insertion (2)
◮ Record may have many fields – we want to redefine one ◮ Record acts as a type (later) and as a dict
◮ use assoc as for dictionary ◮ record fields accessed by Keyword keys
(defn insert [node v] (if (= :leaf node) (Node. v :leaf :leaf) (if (< v (. value node )) (assoc node :left (insert (. left node) v)) (assoc node :right (insert (: right node) v)))))
Immutable data structures 43/74 Binary search tree -
Deletion (1)
(defn remove -smallest [node] ;; Remove the smallest value from the tree and ;; return it with the node root. (when node (let [[Yp Tp] (remove -smallest (. left node ))] (if Yp [Yp (assoc node :left Tp)] [(. value node) (: right node )]))))
Y T1 T2 ? T1 T2 Yp Yp T1 Tp Yp
Immutable data structures 44/74 Binary search tree -
Deletion (2)
- 1. find v in tree
- 2. cut nv2 (smallest) from right subtree nr
- 3. put nv2 and nr2 into the place of node
(defn delete [{nv :value , nl :left , nr :right :as node} v] (cond (nil? node) nil ;; recurse into left/right subtree , reconstruct new tree (< v nv) (Node. nv (delete nl v) nr) (> v nv) (Node. nv nl (delete nr v)) ;; found the nvalue , return it and cut the right subtree (= v nv) (let [[ nv2 nr2] (remove -smallest nr)] (if -not (nil? nv2) ; is right subtree non -empty? (Node. nv2 nl nr2) nl)) ))
Immutable data structures 45/74 Balanced BST -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 46/74 Balanced BST -
Overview
◮ Balanced binary search tree
◮ binary search tree ◮ complexities ◮ lookup O(log n) ◮ insertion O(log n) ◮ deletion O(log n) ◮ iterate over elements O(n)
◮ Variants
◮ Red-black tree ◮ AVL tree ◮ others
Immutable data structures 47/74 Balanced BST -
Red-black tree: definition
- 1. A node is either red or black
- 2. Root node is black
- 3. All leaves are black
- 4. Both children of every red node are black
- 5. Same number of black nodes in each path from the root to a
leaf 15 6 3 8 33 Property
◮ the longest path is less than 2 times the shortest path
◮ hint: shortest must be all blacks
Immutable data structures 48/74 Balanced BST -
Rotate
◮ left and right rotation if two nodes are red
◮ assume changes are made in A (B)
◮ right rotation
◮ if U and root of A (root of B) are both red ◮ change the color of the root A (root of B) to black ◮ preserves ordering
V U A B C U V A B C right rotate left rotate
Immutable data structures 49/74 Balanced BST -
Rotate right: code
(defn - rotate -right -if -reds [{U :left C :right :as V}] (let [A (: left U) B (: right U)] (cond (or ; two cases when need to rotate (and (= (: color U) :red) (= (: color A) :red )) (and (= (: color U) :red) (= (: color B) :red ))) (assoc U :left (assoc A :color :black) :right (assoc V :left B)) :true V ; else do not rotate )))
Immutable data structures 50/74 Balanced BST -
Rotate left: code
(defn - rotate -left -if -reds [{A :left V :right :as U}] (let [B (: left V) C (: right V)] (cond (or ; two cases when need to rotate (and (= (: color V) :red) (= (: color B) :red )) (and (= (: color V) :red) (= (: color C) :red ))) (assoc V :left (assoc U :right B) :right (assoc C :color :black )) :true U ; else do not rotate )))
Immutable data structures 51/74 Balanced BST -
Insertion (1)
◮ insert red node as in binary search tree
◮ rotate if somewhere appear two red nodes
◮ leave black in the root (may appear red as the result of
rotation)
(defn insert [v T] (let [T2 (insert -and -rotate v T)] (assoc T2 :color :black )))
Immutable data structures 52/74 Balanced BST -
Insertion (2)
◮ insert here if in leaf or found the location ◮ otherwise, insert new value into the left or right subtree
◮ try to rotate right or left correspondingly
(defn - insert -and -rotate [v T] (cond (nil? T) (Node. :red v nil nil) (= v (. value T)) T (< v (. value T)) (let [T2 (insert -and -rotate v (. left T))] (rotate -right -if -reds (assoc T :left T2 ))) (> v (. value T)) (let [T2 (insert -and -rotate v (. right T))] (rotate -left -if -reds (assoc T :right T2 ))) ))
Immutable data structures 53/74 Other algorithms -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 54/74 Other algorithms -
Queue (1)
◮ queue may be represented as list, e.g. 1|4|5|nil
◮ adding or removing from head is easy and cheap ◮ adding or removing from tail requires full list copy
◮ alternative: queue with 2 lists
◮ Backward list is used to prepend elements to it ◮ Forward list is used to take elements from it ◮ queue = FList + reversed BList ◮ if forward list is empty it is replaced with reversed backward list
Immutable data structures 55/74 Other algorithms -
Queue (2)
fun {New} queue(nil nil) end fun {IsEmpty Q} queue(FList BList) = Q in FList==nil andthen BList==nil end % append element to the queue proc {Append Q X ?Qn} queue(FList BList) = Q in Qn = queue(FList X|BList) end
Immutable data structures 56/74 Other algorithms -
Queue (3)
% take element from the queue proc {Take Q ?X ?Qn} queue(FList BList) = Q in if FList\=nil then X = FList.1 Qn = queue(FList.2 BList) elseif BList\=nil then RBList in % reverse RBList = {Reverse BList} X = RBList.1 Qn = queue(RBList.2 nil) else raise app("List is empty") end end end
Immutable data structures 57/74 Other algorithms -
Queue (4)
◮ new reference is created for the updated queue
% append elements Qu = {New} Qu2 = {Append Qu 3} Qu3 = {Append Qu2 1} Qu4 = {Append Qu3 2} {System.show Qu4} % take element X Qu5 = {Take Qu4 X} {System.show X} {System.show Qu5}
◮ easy undo
◮ store old references: immutable data structures make it
possible (same for trees!)
Immutable data structures 58/74 Other algorithms -
Graphs
declarative model (as compared to non-declarative)
◮ traversing graph is possible
◮ complexity (usually) multiple of log n ◮ unless graph node ids can be stored in array
◮ changing graph may require copying the whole graph
◮ because of immutable data ◮ node references ◮ same problem for double linked list
Immutable data structures 59/74 Persistent vector and map -
Outline
Immutable data structures Binary Tree Working with immutable data Binary search tree Balanced BST Other algorithms Persistent vector and map
Immutable data structures 60/74 Persistent vector and map -
Persistent vector
◮ http://en.wikipedia.org/wiki/Trie ◮ see (link) Understanding Clojure’s PersistentVector
implementation
◮ tree with 32 children, elements stored from leftmost to the
right
◮ 2-level may store 322 = 1024 elements ◮ 6-level may store 326 = 230 elements
◮ expands as necessary – adding to the end is cheap ◮ tail data optimization – store tail to separate buffer until
exceeds 32 elements
Immutable data structures 61/74 Persistent vector and map -