假设我有一个爬过数组的函数......
flatten([a, b, c, d, [e, f, g, [h, i, j, k], l], m, n, o, p])
>> [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]
Flatten会爬过代码,遇到的每个数组都会递归进入该数组,并返回值,使得你有一个平面数组。
这有效,直到我们有一个数组,如:
a = [];
a[0] = a;
这显然会产生无限递归:
Array[1]
0: Array[1]
0: Array[1]
0: Array[1]
0: Array[1]
0: Array[1]
0: Array[1]
...
如何在不修改数组的情况下检测此行为,以便函数可以处理此问题?
答案 0 :(得分:5)
如果检测递归是必需的,那么你将不得不为它交换内存空间:创建一个你解析的对象数组(递归发送的参数)并检查每个新参数。如果您已经解析了它,请立即返回。
答案 1 :(得分:2)
你必须在flatten()
函数中保留一个访问过的数组数组,并在你重新诅咒之前检查它们是否存在。您必须将访问过的元素列表作为第二个参数传递给recurse
。
function recurse(ar, visited) {
visited = visited || [];
function isVisited(obj) {
for (var i=0;i<visited.length;i++) {
if (visited[i] == obj) {
return true;
}
}
visted.push(obj); // but we've visited it now
return false;
}
// blah blah blah, do your thing... check isVisted[i]
}
检查你的数组是否很深会变得很昂贵,所以你可以在你访问的每个数组上作弊并设置一个属性,并检查它(但当然,你正在修改数组(但不是很显着) )。
function recurse(ar) {
function isVisited(obj) {
var key = 'visited';
var visited = obj.hasOwnProperty(key);
obj[key] = true; // but we've visited it now
return visited;
}
// blah blah blah, do your thing... check isVisted[i]
}
答案 2 :(得分:2)
跟踪已访问过的数组的一种便捷方法是添加一个&#34; flat&#34;每个属性的属性,可能会为每个操作设置一个唯一值。当每个数组已经是一个非常好的对象时,没有必要弄乱单独的地图。
答案 3 :(得分:1)
另一种更肮脏的方式(但最好的方法是使用JSON编码器):
var is_recursive = false;
try {
JSON.stringify(my_recursive_object);
} catch (e) {
if (e.name = 'TypeError')
is_recursive = true;
else
throw e;
}
我确实发现这个问题寻找更好的答案, - 虽然这可能有助于某人想要一个好的黑客; - )
答案 4 :(得分:0)
在现实世界中,第一步是找到那个让你自我引用的对象并用棍子击打它们的冒犯者。
然而,通过将它们作为自引用消除,问题得以解决。把它们变成副本。 Array.slice返回部分数组(包括完整副本)而不更改原始数据。
if(thisElement.constructor.prototype.slice){ //is it an array
thisElement = thisElement.slice(0); //now it's a new but identical array
}
请注意,这仅适用于主数组引用本身。内部数组引用仍然需要更改,因为复制的引用仍指向相同的内容。
此外,如果您可以对绝对不存在的字符进行假设,您可能会发现切片/连接非常有用。