0% found this document useful (0 votes)
85 views10 pages

Dsa 2

The document outlines a syllabus for a B.Tech course on Data Structures and Algorithms, focusing on functional data structures, particularly ConsList, immutable sets, and maps. It explains the principles of functional programming, emphasizing immutability and structural sharing, and discusses real-world applications such as version control systems and concurrency. Additionally, it provides a Java implementation of the ConsList data structure, illustrating its construction and usage.

Uploaded by

krishnapspk2006
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
85 views10 pages

Dsa 2

The document outlines a syllabus for a B.Tech course on Data Structures and Algorithms, focusing on functional data structures, particularly ConsList, immutable sets, and maps. It explains the principles of functional programming, emphasizing immutability and structural sharing, and discusses real-world applications such as version control systems and concurrency. Additionally, it provides a Java implementation of the ConsList data structure, illustrating its construction and usage.

Uploaded by

krishnapspk2006
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

BRANCH : AIE REGULATION : R-22

Course: B.Tech SUBJECT: Data Structures and Algorithms 2

SubjectCode:22AIE203 Year & Sem-II Year-I Sem


Name of the Instructor Naresh Cherukuri

UNIT-2
Syllabus:
Functional data structures, ConsList, immutable Set, Immutable Maps, Sorting
immutable linear structures(functional sort). Map and Reduce Operations on
Sequences

2.1.Introduction to Functional Data Structures:

• A functional data structure is a type of data structure that operates in


a way compatible with the principles of functional programming, which
is a programming paradigm based on immutability, first-class functions,
and no side effects.
• In simpler terms, functional data structures do not change or mutate the
data they store. Instead of modifying the original structure, they create
new versions or copies of it when changes are needed, while the old
version remains unchanged.
Key Idea: Immutability
• Imagine you're working on a shared whiteboard with other people.
In traditional (imperative) programming, if someone writes
something on the whiteboard, it changes what was there before.
• You might lose information about what was previously written. This
is like using mutable data structures — you change the data in place,
and you can’t go back to the old state.
• In contrast, in functional data structures, it’s as if every time you
want to write something on the whiteboard, instead of erasing and
overwriting, you make a copy of the whiteboard with the new
information while the old whiteboard stays intact.

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.

Performance Consideration: Persistent Data Structures


In functional data structures, the idea of persistence plays a crucial role.
Persistence means that every version of the data structure is preserved after
modifications. There are different kinds of persistence:
• Full persistence: You can access and modify any version of the data
structure.
• Partial persistence: You can access any version, but you can only modify
the latest version.
• Ephemeral: Traditional data structures that do not preserve history.
This ensures that previous versions of the data structure remain available
for future use without needing to create entirely new copies, reducing
memory and time overhead.
Real-World Usage and Applications of Functional Data Structures
1. Version Control Systems (like Git)
Functional data structures are ideal for systems like Git, where you need to
keep track of different versions of files and their entire history. When you
make changes to a file in Git, instead of overwriting the file, Git creates a
new snapshot (like a functional data structure would). All past versions of
the file remain accessible, and new changes are stored efficiently through
structural sharing.
o Analogy: Think of a book editor who saves each draft of a
manuscript in separate folders. When they make a small change, they

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.

Benefits of Functional Data Structures

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.

2.2 ConsList in Functional Data Structures:


The fundamental data structure in many functional languages is the immutable
linked list.
It is constructed from two building blocks:
Nil: The empty list
Cons: A cell containing an element also known as head; and a pointer to the rest
of the list also known as tail.
The term cons comes form functional programming language ‘lisp’. In Scala,
there is a double colon operator :: called cons operator.
Let’s take an example in Scala. Consider following list.
List(1, 2, 3) or 1 :: 2 :: 3 :: Nil
In memory, the list is represented as follow:

In the diagram above * represents a cons cell, which contains an element


and a reference to next cons cell.
e.g. first cons cell stores the value of integer 1 and points to the next cons
cell which hold a value of integer 2.
Technically, the cons list is also a (unbalanced) binary tree.

The representation above can be re-arranged as follow:

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;
}

public ConsList<T> tail() {


return tail;
}

The ConsList can have a head element and a tail which itself is a
ConsList.

Now there are two special implementation cases possible for


ConsList.

1. Nil: An empty list

2. Cons: A list with a head and tail

Let’s create concrete classes for these cases.


public class Nil<T> extends ConsList<T> {

private static Nil Nil;

public static <T> Nil<T> getNil() {


if(Nil == null) {
Nil = new Nil<T>();
}
return Nil;
}
private Nil() { }

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 abstract boolean isEmpty();

public static <T> ConsList<T> Nil() {


return getNil();
}

public T head() {
return head;

CH NARESH 6
}

public ConsList<T> tail() {


return tail;
}

And finally the implementation of cons operations.


public class Cons<T> extends ConsList<T> {
private Cons(T head, ConsList<T> tail) {
this.head = head;
this.tail = tail;
}

public static <T> Cons<T> cons(T head, ConsList<T> tail) {


return new Cons<>(head, tail);
}

@Override
public boolean isEmpty() {
return false;
}

@Override
public String toString() {

return "List(" + toString(this) + ")";


}

private String toString(ConsList<T> list) {


return list.isEmpty() ? "" :
list.head() +
(list.tail().isEmpty() ? "" :
", " + toString(list.tail()));
}
}

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())));

ConsList<String> colors = cons("Red", cons("Green", cons("Blue",


Nil())));

Write a Java Program for the implementation of ConsList in Functional


Data structure
package Wonder;

import java.util.NoSuchElementException;

public abstract class ConsList<T> {


T head;
ConsList<T> tail;

public abstract boolean isEmpty();

public static <T> ConsList<T> Nil() {


return Nil.getNil();
}

public T head() {
if (isEmpty()) {
throw new NoSuchElementException("head of empty list");
}
return head;
}

public ConsList<T> tail() {


if (isEmpty()) {
throw new UnsupportedOperationException("tail of empty list");
}
return tail;
}
}

class Nil<T> extends ConsList<T> {

private static final Nil<?> INSTANCE = new Nil<>();

@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");
}
}

class Cons<T> extends ConsList<T> {

private Cons(T head, ConsList<T> tail) {


this.head = head;
this.tail = tail;
}

public static <T> Cons<T> cons(T head, ConsList<T> tail) {


return new Cons<>(head, tail);
}

@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();
}

public static void main(String[] args) {


ConsList<Integer> list2 = cons(1, cons(2, cons(3, Nil())));
System.out.println(list2); // Should print: List(1, 2, 3)

ConsList<String> colors = cons("Red", cons("Green", cons("Blue",


Nil())));
System.out.println(colors); // Should print: List(Red, Green, Blue)
}
}

CH NARESH 10

You might also like