3

我有一个像这样的立方 3D 数组“类”:

function Array3D(size) {
    this.data = new Array(size*size*size);
    var makeIndex = function(p) {
        return p[0] + p[1]*size + p[2]*size*size;
    }
    this.get = function(p) { return this.data[makeIndex(p)]; };
    this.set = function(p, value) { this.data[makeIndex(p)] = value; };
}

我想推广到多个维度,但不影响访问性能。这是我的简单方法:

function ArrayND(size, N) {
    var s = 1;
    for(var i = 0; i < N; i++) s *= size;
    this.data = new Array(s);

    var makeIndex = function(p) {
        var ind = 0;
        for(var i = N-1; i >= 0; i--)
            ind = ind*size + p[i];
        return ind;
    }
    this.get = function(p) { return this.data[makeIndex(p)]; };
    this.set = function(p, value) { this.data[makeIndex(p)] = value; };
}

有什么方法可以“展开”我的makeIndex函数,以便在声明时对循环进行一次评估,而不是在调用时?使用运行时生成的代码的开销是否会eval抵消new Function()不循环的好处?

两者sizeN本质上都是常数,因此重复的乘法和迭代感觉就像只能完成一次。

4

3 回答 3

1

Consider this:

var makeIndex = (function () {
    var arr = _.range( N ).map(function ( i ) {
        return Math.pow( size, i );
    });

    function dotProduct ( a, b ) {
        var sum = 0;
        for ( var i = 0, l = a.length; i < l; i++ ) {
            sum += a[i] * b[i];
        }
        return sum;
    }

    return function ( p ) {
        return dotProduct( p, arr );
    };
}());

So you create the [0, size, size*size, size*size*size, ...] array beforehand, and then on each invocation, you perform a dot product on that array and the p array.

I use underscore's range function in my code.

于 2012-12-02T18:10:05.650 回答
1

这是执行此操作的一种方法,它使用以下方法将函数内容构建为字符串new Function

var makeIndex = (function() {
    var code = []
    var scale = 1;
    for(var i = 0; i < N; i++) {
        code.push("p["+i+"]*"+scale);
        scale *= size;
    }
    return new Function('p', 'return '+code.join('+'));
})();
于 2012-12-02T14:05:02.927 回答
0

传统的多维数组方法有什么问题?

function ArrayND(size, N, fill) {
    if (N < 1) throw new Error('Arrays must have at least one dimension.');
    if (size < 1) throw new Error('Arrays must have at least one element.');
    var arr = new Array(size);
    populate(arr, 1);

    function populate(a, depth) {
        for (var i = 0; i < size; i++) {
            if (depth < N) {
                a[i] = new Array(size);
                populate(a[i], depth+1);
            } else a[i]=fill;
        }
    }

    return arr;
}

这将返回一个访问起来更加直观的多维数组(可以选择填充默认值):

var arr = ArrayND(5, 3, 'hi');

console.log(arr[0][1][2]); // => 'hi'
arr[0][1][3] = 'mom';

更新:由于您的目标是通过提供任意长度的参数来访问多维数组,因此我将使用这种方法:

!function() {
    function ArrayND(size, N, fill) {
        if (N < 1) throw new Error('Arrays must have at least one dimension.');
        if (size < 1) throw new Error('Arrays must have at least one element.');
        if (!(this instanceof ArrayND)) return new ArrayND(size, N, fill); // allow this ctor to be called without `new` operator

        var arr = this;
        arr.length = size;
        populate(arr, 1);

        function populate(a, depth) {
            for (var i = 0; i < size; i++) {
                if (depth < N) {
                    a[i] = new Array(size);
                    populate(a[i], depth+1);
                } else a[i]=fill;
            }
        }

        return arr;
    }

    var proto = Object.create(Array.prototype); // polyfill necessary for older browsers, see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create#Polyfill

    proto.get = function(indicies) {
        var pos = this;
        for (var i = 0; i < indicies.length; i++) {
            pos = pos[indicies[i]];
        }
        return pos;
    };
    proto.set = function(indicies, value) {
        var pos = this;
        for (var i = 0; i < indicies.length - 1; i++) {
            pos = pos[indicies[i]];
        }
        pos[indicies[indicies.length-1]] = value;
    }
    ArrayND.prototype = proto;

    this.ArrayND = ArrayND; // export to global scope
}();

这为您提供了两全其美:anew ArrayND(s, d)仍然看起来和行为像一个普通数组,但也为您getset访问器提供了可以在运行时采用任意数量的索引参数——所有这些都无需修改内置的Array

var arr = new ArrayND(5, 3, 'hi');

console.log(arr[0][1][2]); // => hi
console.log(arr.get([0,1,2]); // => hi

arr.push([]); // => 6 (the new length)
arr.set([6,0], 'I was added');
于 2012-12-02T16:41:48.687 回答