Immutability, part 2: Creating a simple immutable stack - SLaks.Blog


Immutability, part 2: Creating a simple immutable stack – SLaks.Blog
Also check http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack.aspx
The simplest useful example of an immutable class is an immutable stack. Immutable stacks work just like regular stacks – with Push(), Pop(), and Peek() methods – except that instead of mutating the original instance, Push() and Pop() return a new, modified, instance.
IStack<int> myStack = empty; myStack = myStack.Push(1).Push(2).Push(3); while (!myStack.IsEmpty) { Console.WriteLine(myStack.Peek()); myStack = myStack.Pop(); }
Each implementation of this interface would supply a singleton empty instance; since they are immutable; there is no need to have more than one empty instance. Thus, there is no need for a constructor. Since Pop() needs to return the new stack, it cannot return the removed item; therefore, Peek() is the only way to get the item.
The naive implementation of this interface would use a list, and copy the list when creating a new stack:
public class NaiveStack<T> : IStack<T> {
    private readonly ReadOnlyCollection<T> items;
    private NaiveStack(IList<T> items) {
        this.items = new ReadOnlyCollection<T>(items);
    }

    public static readonly NaiveStack<T> Empty 
        = new NaiveStack<T>(new T[0]);

    public IStack<T> Push(T element) {
        var list = new List<T>(items);
        list.Add(element);
        return new NaiveStack<T>(list);
    }

    public IStack<T> Pop() {
        if (IsEmpty)
            throw new InvalidOperationException("Stack is empty");
        var list = new List<T>(items);
        list.RemoveAt(list.Count - 1);
        return new NaiveStack<T>(list);
    }

    public T Peek() { return items.Last(); }
    public bool IsEmpty { 
        get { return items.Count == 0; } 
    }
}
The problem with this approach is that each mutation requires an O(n) copy to create the list behind the new instance. This makesPush() and Pop() awfully slow, and vastly increases the memory footprint until the intermediate instances can be GCd.
To solve these problems, we can design the stack as a persistent data structure. Instead of copying anything when pushing on to a stack, we cab make the new stack instance hold only the newly added item, and maintain a reference to the previous instance storing the rest of the items. Popping from the stack can then simply return the previous instance. Basically, the stack becomes an immutable single-linked list.
public abstract class PersistentStack<T> : IStack<T> {
    public static readonly PersistentStack<T> Empty = new EmptyNode();

    public IStack<T> Push(T element) {
        return new LinkNode(this, element);
    }

    private class EmptyNode : PersistentStack<T> {
        public override IStack<T> Pop() {
            throw new InvalidOperationException("Stack is empty");
        }
        public override T Peek() { 
            throw new InvalidOperationException("Stack is empty");
        }
        public override bool IsEmpty { get { return true; } }
    }

    private class LinkNode : PersistentStack<T> {
        readonly IStack<T> previous;
        readonly T element;

        public LinkNode(IStack<T> previous, T element) {
            this.previous = previous;
            this.element = element;
        }

        public override IStack<T> Pop() {
            return previous;
        }
        public override T Peek() { return element; }
        public override bool IsEmpty { get { return false; } }
    }
    public abstract IStack<T> Pop();
    public abstract T Peek();
    public abstract bool IsEmpty { get; }
}
In this implementation, the empty and non-empty nodes are different enough that I decided to use polymorphism rather than ifstatements. The PersistentStack<T> type contains the only common logic (Push()); everything else is implement by the two concrete classes.
Here, the empty stack truly is a singleton; only one instance will ever be created for each element type. It can only be used as a starting point to create new stacks; the other methods simply throw exceptions. It also serves as the final node in the chain for all non-empty stacks.
Pushing onto any stack (empty or not) returns a new node that holds the new item and points to the previous stack; popping a non-empty stack simply returns that reference.
Like other linked lists, this approach implements both Push() and Pop() in O(1) in both memory and time.
http://blog.slaks.net/2013-06-28/covariant-immutable-stack/
Java: Immutable Stack
class Stack<T> implements IStack<T> {
private T head;
private IStack<T> tail;
public Stack(final T head, final IStack<T> tail) {
this.head = head;
this.tail = tail;
}
public static <U> IStack<U> empty(final Class<U> type) { return new EmptyStack<U>(); }
public boolean isEmpty() { return false; }
public T peek() { return this.head; }
public IStack<T> pop() { return this.tail; }
public IStack<T> push(T value) { return new Stack<T>(value, this); }
public Iterator<T> iterator() { return new StackIterator<T>(this); }
private static class StackIterator<U> implements Iterator<U> {
private IStack<U> stack;
public StackIterator(final IStack<U> stack) { this.stack = stack; }
public boolean hasNext() { return !this.stack.isEmpty(); }
public U next() {
U result = this.stack.peek();
this.stack = this.stack.pop();
return result;
}
public void remove() { }
}
private static class EmptyStack<U> implements IStack<U> {
public boolean isEmpty() { return true; }
public U peek() { throw new RuntimeException(“empty stack”); }
public IStack<U> pop() { throw new RuntimeException(“empty stack”); }
public IStack<U> push(U value) { return new Stack<U>(value, this); }
public Iterator<U> iterator() { return new StackIterator<U>(this); }
}
}
https://github.com/tunnelvisionlabs/java-immutable/blob/master/src/com/tvl/util/ImmutableLinkedStack.java
public final class ImmutableLinkedStack<T> implements ImmutableStack<T> {

    /**
     * The singleton empty stack.
     */
    private static final ImmutableLinkedStack<?> EMPTY_STACK = new ImmutableLinkedStack<Object>();

    /**
     * The element on the top of the stack.
     */
    private final T head;

    /**
     * A stack that contains the rest of the elements (under the top element);
     */
    private final ImmutableLinkedStack<T> tail;

    /**
     * Initializes a new instance of the {@link ImmutableLinkedStack} class that acts as the empty stack.
     */
    private ImmutableLinkedStack() {
        head = null;
        tail = null;
    }

    /**
     * Initializes a new instance of the {@link ImmutableLinkedStack} class.
     *
     * @param head The head element on the stack.
     * @param tail The rest of the elements on the stack.
     */
    private ImmutableLinkedStack(T head, ImmutableLinkedStack<T> tail) {
        Requires.notNull(tail, "tail");

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

    /**
     * Return an empty collection.
     *
     * @param <T> The type of items stored by the collection.
     * @return The immutable collection.
     */
    public static <T> ImmutableLinkedStack<T> create() {
        return empty();
    }

    /**
     * Creates a new immutable collection pre-filled with the specified item.
     *
     * @param <T> The type of items stored by the collection.
     * @param item The item to pre-populate.
     * @return The immutable collection.
     */
    public static <T> ImmutableLinkedStack<T> create(T item) {
        return ImmutableLinkedStack.<T>empty().push(item);
    }

    /**
     * Creates a new immutable collection pre-filled with the specified items.
     *
     * @param <T> The type of items stored by the collection.
     * @param items The items to pre-populate.
     * @return The immutable collection.
     */
    public static <T> ImmutableLinkedStack<T> create(T... items) {
        Requires.notNull(items, "items");

        ImmutableLinkedStack<T> stack = empty();
        for (T item : items) {
            stack = stack.push(item);
        }

        return stack;
    }

    /**
     * Creates a new immutable collection pre-filled with the specified items.
     *
     * @param <T> The type of items stored by the collection.
     * @param items The items to pre-populate.
     * @return The immutable collection.
     */
    public static <T> ImmutableLinkedStack<T> createAll(Iterable<? extends T> items) {
        Requires.notNull(items, "items");

        ImmutableLinkedStack<T> stack = empty();
        for (T item : items) {
            stack = stack.push(item);
        }

        return stack;
    }

    /**
     * Gets the empty stack, upon which all stacks are built.
     *
     * @param <T> The type of element stored by the stack.
     * @return The empty stack.
     */
    public static <T> ImmutableLinkedStack<T> empty() {
        @SuppressWarnings(Suppressions.UNCHECKED_SAFE)
        ImmutableLinkedStack<T> result = (ImmutableLinkedStack<T>)EMPTY_STACK;
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEmpty() {
        return tail == null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ImmutableLinkedStack<T> clear() {
        return empty();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ImmutableLinkedStack<T> push(T value) {
        return new ImmutableLinkedStack<T>(value, this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ImmutableLinkedStack<T> pop() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        return tail;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T peek() {
        if (isEmpty()) {
            throw new EmptyStackException();
        }

        return head;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterator<T> iterator() {
        return new Itr<T>(this);
    }

    /**
     * Reverses the order of the stack.
     *
     * @return The reversed stack.
     */
    ImmutableLinkedStack<T> reverse() {
        ImmutableLinkedStack<T> result = clear();
        for (ImmutableLinkedStack<T> f = this; !f.isEmpty(); f = f.pop()) {
            result = result.push(f.peek());
        }

        return result;
    }

    private static final class Itr<T> implements Iterator<T> {

        /**
         * The original stack being enumerated.
         */
        private final ImmutableLinkedStack<T> originalStack; // no need this.
        /**
         * The remaining stack not yet enumerated.
         */
        private ImmutableLinkedStack<T> remainingStack;

        /**
         * Initializes a new instance of the {@link Itr} class.
         *
         * @param originalStack The stack to enumerate.
         */
        public Itr(ImmutableLinkedStack<T> originalStack) {
            this.originalStack = originalStack;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean hasNext() {
            if (remainingStack == null) {
                return !originalStack.isEmpty();
            }

            return !remainingStack.isEmpty();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public T next() {
            if (remainingStack == null) {
                remainingStack = originalStack;
            }

            if (remainingStack.isEmpty()) {
                throw new NoSuchElementException();
            }

            T result = remainingStack.peek();
            remainingStack = remainingStack.pop();
            return result;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
http://cs.gmu.edu/~pammann/332/assigns/code/ImmutableStack.java
No need Map<Object, ImmutableStack> map.
public final class ImmutableStack {
   final private Object top;                 //  top of stack
   final private ImmutableStack remainder;   // rest of stack - null for empty stack

   final private static ImmutableStack emptyStack = new ImmutableStack(null, null);

   final private Map<Object, ImmutableStack> map = new HashMap<Object, ImmutableStack>();

   public static ImmutableStack getStack() { return emptyStack; }

   private ImmutableStack(Object top,  ImmutableStack remainder) {
     this.top = top;
     this.remainder = remainder;
   }

   public ImmutableStack push (Object e) {
     if (map.containsKey(e)) return map.get(e);

     ImmutableStack result;
     synchronized(this) {
        if (map.containsKey(e)) return map.get(e);   // double check idiom
        result = new ImmutableStack(e, this);
        map.put(e, result);
     }
     return result;
   }

   public ImmutableStack pop () {
     if (isEmpty()) throw new IllegalStateException("Stack.pop");
     return remainder;
   }

   public Object top () {
     if (isEmpty()) throw new IllegalStateException("Stack.top");
     return top;
   }

   public boolean isEmpty () {
     return (this.equals(emptyStack));
   }
}
http://blog.csdn.net/shoulinjun/article/details/31760103
有趣的函数式数据结构《一》----不可变栈
什么是不可变?往栈中插入一个元素,原来的栈保持不变,返回一个新的栈(已插入新的元素)。
push, pop,getMax 等操作都要求在 常数时间内完成。

可能读者会产生疑惑,既然要返回一个新的栈,是不是就必须先拷贝一份原来的栈,然后在新的栈中插入元素。
但是这样复杂度就是线性的,如何能够在常数时间内完成呢??
这里,就是immutable DATA STRUCTRUE 的强大。。

本文给出一个C++ 的实现版本,基本理想是利用内存共享,以及均摊时间的思想。
下图给出了实现的核心思想。
三个链表[1,2,3], [1,2,4], [1,2,5]共用节点1,2
这是因为一个节点可以有多个前驱节点指向它本身。。而栈的特性:仅仅在链表的头部插入删除的性质保证了节点的共享。



Read full article from Immutability, part 2: Creating a simple immutable stack – SLaks.Blog

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts