Dsa 2
Dsa 2
UNIT-2
Syllabus:
Functional data structures, ConsList, immutable Set, Immutable Maps, Sorting
immutable linear structures(functional sort). Map and Reduce Operations on
Sequences
CH NARESH 1
• That way, anyone can always refer to the previous versions. This
approach prevents any accidental loss of information and ensures
predictability.
How Functional Data Structures Work
• To ensure efficiency, functional data structures often rely on
structural sharing. Let’s say you have a tree structure, and you
want to add a new node.
• Instead of copying the entire tree, you can create a new tree that
shares most of its nodes with the original tree, only modifying the
necessary parts.
Example: A Functional Linked List
Imagine a simple linked list where each node contains some data and a
pointer to the next node:
• If you want to add a new element to the front of the list, you don’t create
an entirely new list.
• You create a new node that points to the existing list, leaving the old version
intact.
It’s like adding a new book to the front of a stack of books. You don’t need
to reorder all the other books; you just place a new one on top.
CH NARESH 2
only modify the new version without touching the old drafts, but
most of the content (the unchanged parts) is shared across versions.
2. Concurrency and Parallel Computing
Because functional data structures are immutable, they’re great for
concurrent programming. Multiple threads or processes can safely
access and modify the data structure without worrying about one thread
accidentally changing the data that another is using. There’s no need for
complex locking mechanisms to prevent conflicts.
o Analogy: Imagine multiple people are working on the same Google
Doc, but instead of worrying about overwriting each other's work,
every change made by a person creates a new version of the
document. Everyone can work in parallel without stepping on each
other’s toes.
3. Undo Functionality in Software
Functional data structures are perfect for implementing undo and redo
functionality in applications. Since every modification creates a new
version, you can easily revert to any previous version of the data structure.
o Analogy: If you’re using a drawing app, and you make a mistake,
you can undo the last action and go back to the previous version of
your drawing. Each version of your drawing is preserved in some
way, allowing you to go back and forth.
4. Blockchain and Cryptographic Systems
In blockchains, every block of data (containing transactions or
information) builds on the previous block. Since past blocks cannot be
modified, it behaves similarly to a functional data structure. If a new block
needs to be added, it doesn’t alter the previous blocks but rather builds a
new version (new block) linked to the old one.
o Analogy: Imagine a chain where each link represents a moment in
time. When you want to extend the chain, you simply add a new link
to the end without breaking or changing the previous links.
5. Database Systems with Snapshot Isolation
Some databases use functional data structures to implement snapshot
isolation, where every transaction sees the database as if it were a frozen
snapshot at a certain point in time. The database can efficiently manage
multiple versions of data without locking users out or causing delays.
o Analogy: It’s like a library where multiple readers can borrow
different copies of the same book. Each reader can see the same
content, even if one person marks up their copy — the other readers
are unaffected.
CH NARESH 3
1. Predictability: Since data doesn’t change, functions that use functional
data structures always produce the same output for the same input, which
is key for debugging and understanding how programs behave.
2. Thread Safety: Immutability means there’s no risk of data being changed
by multiple threads at the same time, making it perfect for concurrent or
parallel programs.
3. Reversible Changes: Functional data structures naturally support
operations like undoing and versioning, which is essential in applications
where history matters.
4. Efficient Updates: With structural sharing, you don’t have to make full
copies of data structures, saving time and memory.
CH NARESH 4
Let’s see how this could be modelled in Java. The basic structure of
cons list can be implemented using following abstract class.
public abstract class ConsList<T> {
protected T head;
protected ConsList<T> tail;
public abstract boolean isEmpty();
public T head() {
return head;
}
The ConsList can have a head element and a tail which itself is a
ConsList.
CH NARESH 5
@Override
public boolean isEmpty() {
return true;
}
@Override
public T head() {
throw new NoSuchElementException("head of empty list");
}
@Override
public ConsList<T> tail() {
throw new UnsupportedOperationException("tail of empty
list");
}
The Nil will be used to denote an empty list. Hence isEmpty method always
returns true. The head and tail methods throw exceptions, since these operations
cannot be performed on an empty list. Nil is typically used to denote an end of the
list.
There will always be a single instance of Nil class used to denote an empty list, so
we can mark this class as an inner class of ConsList and create a singleton Nil
object.
The ConsList class can be modified to include this singleton Nil as a static
member.
public abstract class ConsList<T> {
T head;
ConsList<T> tail;
public T head() {
return head;
CH NARESH 6
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public String toString() {
Note that the constructor is private and the list can be formed using
cons method which take head and tail and creates a cons cell by
connecting head with tail. The isEmpty method would always return
false.
CH NARESH 7
Now the list can be constructed using following syntax:
ConsList<Integer> list2 = cons(1, cons(2, cons(3, Nil())));
import java.util.NoSuchElementException;
public T head() {
if (isEmpty()) {
throw new NoSuchElementException("head of empty list");
}
return head;
}
@SuppressWarnings("unchecked")
public static <T> Nil<T> getNil() {
CH NARESH 8
return (Nil<T>) INSTANCE;
}
private Nil() {}
@Override
public boolean isEmpty() {
return true;
}
@Override
public T head() {
throw new NoSuchElementException("head of empty list");
}
@Override
public ConsList<T> tail() {
throw new UnsupportedOperationException("tail of empty list");
}
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("List(");
ConsList<T> current = this;
while (!current.isEmpty()) {
sb.append(current.head());
CH NARESH 9
current = current.tail();
if (!current.isEmpty()) {
sb.append(", ");
}
}
sb.append(")");
return sb.toString();
}
CH NARESH 10