在为简单语言构建JavaScript解释器的过程中,我遇到了以下问题;
解析后,我们得到一个索引数组,指定要修改的n维数组中的元素。例如,在解析之后:
a[1, 1, 1]
我们得到一个数组[1, 1, 1]
。我正在处理的语言没有变量定义,因此变量在首次使用时会被初始化。我的目标是能够创建这个n维数组,以便我可以将它放在变量表中(在上面的例子中,我们需要创建一个三维数组)。
eval()
创建一个n维数组?< / p>
答案 0 :(得分:11)
在Chrome中测试:
function createNDimArray(dimensions) {
if (dimensions.length > 0) {
var dim = dimensions[0];
var rest = dimensions.slice(1);
var newArray = new Array();
for (var i = 0; i < dim; i++) {
newArray[i] = createNDimArray(rest);
}
return newArray;
} else {
return undefined;
}
}
然后createNDimArray([3, 2, 5])
返回一个3x2x5数组。
您可以使用类似的递归过程来访问索引在数组中的元素:
function getElement(array, indices) {
if (indices.length == 0) {
return array;
} else {
return getElement(array[indices[0]], indices.slice(1));
}
}
设置元素类似,并留给读者练习。
答案 1 :(得分:5)
内置任何东西,但创建一个可以完成这项工作的功能非常容易:
var genArray = function () {
var arr, len, i;
if(arguments.length > 0) {
len = [].slice.call(arguments, 0, 1)[0];
arr = new Array(len);
for(i = 0; i < len; i++) {
arr[i] = genArray.apply(null, [].slice.call(arguments, 1));
}
} else {
return null; //or whatever you want to initialize values to.
}
return arr;
};
var a = genArray(3, 2); //is [[null, null],[null, null],[null, null]]
var b = genArray(3, 1, 1); //is [[[null]],[[null]],[[null]]]
a[0][1]; //is null
b[1][0][0]; //is null
b[1][0][0] = 3;
b[1][0][0]; //is 3;
b; //is [[[null]],[[3]],[[null]]]
也许这会有所帮助?
PS -
我知道这似乎比必要的努力更多。但不幸的是,JavaScript数组并不是真正的“数组”(如果用“数组”表示连续的,索引的,不可变的内存块)。在大多数语言中,它们更像是“地图”。因此创建它们需要花费一定的精力。大多数语言创建多维数组都没有问题,因为它们只是进行了一些简单的乘法,然后是malloc()
。但是使用JavaScript,如果你想预先构建它们,你必须以递归方式生成数组。这很痛苦,但它确实展示了口译员所需的努力。
去图。
答案 2 :(得分:1)
用于创建n维数组:
function createNDimArray(dimensions) {
var ret = undefined;
if(dimensions.length==1){
ret = new Array(dimensions[0]);
for (var i = 0; i < dimensions[0]; i++)
ret[i]=null; //or another value
return ret;
}
else{
//recursion
var rest = dimensions.slice(1);
ret = new Array(dimensions[0]);
for (var i = 0; i < dimensions[0]; i++)
ret[i]=createNDimArray(rest);
return ret;
}
}
答案 3 :(得分:1)
编辑:由于任何递归解决方案都会限制您可以创建的数组大小...我在我的 PJs @ GitHub库中创建了另一个解决方案。这个以伪即时速度运行,可以创建和管理任何大小的任何结构的多维数组,任何分支都有任何维度。它还可以模拟预填充和/或使用自定义设计的节点对象。请在此处查看:https://github.com/PimpTrizkit/PJs/wiki/14.-Complex-Multidimensional-Object--(pCMO.js)
使用jfabrizio解决方案的修改版本:
function createNDimArray(dimensions) {
var t, i = 0, s = dimensions[0], arr = new Array(s);
if ( dimensions.length < 3 ) for ( t = dimensions[1] ; i < s ; ) arr[i++] = new Array(t);
else for ( t = dimensions.slice(1) ; i < s ; ) arr[i++] = createNDimArray(t);
return arr;
}
用途:
var arr = createNDimArray([3, 2, 3]);
// arr = [[[,,],[,,]],[[,,],[,,]],[[,,],[,,]]]
console.log(arr[2][1]); // in FF: Array [ <3 empty slots> ]
console.log("Falsy = " + (arr[2][1][0]?true:false) ); // Falsy = false
我发现这要快得多。我可能会说,这可能是在Javascript中生成N维数组的最快方法。上面的重构有一些很好的速度提升。但是,最好的速度提升来自于预先填充,当然。此版本不预先填充阵列。它只返回一个完全创建的Ns长度的N维数组,其中最后一级只是一个空数组。如果你真的需要arr[x][y][z]?arr[x][y][z]:null
值,我希望null
就足够了。这是我的用途。 :)
如果需要预填充,请使用其原始版本。
而且,如果你真的不关心我做了什么;然后停止阅读。
想要更多极客谈话?关于那些在那里学习的人的递归的一点点。好的就是战术。在进行深度递归时,请记住最终级别。它的大部分工作都在这里完成。在这种情况下,它的第N个维度,字面上。这是你的“有效载荷”,其余的是物流。在jfab的函数中,当dimensions.length
到达1
时,它的最后一个维度,它在第N个维度中并执行有效负载。这是创建空数组,或者在我的情况下,是一个空数组。由于递归变得如此之深,每个维度都是最后一个维度的一个因素。当你到达Nth维度时,你会有很多函数调用,而后勤对计算机来说变得很麻烦。在Nth维度,您将为有效负载调用基本递归函数(在我们的情况下为createNDimArray
),而不是物流。现在,就像在jfab的原始解决方案中一样,将有效负载的执行作为你在递归中做的第一件事(如果可能的话)通常是一件好事,特别是如果简单的话。在这里,通过使有效载荷成为最终2D阵列的构建(而不仅仅是仅通过返回new Array()
的1D阵列)。然后,过多的函数调用现在不必在此级别发生。当然,现在,如果要预先填充数组,则此快捷方式并不总是有用。但更重要的是,预填充阵列将是适当的有效载荷。通过不访问第N维上的每个项目,我们已经有效地删除了它。这样,有一个较少级别的函数调用,基本上第N个维度的有效负载实际上是在第N-1维度上完成的。我们永远不会再次调用递归函数来传递new Array()
。不幸的是,对new Array(x)
(一般情况下)的调用并不是这样。它的执行时间会随着x
的增加而增加。这仍然有效地访问了Nth Dimension中的每个项目,但是现在我们只使用本机代码执行一次,并且包含在紧密且轻微的循环中。现在我们要求createNDimArray
只能用N&gt;来调用1,即从未用于创建1D数组。从理论上讲,你可能需要更大的N,并在最后展开更多尺寸。基本上,带有if ( dimensions.length < 3 )
的行会读取类似< 4
或< 5
的内容,您必须将那些更多的for
循环包围在那里,并且每个所有这些都需要他们自己的var
s的集合 - 所以我不确定它的效率是多少,因为你正在交易过多的函数调用和堆栈空间/操作与类似的想法,但在嵌入式{{1 }}}但是我认为如果你知道N总是高于一定水平,或者它只是最终尺寸,它可以加速一些环境。就像在这里,我做了最后两个维度。但如果你展开太多,那么你的有效载荷本身就是一只熊。只有测试才能证明这是否值得。看起来堆栈空间似乎有限,我想我已经记得已经能够制作出更多展开的更大阵列。您可以制作阵列的数量有限。如果我这样做,那么在Nth级别为每个项目调用自己的递归解决方案具有最低限制。回想..正确....低得多。
修改他的解决方案的下一部分只是物流,它只是一个简单的重构,可以摆脱过多的块和代码。加入所有for
工作,就是这样。由于您需要返回var
,因此一旦循环结束,您的所有arr
也可能首先在一行上工作,幸运的是,四个var
中的三个具有相同的功能初始化。请记住,如果可能,Javascript可以在加入var
时优化代码。这也使得代码更小。
PT
答案 4 :(得分:0)
使用createNDimArray
,map
和apply
函数的另一版bind
:
function createNDimArray(dims) {
return dims.length === 1
? new Array(dims[0])
: Array.apply(null, Array(dims[0])).map(createNDimensionalArray.bind(null, dims.slice(1)));
}
createNDimArray([3, 2, 5]); // returns 3x2x5 array
答案 5 :(得分:0)
创建ND阵列需要克隆嵌套的ND阵列。因此,您需要一个正确的Array.prototype.clone()
方法,其余的很容易。据我所知,以下是JS中最简单,最有效的方法。
Array.prototype.clone = function(){
return this.reduce((p,c,i) => (p[i] = Array.isArray(c) ? c.clone() : c, p),[])
}
function arrayND(...n){
return n.reduceRight((p,c) => c = (new Array(c)).fill().map(e => Array.isArray(p) ? p.clone() : p ));
}
var NDarr = arrayND(4,4,4,4,"."); // size of each dimension and the init value at the end
console.log(JSON.stringify(NDarr))
NDarr[0][1][2][3] = "kitty"; //access any location and change.
console.log(JSON.stringify(NDarr))
答案 6 :(得分:0)
这里有很好的答案,但是随着JavaScript的更改,这是使用JavaScript中的一些更新功能解决此问题的另一种方法。
function nArray (dem, size=dem, fill=null, currDepth=0) {
const arr = new Array(size).fill(fill);
return (currDepth+1 === dem) ? arr : arr.map(i => nArray(dem, size, fill, currDepth+1));
};
dem
是数组的尺寸。
size
是每个尺寸的大小,默认情况下,它是dem
的值。
fill
是将作为默认填充值的值。
currDepth
不可用于函数的递归性质。
答案 7 :(得分:0)
使用默认值创建n维矩阵数组
function arr (arg, def = 0){
if (arg.length > 2){
return Array(arg[0]).fill().map(()=>arr(arg.slice(1)));
} else {
return Array(arg[0]).fill().map(()=>Array(arg[1]).fill(def));
}
}
//simple usage -> fills with 0
var s = arr([3,5,8,4]) // 4 dimensions
var t = arr([5,7]) // 2 dimensions
//fill with null
var k = arr([4,7,9] , null) // 3 dimensions
答案 8 :(得分:0)
如果您需要在每个集群中创建索引从0到4的4d数组,请执行以下代码:
function createNDimArray(dimensions) {
if (dimensions.length > 0) {
var dim = dimensions[0];
var rest = dimensions.slice(1);
var newArray = new Array();
for (var i = 0; i < dim; i++) {
newArray[i] = createNDimArray(rest);
}
return newArray;
} else {
return undefined;
}
}
var MyArray=createNDimArray([5, 5, 5, 5]);
//returns a 5x5x5x5 array with index from 0 to 4;
MyArray[4][4][4][4]="MyArray 4d MyValue";
alert(MyArray[4][4][4][4]);
//For 5-demension array with this param.: 5x4x3x2x2 -> do this:
var MyArray_5d=createNDimArray([5, 4, 3, 2, 2]);
MyArray_5d[4][3][2][1][1]="MyArray 5d MyValue";
alert(MyArray_5d[4][3][2][1][1]);