What are some good examples that I can use to explain functional programming?
The audience would be people with little programming experience, or people who only have ob
Immutable data structures
You know, I've commented to people about functional programming before and have brought up the whole "immutable data structure" thing. This usually blows people's mind -- how are you supposed to add something to a collection if you can't actually add it?
The immediate answer is "well you don't, you just create a new version of your data structure with the object added", and the immediate response is "that doesn't sound very efficient". Well it is.
Immutable stack
Canonical immutable data structure is an immutable stack, which is basically a node containing two readonly values: a head and a tail. Pushing and popping from the stack are O(1). Indexed lookups, inserts/deletes in the middle of the list require O(n) time, which incidentally is the same as a mutable stack.
using System;
namespace Juliet
{
public abstract class Stack
{
public static readonly Stack Empty = new Nil();
public abstract T Head { get; }
public abstract Stack Tail { get; }
public abstract bool IsEmpty { get; }
public Stack Push(T value) { return new Cons(value, this); }
public Stack Append(Stack other)
{
if (this.IsEmpty) { return other; }
else { return new Cons(this.Head, this.Tail.Append(other)); }
}
sealed class Nil : Stack
{
public override T Head { get { throw new Exception("Empty stack"); } }
public override Stack Tail { get { throw new Exception("Empty stack"); } }
public override bool IsEmpty { get { return true; } }
}
sealed class Cons : Stack
{
private readonly T _head;
private readonly Stack _tail;
public override T Head { get { return _head; ; } }
public override Stack Tail { get { return _tail; } }
public override bool IsEmpty { get { return false; } }
public Cons(T head, Stack tail)
{
_head = head;
_tail = tail;
}
}
}
class Program
{
static void Main(string[] args)
{
var s = Stack.Empty.Push(1).Push(2).Push(3).Push(4).Push(5);
while (!s.IsEmpty)
{
Console.Write("{0} ", s.Head);
s = s.Tail;
}
Console.ReadKey(true);
}
}
}
Immutable AVL Tree
Most people aren't impressed by stacks because it isn't a "serious" data structure, but AVL trees and other self-balancing data structures are pretty impressive. Trees are actually very easy to write as immutable data structures.
This version supports O(log n) inserts and lookups, delete isn't shown but it can be implemented in O(log n) as well. In other words, the immutable AVL tree has the same computational complexity as as its mutable variant:
using System;
using System.Linq;
namespace Juliet
{
public abstract class AvlTree
where T : IComparable
{
static AvlTree Make(AvlTree left, T value, AvlTree right)
{
return new Node(left, value, right, Math.Max(left.Height, right.Height) + 1, left.Count + right.Count + 1);
}
public static readonly AvlTree Empty = new Nil();
protected abstract AvlTree Left { get; }
protected abstract AvlTree Right { get; }
protected abstract T Value { get; }
public abstract int Height { get; }
public abstract int Count { get; }
public bool Contains(T v)
{
if (this.Height == 0) { return false; }
else
{
int compare = v.CompareTo(this.Value);
if (compare < 0) { return this.Left.Contains(v); }
else if (compare > 0) { return this.Right.Contains(v); }
else { return true; }
}
}
public AvlTree Insert(T v)
{
if (this.Height == 0) { return Make(this, v, this); }
else
{
int compare = v.CompareTo(this.Value);
if (compare < 0) { return Balance(Make(this.Left.Insert(v), this.Value, this.Right)); }
else if (compare > 0) { return Balance(Make(this.Left, this.Value, this.Right.Insert(v))); }
else { return this; }
}
}
private static AvlTree Balance(AvlTree tree)
{
if (tree.Height > 0)
{
int outerBalanceFactor = tree.Left.Height - tree.Right.Height;
if (outerBalanceFactor <= -2 && tree.Right.Height > 0) // right-side too heavy
{
int innerBalanceFactor = tree.Right.Left.Height - tree.Right.Right.Height;
if (innerBalanceFactor <= -1) // right-right case
{
return LeftRotate(tree);
}
else if (innerBalanceFactor >= 1) // right-left case
{
return DoubleLeftRotate(tree);
}
}
else if (outerBalanceFactor >= 2 && tree.Left.Height > 0) // left-side too heavy
{
int innerBalanceFactor = tree.Left.Left.Height - tree.Left.Right.Height;
if (innerBalanceFactor <= -1) // left-left case
{
return DoubleRightRotate(tree);
}
else if (innerBalanceFactor >= 1) // left-right case
{
return RightRotate(tree);
}
}
}
return tree;
}
private static AvlTree LeftRotate(AvlTree tree)
{
if (tree.Height > 0 && tree.Right.Height > 0)
{
T p = tree.Value;
T q = tree.Right.Value;
AvlTree a = tree.Left;
AvlTree b = tree.Right.Left;
AvlTree c = tree.Right.Right;
return Make(Make(a, p, b), q, c);
}
return tree;
}
private static AvlTree RightRotate(AvlTree tree)
{
if (tree.Height > 0 && tree.Left.Height > 0)
{
T q = tree.Value;
T p = tree.Left.Value;
AvlTree a = tree.Left.Left;
AvlTree b = tree.Left.Right;
AvlTree c = tree.Right;
return Make(a, p, Make(b, q, c));
}
return tree;
}
private static AvlTree DoubleLeftRotate(AvlTree tree)
{
if (tree.Height > 0)
{
// right-rotate right child, left-rotate root
return LeftRotate(Make(tree.Left, tree.Value, RightRotate(tree.Right)));
}
return tree;
}
private static AvlTree DoubleRightRotate(AvlTree tree)
{
if (tree.Height > 0)
{
// left-rotate left child, right-rotate root
return RightRotate(Make(LeftRotate(tree.Left), tree.Value, tree.Right));
}
return tree;
}
sealed class Nil : AvlTree
{
protected override AvlTree Left { get { throw new Exception("Empty tree"); } }
protected override AvlTree Right { get { throw new Exception("Empty tree"); } }
protected override T Value { get { throw new Exception("Empty tree"); } }
public override int Height { get { return 0; } }
public override int Count { get { return 0; } }
}
sealed class Node : AvlTree
{
readonly AvlTree _left;
readonly AvlTree _right;
readonly T _value;
readonly int _height;
readonly int _count;
protected override AvlTree Left { get { return _left; } }
protected override AvlTree Right { get { return _right; } }
protected override T Value { get { return _value; } }
public override int Height { get { return _height; } }
public override int Count { get { return _count; } }
public Node(AvlTree left, T value, AvlTree right, int height, int count)
{
_left = left;
_right = right;
_value = value;
_height = height;
_count = count;
}
}
}
class Program
{
static void Main(string[] args)
{
var tree = AvlTree.Empty;
foreach(int i in Enumerable.Range(1, 1000000).OrderBy(_ => Guid.NewGuid()))
{
tree = tree.Insert(i);
}
Console.Write("Tree height: {0}, tree count: {1}", tree.Height, tree.Count);
Console.ReadKey(true);
}
}
}