2012年5月9日星期三

代码练习 - 容器

一组简单的代码练习,实现了基本的容器类型。
小小的活动手指,了解相关的运行调试编译环境。


* 代码练习一,简单的双向链表。

增删元素时,节点对前后节点的引用可千万别弄错呦。
包括自己的以及前后节点的都得修改,有遗漏就糟糕了。
依赖调试就不好了,得能够手写一遍就正确才可以的。

class DList(object):
    """
    Doubly linked list
    link: http://www.w3schools.com/jsref/jsref_obj_array.asp
    """
    class Node:
        def __init__(self,item,prev,next):
            self.item = item
            self.prev = prev
            self.next = next
    def __init__(self):
        self.sentinel = self.Node(None,None,None)
        self.sentinel.prev = self.sentinel
        self.sentinel.next = self.sentinel
        self.len = 0
    def length(self):
        for k,v in enumerate(self,1):
            pass
        else:
            assert k == self.len
            return k
    def __len__(self):
        return self.len
    def shift(self):
        if self.sentinel.next is self.sentinel:
            raise Exception("empty")
        self.len -= 1
        node = self.sentinel.next
        self._remove(node)
        return node.item
    def unshift(self,item):
        self.len += 1
        #self._insert(item,self.sentinel.next)
        self.sentinel.next = self.Node(item,self.sentinel,self.sentinel.next)
        self.sentinel.next.next.prev = self.sentinel.next
        return self.len
    def push(self,item):
        self.len += 1
        self._insert(item,self.sentinel)
        #self.sentinel.prev = self.Node(item,self.sentinel.prev,self.sentinel)
        #self.sentinel.prev.prev.next = self.sentinel.prev
        return self.len
    def pop(self):
        if self.sentinel.prev is self.sentinel:
            raise Exception("empty")
        self.len -= 1
        node = self.sentinel.prev
        self._remove(node)
        #self.sentinel.prev.prev.next = self.sentinel.prev.next
        #self.sentinel.prev = self.sentinel.prev.prev
        return node.item
    def indexOf(self,item):
        for k,v in enumerate(self):
            if v==item:
                return k
        else:
            return -1
    def lastIndexOf(self,item):
        for k,v in enumerate(reversed(self)):
            if v==item:
                return k
        else:
            return -1
    def slice(self,start,end):
        l = self.__class__()
        length = self.length()
        begin = start+length if start<0 else start
        stop = end+length if end<0 else end
        for k,v in enumerate(self):
            if k >= stop:
                break
            if k >= begin:
                l.push(v)
        return l
    @classmethod
    def _remove(cls,node):
        node.prev.next = node.next
        node.next.prev = node.prev
    @classmethod
    def _insert(cls,item,node):
        node.prev.next = cls.Node(item,node.prev,node)
        node.prev = node.prev.next
    def splice(self,start,howmany,*items):
        l = self.__class__()
        length = self.length()
        begin = start+length if start<0 else start
        stop = begin + howmany
        k, p = 0, self.sentinel.next
        while p is not self.sentinel:
            if k >= stop:
                break
            if k >= begin:
                l.push(p.item)
                self._remove(p)
                self.len -= 1
            p = p.next
            k += 1
        for i in items:
            self.len += 1
            self._insert(i,p)
        return l
    def _copy(self):
        l = self.__class__()
        for i in self:
            l.push(i)
        return l
    def concat(self,*lists):
        l = self._copy()
        for i in lists:
            for j in i:
                l.push(j)
        return l
    def join(self,sep):
        assert hasattr(sep,'join')
        return sep.join(map(str,self))
    def reverse(self):
        p = self.sentinel
        while True:
            q = p.next
            p.prev, p.next = p.next, p.prev
            p = q
            if p is self.sentinel:
                break
    def sort(self,cmp=lambda x,y:x-y):
        rest = self.sentinel.next
        while rest is not self.sentinel:
            m, p =rest, rest.next
            while p is not self.sentinel:
                if cmp(p.item,m.item)<0:
                    m = p
                p = p.next
            if rest is m:
                rest = m.next
            #self._remove(m);self._insert(m.item,rest)
            m.prev.next, m.next.prev = m.next, m.prev
            m.prev, m.next = rest.prev, rest
            rest.prev.next, rest.prev = m, m
    def __iter__(self):
        p = self.sentinel.next
        while p is not self.sentinel:
            yield p.item
            p = p.next
    def __reversed__(self):
        p = self.sentinel.prev
        while p is not self.sentinel:
            yield p.item
            p = p.prev
    def __str__(self):
        return ','.join(map(str,self))
    def __repr__(self):
        return '[%s]'%', '.join(map(str,self))
    def valueOf(self):
        return str(self)
    def toString(self):
        return repr(self)
    def map(self,proc):
        l = self.__class__()
        for i in self:
            l.push(proc(i))
        return l
    def forEach(self,proc):
        for i in self:
            proc(i)

这里通过私有的remove和insert方法来增删节点比直接增删节点要不容易错一些。






* 代码小练习二,仿JavaScript数组的实现。

接着前面的那一个再做个小练习,不过咱好菜呀,居然写得好费尽的感觉(沮丧。
下标和范围什么的总是搞错,单个函数一长的话就头晕晕的了,而且最后还是依赖调试了。
这样是不行的,真的不行的,在非类型安全的语言或者纸张上书写的话这样就写不出正确的代码是不行的。
嘛,或许再熟练一些就会好一些吧,对于常见的情况抽象为函数或固定的代码。恩,或许就是这样的。

<script>
var print = function(x) {
    document.write(x);
    document.writeln("<br>");
};
var p = print;
print("hello world");
function JArray() {
    var that = (this instanceof arguments.callee) ? this : new arguments.callee();
    if(arguments.length == 1 && this instanceof arguments.callee)
        that.length = arguments[0];
    else {
        that.length = 0;
        for(var i = 0; i < arguments.length; i++)
            that.push(arguments[i])

    }
    return that;
};
JArray.prototype.push = function(item) {
    this[this.length] = item;
    return ++this.length;
};
JArray.prototype.pop = function(item) {
    if(this.length == 0)
        return undefined;
    this.length--;
    var item = this[this.length];
    delete this[this.length];
    return item;
};
JArray.prototype.shift = function() {
    if(this.length == 0)
        return undefined;
    var item = this[0];
    for(var i = 0; i < this.length; i++)
        this[i] = this[i + 1];
    this.length--;
    delete this[this.length]
    return item;
};
JArray.prototype.unshift = function(item) {
    for(var i = this.length; i > 0; i--)
        this[i] = this[i - 1];
    this[0] = item;
    return ++this.length;
};
JArray.prototype.join = function(sep) {
    var sep = sep || ',';
    var string = this.length == 0 ? "" : this[0];
    for(var i = 1; i < this.length; i++) {
        string += sep + this[i];
    };
    return string;
};
JArray.prototype.toString = function(item) {
    return this.join();
};
JArray.prototype.slice = function(start, end) {
    var array = new this.constructor();
    var start = start < 0 ? start + this.length : start;
    var end = end || this.length;
    end = end < 0 ? end + this.length : end;
    for(var i = start; i < end && i < this.length; i++)
        array.push(this[i]);
    return array;
};
JArray.prototype.indexOf = function(item) {
    for(var i = 0; i < this.length; i++)
        if(this[i] == item)
            return i;
    return -1;
};
JArray.prototype.lastIndexOf = function(item) {
    for(var i = this.length - 1; i >= 0; i--)
        if(this[i] == item)
            return i;
    return -1;
};
JArray.prototype.concat = function() {
    var array = new this.constructor();
    for(var i = 0; i < this.length; i++)
        array.push(this[i]);
    for(var i = 0; i < arguments.length; i++)
        for(var j = 0; j < arguments[i].length; j++)
            array.push(arguments[i][j]);
    return array;
};
JArray.prototype.splice = function(index, howmany, item1) {
    var array = new this.constructor();
    var index = index < 0 ? index + this.length : index;
    var howmany = Math.min(this.length - index, howmany);
    var end = index + howmany;
    for(var i = index; i < end; i++)
        array.push(this[i]);
    var itemnum = arguments.length - 2;
    if(howmany > itemnum) {
        var eat = howmany - itemnum;
        for(var i = end; i < this.length; i++)
            this[i - eat] = this[i];
        for(var i = this.length - eat; i < this.length; i++)
            delete this[i];
        this.length -= eat;
    } else {
        var room = itemnum - howmany;
        for(var i = this.length - 1; i >= index; i--)
            this[i + room] = this[i];
        this.length += room;
    }
    for(var i = 0; i < itemnum; i++)
        this[index + i] = arguments[i + 2];
    return array;
};
JArray.prototype.reverse = function(start, howmany, item1) {
    for(var i = 0, j = this.length - 1; i < j; i++, j--) {
        var tmp = this[i];
        this[i] = this[j];
        this[j] = tmp
    };
};
JArray.prototype.sort = function(cmp) {
    var cmp = cmp || (function(x, y) {
        return x < y ? -1 : 1
    });
    var partition = function(array, x, y) {
        var pivot = array[x];
        var z = x;
        for(var i = x + 1; i < y; i++) {
            var tmp = array[i];
            if(cmp(tmp, pivot) < 0) {
                z++;
                array[i] = array[z];
                array[z] = tmp;
            }
        }
        array[x] = array[z];
        array[z] = pivot;
        return z;
    };
    var qsort = function(array, x, y) {
        if(x < y) {
            var z = partition(array, x, y);
            qsort(array, x, z);
            qsort(array, z + 1, y);
        }
    };
    qsort(this, 0, this.length);
    return this;
};
JArray.prototype.forEach = function(proc) {
    for(var i = 0; i < this.length; i++)
        proc(this[i])
};
JArray.prototype.every = function(proc) {
    for(var i = 0; i < this.length; i++)
        if(!proc(this[i]))
            return false;
    return true;
};
</script>

测试代码没有贴,不过最好其实还是给每个方法都对应几种情况写好自动测试为好吧这样。
好写细节和js本身的Array不一样,比如一些可选参数的情况,这里仅做拙劣的练习就不完善它了。
link: https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.6#Array_extras



* 代码小练习三,简单的关联数组。

一个简单的关联数组,key限定为字符串类型。


#include <iostream>
#include <cstring>
using namespace std;
template<class T>
class Hash{
    class NotFound{};
    class Full{};
    struct Entry{
        const char* key;
        T value;
        };
    int _capacity;
    Entry* table;
    Entry* search(const char* key){
        int hash = strhash(key)%_capacity;
        for(int i=hash;i<hash+_capacity;i++){
            Entry* entry = &table[i%_capacity];
            if(entry->key==NULL || streq(entry->key,key))
                return entry;
        }
        return NULL;
    }
public:
    static int strhash(const char* str){
        int hash = 0;
        for(const char* p = str; *p; p++)
            hash = 31 * hash + *p;
        return hash;
        }
    static bool streq(const char* str1,const char* str2){
            return !strcmp(str1,str2);
        }
    Hash(int capacity)
        :_capacity(capacity),table(new Entry[capacity]){
            for(int i=0;i<capacity;i++)
                table[i].key = NULL;
        }
    ~Hash(){
        delete[] table;
        }
    void put(const char* key,T value){
        Entry* entry = this->search(key);
        if(entry){
            if(entry->key==NULL)
                entry->key = key;
            entry->value = value;
        }else
            throw Full();//"Full";
        }
    void del(const char* key){
        Entry* entry = this->search(key);
        if(entry && entry->key){
            entry->key=NULL;
        entry->value=NULL;
        }
    }
    T get(const char* key){
        Entry* entry = this->search(key);
        if(entry && entry->key)
            return entry->value;
        else
            throw NotFound();//"Not Found";
    }
    bool has(const char* key){
        Entry* entry = this->search(key);
        return entry && entry->key;
    }
    };
int main(int argc, char **argv)
{
    cout<<"hello world"<<endl;
    Hash<int> hash(100);
    cout<<hash.has("a")<<endl;
    hash.put("a",125);
    hash.put("b",128);
    cout<<hash.has("a")<<endl;
    cout<<hash.has("b")<<endl;
    cout<<hash.get("b")<<endl;
    hash.del("b");
    cout<<hash.has("b")<<endl;
    //cout<<hash.get("c")<<endl;
    return 0;
}



* 代码练习四,优先队列。

没有评论: