SlideShare a Scribd company logo
1 of 40
Purely Functional
Data Structures in Scala
Vladimir Kostyukov
http://vkostyukov.ru
Agenda
• Immutability & Persistence
• Singly-Linked List
• Banker’s Queue
• Binary Search Tree
• Balanced BST: Red-Black Tree
• Scala support of these things
• Patricia Trie
• Hash Array Mapped Trie
2
Immutability & Persistence
Two problems:
• FP paradigm doesn’t support destructive updates
• FP paradigm expects both the old and new
versions of DS will be available after update
Two solutions:
• Immutable objects aren’t changeable
• Persistent objects support multiple versions
3
Singly-Linked List
4
35 7
Cons
Nil
abstract sealed class List {
def head: Int
def tail: List
def isEmpty: Boolean
}
case object Nil extends List {
def head: Int = fail("Empty list.")
def tail: List = fail("Empty list.")
def isEmpty: Boolean = true
}
case class Cons(head: Int, tail: List = Nil) extends List {
def isEmpty: Boolean = false
}
List: analysis
5
35 7A =
B = Cons(9, A) = 9
C = Cons(1, Cons(8, B)) = 1 8
structural sharing
/**
* Time - O(1)
* Space - O(1)
*/
def prepend(x: Int): List = Cons(x, this)
/**
* Time - O(n)
* Space - O(n)
*/
def append(x: Int): List =
if (isEmpty) Cons(x)
else Cons(head, tail.append(x))
List: append & prepend
6
35 79
35 7 9
List: apply
7
35 7 42 6
n - 1
/**
* Time - O(n)
* Space - O(n)
*/
def apply(n: Int): A =
if (isEmpty) fail("Index out of bounds.")
else if (n == 0) head
else tail(n - 1) // or tail.apply(n - 1)
List: concat
8
path copying
A = 42 6
B = 35 7
C = A.concat(B) = 42 6
/**
* Time - O(n)
* Space - O(n)
*/
def concat(xs: List): List =
if (isEmpty) xs
else tail.concat(xs).prepend(head)
List: reverse (two approaches)
9
42 6 46 2reverse( ) =
def reverse: List =
if (isEmpty) Nil
else tail.reverse.append(head)
, or tail recursion in O(n)
The straightforward solution in O(n2)
def reverse: List = {
@tailrec
def loop(s: List, d: List): List =
if (s.isEmpty) d
else loop(s.tail, d.prepend(s.head))
loop(this, Nil)
}
List performance
10
prepend
head
tail
append
apply
reverse
concat
Banker’s Queue
• Based on two lists (in and out)
• Guarantees amortized O(1) performance
11
class Queue(in: List[Int] = Nil, out: List[Int] = Nil) {
def enqueue(x: Int): Queue = ???
def dequeue: (Int, Queue) = ???
def front: Int = dequeue match { case (a, _) => a }
def rear: Queue = dequeue match { case (_, q) => q }
def isEmpty: Boolean = in.isEmpty && out.isEmpty
}
Queue: analysis
12
A = new Queue( , )
B = A.enqueue(1) = 1 , )
C = B.enqueue(2) = 12 , )
D = C.enqueue(3) = 23 1 , )
(V, E) = D.dequeue = , ))2 3
(U, F) = E.dequeue = , ))3
reverse
new Queue(
new Queue(
new Queue(
(1, new Queue(
(2, new Queue(
Amortized vs. Average Case
• Average Case analysis makes assumptions about
typical (most likely) input
• Amortized analysis considers total performance of
sequence of operations in a the worst case
Example:
• Dynamically-Resizing Array (java.util.ArrayList)
– Has O(n) average case performance for add operation
– It can be amortized to O(1)
• Usually it takes O(1) since the storage is big enough
• Sometimes it can take O(n) due to reallocation & copying
13
Queue: enqueue & dequeue
14
/**
* Time - O(1)
* Space - O(1)
*/
def enqueue(x: Int): Queue = new Queue(x :: in, out)
/**
* Time - O(1)
* Space - O(1)
*/
def dequeue: (Int, Queue) = out match {
case hd :: tl => (hd, new Queue(in, tl)) // O(1)
case Nil => in.reverse match { // O(n)
case hd :: tl => (hd, new Queue(Nil, tl))
case Nil => fail("Empty queue.")
}
}
Queue performance
15
enqueue
dequeue*
front*
rear*
* amortized complexity
Binary Search Tree
16
5
2 7
1 3 8
BST hierarchy
17
abstract sealed class Tree {
def value: Int
def left: Tree
def right: Tree
def isEmpty: Boolean
}
case object Leaf extends Tree {
def value: Int = fail("Empty tree.")
def left: Tree = fail("Empty tree.")
def right: Tree = fail("Empty tree.")
def isEmpty: Boolean = true
}
case class Branch(value: Int,
left: Tree = Leaf,
right: Tree = Leaf) extends Tree {
def isEmpty: Boolean = false
}
5
Branch
Leaf
BST: analysis
18
A = Branch(5) = 5
B = Branch(7, A, Branch(9)) = 7
9
C = Branch(1, Leaf, B) = 1
structural sharing
BST: insert
19
5
2 7
1 3 86
path copying
7
5
/**
* Time - O(log n)
* Space - O(log n)
*/
def add(x: Int): Tree =
if (isEmpty) Branch(x)
else if (x < value) Branch(value, left.add(x), right)
else if (x > value) Branch(value, left, right.add(x))
else this
BST: remove (cases)
20
5
2 7
1 3 8
5
2 7
1 3 8
5
2 7
1 3 8
BST: remove (code)
21
/**
* Time - O(log n)
* Space - O(log n)
*/
def remove(x: Int): Tree =
if (isEmpty) fail("Can't find " + x + " in this tree.")
else if (x < value) Branch(value, left.remove(x), right)
else if (x > value) Branch(value, left, right.remove(x))
else {
if (left.isEmpty && right.isEmpty) Leaf // case 1
else if (left.isEmpty) right // case 2
else if (right.isEmpty) left // case 2
else { // case 3
val succ = right.min // case 3
Branch(succ, left, right.remove(succ)) // case 3
}
}
/**
* Time - O(log n)
* Space - O(log n)
*/
def min: Int = {
@tailrec def loop(t: Tree, m: Int): Int =
if (t.isEmpty) m else loop(t.left, t.value)
if (isEmpty) fail("Empty tree.")
else loop(left, value)
}
/**
* Time - O(log n)
* Space - O(log n)
*/
def max: Int = {
@tailrec def loop(t: Tree[Int], m: Int): Int =
if (t.isEmpty) m else loop(t.right, t.value)
if (isEmpty) fail("Empty tree.")
else loop(right, value)
}
BST: min & max
22
5
2 7
1 3 8
5
2 7
1 3 8
BST: apply
23
5
2 7
1 3 6 8
n - 1
/**
* Time - O(log n)
* Space - O(log n)
*/
def apply(n: Int): A =
if (isEmpty) fail("Tree doesn't contain a " + n + "th element.")
else if (n < left.size) left(n)
else if (n > left.size) right(n - size - 1)
else value
BST: DFS (pre-order traversal)
24
/**
* Time - O(n)
* Space - O(log n)
*/
def valuesByDepth: List[Int] = {
def loop(s: List[Tree]): List[Int] =
if (s.isEmpty) Nil
else if (s.head.isEmpty) loop(s.tail)
else s.head.value :: loop(s.head.right :: s.head.left :: s.tail)
loop(List(this))
}
5
2 7
1 3 8
BST: BFS (level-order traversal)
25
/**
* Time - O(n)
* Space - O(log n)
*/
def valuesByBreadth: List[Int] = {
import scala.collection.immutable.Queue
def loop(q: Queue[Tree]): List[Int] =
if (q.isEmpty) Nil
else if (q.head.isEmpty) loop(q.tail)
else q.head.value :: loop(q.tail :+ q.head.left :+ q.head.right)
loop(Queue(this))
}
5
2 7
1 3 8
BST: inverse (problem)
26
-5
-7 -2
-8 -3 -1
5
2 7
1 3 8
invert
BST: inverse (solution)
27
/**
* Time - O(n)
* Space - O(log n)
*/
def invert: Tree =
if (isEmpty) Leaf else Branch(-value, right.invert, left.invert)
BST performance
28
insert
contains
remove
min
max
apply
bfs
dfs
How fast BST?
29
It’s extremely fast if it’s balanced
Balanced BST: Red-Black Tree
• Red invariant: No red node has red parent
• Black invariant: Every root-to-leaf path contains
the same number of black nodes
• Suggested by Chris Okasaki in his paper “Red-Black
Trees in a Functional Settings”
• Asymptotically optimal implementation
• Easy to understand and implement
30
R-B Tree chart sheet
31
z
y
x
x
y
z
z
y
x
z
x
y
x
z
y
Double Rotation
Double Rotation
Single Rotation
Single Rotation
R-B Tree: balanced insert
32
def balancedAdd(x: Int): Tree =
if (isEmpty) RedBranch(x)
else if (x < value) balance(isBlack, value, left.balancedAdd(x), right)
else if (x > value) balance(isBlack, value, left, right.balancedAdd(x))
else this
def balance(b: Boolean, x: Int, left: Tree, right: Tree): Tree =
(b, left, right) match {
case (true, RedBranch(y, RedBranch(z, a, b), c), d) =>
BlackBranch(y, RedBranch(z, a, b), RedBranch(x, c, d))
case (true, a, RedBranch(y, b, RedBranch(z, c, d))) =>
BlackBranch(y, RedBranch(x, a, b), RedBranch(z, c, d))
case (true, RedBranch(z, a, RedBranch(y, b, c)), d) =>
BlackBranch(y, RedBranch(z, a, b), RedBranch(x, c, d))
case (true, a, RedBranch(z, RedBranch(y, b, c), d)) =>
BlackBranch(y, RedBranch(x, a, b), RedBranch(z, c, d))
case (true, _, _) => BlackBranch(x, left, right)
case (false, _, _) => RedBranch(x, left, right)
}
What about Scala?
• Scala has Singly-Linked List
– scala.collection.immutable.List
• Scala has Banker’s Queue
– scala.collection.immutable.Queue
• Scala has Balanced BST (R-B Tree)
– scala.collection.immutable.TreeSet
– scala.collection.immutable.TreeMap
• And a bit more …
33
Patricia Trie
34
Binary Trie analysis
35
{ 1 -> “one”, 4 -> “four”, 5 -> “five” }
0 1
00 10 1101
100
four
001 101
one five
Patricia Trie analysis
36
{ 1 -> “one”, 4 -> “four”, 5 -> “five” }
1
44 -> four
1 -> one 5 -> five
Branching Bit
= 0x001
= 0x100
Hash Array Mapped Trie
37
HMAT analysis
38
● ... ●
● ... ● ● ... ●
1 2 ... 32 993 994 ... 1024 31755 31756 ... 31786 32737 32736 ... 32768
Bedtime Reading
• Okasaki’s Purely Functional Data Structures
• http://okasaki.blogspot.com/
• https://github.com/vkostyukov/scalacaster
• http://www.codecommit.com/blog/
• http://cstheory.stackexchange.com/questions/1539/whats-new-in-
purely-functional-data-structures-since-okasaki
39
40
vkostyukov
vkostyukov

More Related Content

What's hot

Queue Implementation Using Array & Linked List
Queue Implementation Using Array & Linked ListQueue Implementation Using Array & Linked List
Queue Implementation Using Array & Linked List
PTCL
 

What's hot (20)

Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Zio in real world
Zio in real worldZio in real world
Zio in real world
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Category theory for beginners
Category theory for beginnersCategory theory for beginners
Category theory for beginners
 
Preparing for Scala 3
Preparing for Scala 3Preparing for Scala 3
Preparing for Scala 3
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programming
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
 
Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!
 
Introduction to Rust language programming
Introduction to Rust language programmingIntroduction to Rust language programming
Introduction to Rust language programming
 
Python lab manual all the experiments are available
Python lab manual all the experiments are availablePython lab manual all the experiments are available
Python lab manual all the experiments are available
 
Queue Implementation Using Array & Linked List
Queue Implementation Using Array & Linked ListQueue Implementation Using Array & Linked List
Queue Implementation Using Array & Linked List
 
Arrays
ArraysArrays
Arrays
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
 
Scala Intro
Scala IntroScala Intro
Scala Intro
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 

Similar to Purely Functional Data Structures in Scala

JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
PROIDEA
 
Data structures in scala
Data structures in scalaData structures in scala
Data structures in scala
Meetu Maltiar
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scala
djspiewak
 
lecture 12
lecture 12lecture 12
lecture 12
sajinsc
 
lecture 13
lecture 13lecture 13
lecture 13
sajinsc
 

Similar to Purely Functional Data Structures in Scala (20)

JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
 
CS-102 BST_27_3_14v2.pdf
CS-102 BST_27_3_14v2.pdfCS-102 BST_27_3_14v2.pdf
CS-102 BST_27_3_14v2.pdf
 
Zippers
ZippersZippers
Zippers
 
Lispprograaming excercise
Lispprograaming excerciseLispprograaming excercise
Lispprograaming excercise
 
Functional programming in scala
Functional programming in scalaFunctional programming in scala
Functional programming in scala
 
Data structures in scala
Data structures in scalaData structures in scala
Data structures in scala
 
Data Structures In Scala
Data Structures In ScalaData Structures In Scala
Data Structures In Scala
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scala
 
Python_ 3 CheatSheet
Python_ 3 CheatSheetPython_ 3 CheatSheet
Python_ 3 CheatSheet
 
Python3 cheatsheet
Python3 cheatsheetPython3 cheatsheet
Python3 cheatsheet
 
lecture 12
lecture 12lecture 12
lecture 12
 
Review session2
Review session2Review session2
Review session2
 
Mementopython3 english
Mementopython3 englishMementopython3 english
Mementopython3 english
 
Python3
Python3Python3
Python3
 
Mementopython3 english
Mementopython3 englishMementopython3 english
Mementopython3 english
 
Advance LISP (Artificial Intelligence)
Advance LISP (Artificial Intelligence) Advance LISP (Artificial Intelligence)
Advance LISP (Artificial Intelligence)
 
Introduction to python cheat sheet for all
Introduction to python cheat sheet for allIntroduction to python cheat sheet for all
Introduction to python cheat sheet for all
 
Functional programming with_scala
Functional programming with_scalaFunctional programming with_scala
Functional programming with_scala
 
lecture 13
lecture 13lecture 13
lecture 13
 
R Cheat Sheet for Data Analysts and Statisticians.pdf
R Cheat Sheet for Data Analysts and Statisticians.pdfR Cheat Sheet for Data Analysts and Statisticians.pdf
R Cheat Sheet for Data Analysts and Statisticians.pdf
 

Recently uploaded

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Recently uploaded (20)

Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 

Purely Functional Data Structures in Scala

  • 1. Purely Functional Data Structures in Scala Vladimir Kostyukov http://vkostyukov.ru
  • 2. Agenda • Immutability & Persistence • Singly-Linked List • Banker’s Queue • Binary Search Tree • Balanced BST: Red-Black Tree • Scala support of these things • Patricia Trie • Hash Array Mapped Trie 2
  • 3. Immutability & Persistence Two problems: • FP paradigm doesn’t support destructive updates • FP paradigm expects both the old and new versions of DS will be available after update Two solutions: • Immutable objects aren’t changeable • Persistent objects support multiple versions 3
  • 4. Singly-Linked List 4 35 7 Cons Nil abstract sealed class List { def head: Int def tail: List def isEmpty: Boolean } case object Nil extends List { def head: Int = fail("Empty list.") def tail: List = fail("Empty list.") def isEmpty: Boolean = true } case class Cons(head: Int, tail: List = Nil) extends List { def isEmpty: Boolean = false }
  • 5. List: analysis 5 35 7A = B = Cons(9, A) = 9 C = Cons(1, Cons(8, B)) = 1 8 structural sharing
  • 6. /** * Time - O(1) * Space - O(1) */ def prepend(x: Int): List = Cons(x, this) /** * Time - O(n) * Space - O(n) */ def append(x: Int): List = if (isEmpty) Cons(x) else Cons(head, tail.append(x)) List: append & prepend 6 35 79 35 7 9
  • 7. List: apply 7 35 7 42 6 n - 1 /** * Time - O(n) * Space - O(n) */ def apply(n: Int): A = if (isEmpty) fail("Index out of bounds.") else if (n == 0) head else tail(n - 1) // or tail.apply(n - 1)
  • 8. List: concat 8 path copying A = 42 6 B = 35 7 C = A.concat(B) = 42 6 /** * Time - O(n) * Space - O(n) */ def concat(xs: List): List = if (isEmpty) xs else tail.concat(xs).prepend(head)
  • 9. List: reverse (two approaches) 9 42 6 46 2reverse( ) = def reverse: List = if (isEmpty) Nil else tail.reverse.append(head) , or tail recursion in O(n) The straightforward solution in O(n2) def reverse: List = { @tailrec def loop(s: List, d: List): List = if (s.isEmpty) d else loop(s.tail, d.prepend(s.head)) loop(this, Nil) }
  • 11. Banker’s Queue • Based on two lists (in and out) • Guarantees amortized O(1) performance 11 class Queue(in: List[Int] = Nil, out: List[Int] = Nil) { def enqueue(x: Int): Queue = ??? def dequeue: (Int, Queue) = ??? def front: Int = dequeue match { case (a, _) => a } def rear: Queue = dequeue match { case (_, q) => q } def isEmpty: Boolean = in.isEmpty && out.isEmpty }
  • 12. Queue: analysis 12 A = new Queue( , ) B = A.enqueue(1) = 1 , ) C = B.enqueue(2) = 12 , ) D = C.enqueue(3) = 23 1 , ) (V, E) = D.dequeue = , ))2 3 (U, F) = E.dequeue = , ))3 reverse new Queue( new Queue( new Queue( (1, new Queue( (2, new Queue(
  • 13. Amortized vs. Average Case • Average Case analysis makes assumptions about typical (most likely) input • Amortized analysis considers total performance of sequence of operations in a the worst case Example: • Dynamically-Resizing Array (java.util.ArrayList) – Has O(n) average case performance for add operation – It can be amortized to O(1) • Usually it takes O(1) since the storage is big enough • Sometimes it can take O(n) due to reallocation & copying 13
  • 14. Queue: enqueue & dequeue 14 /** * Time - O(1) * Space - O(1) */ def enqueue(x: Int): Queue = new Queue(x :: in, out) /** * Time - O(1) * Space - O(1) */ def dequeue: (Int, Queue) = out match { case hd :: tl => (hd, new Queue(in, tl)) // O(1) case Nil => in.reverse match { // O(n) case hd :: tl => (hd, new Queue(Nil, tl)) case Nil => fail("Empty queue.") } }
  • 17. BST hierarchy 17 abstract sealed class Tree { def value: Int def left: Tree def right: Tree def isEmpty: Boolean } case object Leaf extends Tree { def value: Int = fail("Empty tree.") def left: Tree = fail("Empty tree.") def right: Tree = fail("Empty tree.") def isEmpty: Boolean = true } case class Branch(value: Int, left: Tree = Leaf, right: Tree = Leaf) extends Tree { def isEmpty: Boolean = false } 5 Branch Leaf
  • 18. BST: analysis 18 A = Branch(5) = 5 B = Branch(7, A, Branch(9)) = 7 9 C = Branch(1, Leaf, B) = 1 structural sharing
  • 19. BST: insert 19 5 2 7 1 3 86 path copying 7 5 /** * Time - O(log n) * Space - O(log n) */ def add(x: Int): Tree = if (isEmpty) Branch(x) else if (x < value) Branch(value, left.add(x), right) else if (x > value) Branch(value, left, right.add(x)) else this
  • 20. BST: remove (cases) 20 5 2 7 1 3 8 5 2 7 1 3 8 5 2 7 1 3 8
  • 21. BST: remove (code) 21 /** * Time - O(log n) * Space - O(log n) */ def remove(x: Int): Tree = if (isEmpty) fail("Can't find " + x + " in this tree.") else if (x < value) Branch(value, left.remove(x), right) else if (x > value) Branch(value, left, right.remove(x)) else { if (left.isEmpty && right.isEmpty) Leaf // case 1 else if (left.isEmpty) right // case 2 else if (right.isEmpty) left // case 2 else { // case 3 val succ = right.min // case 3 Branch(succ, left, right.remove(succ)) // case 3 } }
  • 22. /** * Time - O(log n) * Space - O(log n) */ def min: Int = { @tailrec def loop(t: Tree, m: Int): Int = if (t.isEmpty) m else loop(t.left, t.value) if (isEmpty) fail("Empty tree.") else loop(left, value) } /** * Time - O(log n) * Space - O(log n) */ def max: Int = { @tailrec def loop(t: Tree[Int], m: Int): Int = if (t.isEmpty) m else loop(t.right, t.value) if (isEmpty) fail("Empty tree.") else loop(right, value) } BST: min & max 22 5 2 7 1 3 8 5 2 7 1 3 8
  • 23. BST: apply 23 5 2 7 1 3 6 8 n - 1 /** * Time - O(log n) * Space - O(log n) */ def apply(n: Int): A = if (isEmpty) fail("Tree doesn't contain a " + n + "th element.") else if (n < left.size) left(n) else if (n > left.size) right(n - size - 1) else value
  • 24. BST: DFS (pre-order traversal) 24 /** * Time - O(n) * Space - O(log n) */ def valuesByDepth: List[Int] = { def loop(s: List[Tree]): List[Int] = if (s.isEmpty) Nil else if (s.head.isEmpty) loop(s.tail) else s.head.value :: loop(s.head.right :: s.head.left :: s.tail) loop(List(this)) } 5 2 7 1 3 8
  • 25. BST: BFS (level-order traversal) 25 /** * Time - O(n) * Space - O(log n) */ def valuesByBreadth: List[Int] = { import scala.collection.immutable.Queue def loop(q: Queue[Tree]): List[Int] = if (q.isEmpty) Nil else if (q.head.isEmpty) loop(q.tail) else q.head.value :: loop(q.tail :+ q.head.left :+ q.head.right) loop(Queue(this)) } 5 2 7 1 3 8
  • 26. BST: inverse (problem) 26 -5 -7 -2 -8 -3 -1 5 2 7 1 3 8 invert
  • 27. BST: inverse (solution) 27 /** * Time - O(n) * Space - O(log n) */ def invert: Tree = if (isEmpty) Leaf else Branch(-value, right.invert, left.invert)
  • 29. How fast BST? 29 It’s extremely fast if it’s balanced
  • 30. Balanced BST: Red-Black Tree • Red invariant: No red node has red parent • Black invariant: Every root-to-leaf path contains the same number of black nodes • Suggested by Chris Okasaki in his paper “Red-Black Trees in a Functional Settings” • Asymptotically optimal implementation • Easy to understand and implement 30
  • 31. R-B Tree chart sheet 31 z y x x y z z y x z x y x z y Double Rotation Double Rotation Single Rotation Single Rotation
  • 32. R-B Tree: balanced insert 32 def balancedAdd(x: Int): Tree = if (isEmpty) RedBranch(x) else if (x < value) balance(isBlack, value, left.balancedAdd(x), right) else if (x > value) balance(isBlack, value, left, right.balancedAdd(x)) else this def balance(b: Boolean, x: Int, left: Tree, right: Tree): Tree = (b, left, right) match { case (true, RedBranch(y, RedBranch(z, a, b), c), d) => BlackBranch(y, RedBranch(z, a, b), RedBranch(x, c, d)) case (true, a, RedBranch(y, b, RedBranch(z, c, d))) => BlackBranch(y, RedBranch(x, a, b), RedBranch(z, c, d)) case (true, RedBranch(z, a, RedBranch(y, b, c)), d) => BlackBranch(y, RedBranch(z, a, b), RedBranch(x, c, d)) case (true, a, RedBranch(z, RedBranch(y, b, c), d)) => BlackBranch(y, RedBranch(x, a, b), RedBranch(z, c, d)) case (true, _, _) => BlackBranch(x, left, right) case (false, _, _) => RedBranch(x, left, right) }
  • 33. What about Scala? • Scala has Singly-Linked List – scala.collection.immutable.List • Scala has Banker’s Queue – scala.collection.immutable.Queue • Scala has Balanced BST (R-B Tree) – scala.collection.immutable.TreeSet – scala.collection.immutable.TreeMap • And a bit more … 33
  • 35. Binary Trie analysis 35 { 1 -> “one”, 4 -> “four”, 5 -> “five” } 0 1 00 10 1101 100 four 001 101 one five
  • 36. Patricia Trie analysis 36 { 1 -> “one”, 4 -> “four”, 5 -> “five” } 1 44 -> four 1 -> one 5 -> five Branching Bit = 0x001 = 0x100
  • 37. Hash Array Mapped Trie 37
  • 38. HMAT analysis 38 ● ... ● ● ... ● ● ... ● 1 2 ... 32 993 994 ... 1024 31755 31756 ... 31786 32737 32736 ... 32768
  • 39. Bedtime Reading • Okasaki’s Purely Functional Data Structures • http://okasaki.blogspot.com/ • https://github.com/vkostyukov/scalacaster • http://www.codecommit.com/blog/ • http://cstheory.stackexchange.com/questions/1539/whats-new-in- purely-functional-data-structures-since-okasaki 39