我有一个像这样的立方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()
中运行时生成的代码的开销是否会抵消不循环的好处?
size
和N
都是常量,所以重复的乘法和迭代感觉就像只能做一次。
答案 0 :(得分: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('+'));
})();
答案 1 :(得分:1)
考虑一下:
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 );
};
}());
因此,您事先创建[0, size, size*size, size*size*size, ...]
数组,然后在每次调用时,在该数组和p
数组上执行点积。
我在代码中使用了下划线的range函数。
答案 2 :(得分: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
}();
这为您提供了两全其美的效果:new ArrayND(s, d)
仍然看起来和行为类似于普通数组,但也为您提供get
和set
个访问者,可以使用任意数量的运行时的索引参数 - 所有这些都不需要修改内置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');