view doodle/containers/list.d @ 40:1f97022e5c6d

Checkpoint. Development continues...
author daveb
date Mon, 12 Apr 2010 14:01:54 +0930
parents
children
line wrap: on
line source

module doodle.containers.list;

///
/// Doubly-linked list.
/// Consists of a sequence of cells with references to the previous and next member.
/// List keeps track of its length.
/// 
/// The list has two ends:
///     "head" and "tail".
/// There are two directions:
///     "forwards" -> towards the "head" and
///     "backwards" -> towards the "tail"
/// opApply works from the head to the tail.
/// opApplyReverse works from the tail to the head.
///
/// When used as a queue, typically use addTail and removeHead.
///
/// Design goals of list is to be completely symmetric and clear in its
/// forwards and backwards operations, and to be able to contain immutable types.
///
class List(T) {

    //
    // Standard operations:
    //

    /// Construct an empty list
    this() { }

    /// Return a copy of the list
    List!(T) dup() const {
        auto l = new List!(T);
        for (auto c = &mHead; c; c = &c.mBackward) {
            l.addTail(c.mElement);
        }
        return l;
    }

    /// Return true if the list is empty
    bool isEmpty() const {
        return mLength == 0;
    }

    /// Return the length of the list
    uint length() const {
        return mLength;
    }

    /// Return the head of the list, asserting if the list is empty
    T head() {
        assert(!isEmpty());
        return mHead.mElement;
    }

    /// Return the tail of the list, asserting if the list is empty
    T tail() {
        assert(!isEmpty());
        return mTail.mElement;
    }

    /// Add to the head of the list
    void addHead(T t) {
        add(mHead, null, t);
    }

    /// Add to the tail of the list
    void addTail(T t) {
        add(null, mTail, t);
    }

    /// Remove the head of the list, asserting if the list is empty
    T removeHead() {
        return remove(mHead).mElement;
    }

    /// Remove the tail of the list, asserting of the list is empty 
    T removeTail() {
        return remove(mTail).mElement;
    }

    // Clear the list (garbage collector does the rest)
    void clear() {
        mHead = mTail = null;
        mLength = 0;
    }

    /// Iterate over the list from head to tail - used by foreach
    int opApply(int delegate(ref T) dg) {
        int result;

        auto c = mHead;

        while (c !is null) {
            auto t = c.mElement;
            result = dg(t);
            if (result != 0) {
                break;
            }
            c = c.mBackward;
        }

        return result;
    }

    /// Iterate over the list from tail to head - used by foreach_reverse
    int opApplyReverse(int delegate(ref T) dg) {
        int result;

        auto c = mTail;

        while (c !is null) {
            auto t = c.mElement;
            result = dg(t);
            if (result != 0) {
                break;
            }
            c = c.mForward;
        }

        return result;
    }


    //
    // Iterator operations:
    //

    /// Return the first matching element going backward from head, or null
    Iterator findHead(T t) {
        for (auto c = mHead; c !is null; c = c.mBackward) {
            if (c.mElement == t) {
                return new Iterator(c);
            }
        }

        return null;
    }

    /// Return the first matching element going forward from tail, or null
    Iterator findTail(T t) {
        for (auto c = mTail; c !is null; c = c.mForward) {
            if (c.mElement == t) {
                return new Iterator(c);
            }
        }

        return null;
    }

    /// insert a new entry into list, forward of entry specified by iterator
    void insertForward(Iterator i, T t) {
        assert(i !is null);
        add(i.mCell, i.mCell.mForward, t);
    }

    /// insert a new entry into list, backward of entry specified by iterator
    void insertBackward(Iterator i, T t) {
        assert(i !is null);
        add(i.mCell.mBackward, i.mCell, t);
    }

    /// Remove specified entry from list, returning iterator to entry forward of removed one
    Iterator eraseForward(Iterator i) {
        assert(i !is null);
        Cell cell = remove(i.mCell);
        return cell.mForward ? new Iterator(cell.mForward) : null;
    }

    /// Remove specified entry from list, returning iterator to entry backward of removed one
    Iterator eraseBackward(Iterator i) {
        assert(i !is null);
        Cell cell = remove(i.mCell);
        return cell.mBackward ? new Iterator(cell.mBackward) : null;
    }

    ///
    /// Iterator for a List
    ///
    class Iterator {

        private Cell mCell;

        private this(Cell cell) {
            assert(cell !is null);
            mCell = cell;
        }

        T element() {
            return mCell.mElement;
        }

        void goForward() {
            assert(mCell.mForward !is null);
            mCell = mCell.mForward;
        }

        void goBackward() {
            assert(mCell.mBackward !is null);
            mCell = mCell.mBackward;
        }

        bool forwardValid() const {
            return mCell.mForward !is null;
        }

        bool backwardValid() const {
            return mCell.mBackward !is null;
        }

        override bool opEquals(Object other) const {
            Iterator o = cast(Iterator) other;
            if (!o) return 0;
            return mCell is o.mCell;
        }
    }

    //
    // Implementation:
    //

    private {
        Cell mHead;
        Cell mTail;
        uint mLength;

        /// An entry in the list
        class Cell {
            Cell mForward, mBackward;
            T mElement;

            this(T element, Cell backward, Cell forward) {
                mElement = element;
                mBackward = backward;
                mForward = forward;
            }
        }

        /// Add new cell between backward and forward
        void add(Cell backward, Cell forward, T t) {
            Cell cell = new Cell(t, backward, forward);

            if (backward) {
                backward.mForward = cell;
            }
            else {
                mTail = cell;
            }

            if (forward) {
                forward.mBackward = cell;
            }
            else {
                mHead = cell;
            }

            ++mLength;
        }

        /// Remove a cell from the list, returning the removed cell
        Cell remove(Cell cell) {
            if (cell.mBackward) {
                cell.mBackward.mForward = cell.mForward;
            }
            else {
                mTail = cell.mForward;
            }

            if (cell.mForward) {
                cell.mForward.mBackward = cell.mBackward;
            }
            else {
                mHead = cell.mBackward;
            }

            --mLength;

            return cell;
        }
    }
}