是否有一种方法可以在JavaScript中返回数组的其余部分,即由除数组的第一个元素之外的所有元素组成的数组部分?
注意:我不要求返回新的数组,例如与arr.slice(1)
等,我不想砍掉数组的第一个元素,例如与arr.shift()
。
例如,给定数组[3, 5, 8]
,数组的其余部分为[5, 8]
,如果数组的其余部分被更改,例如通过分配(破坏性操作),数组也会更改。我只是想知道,作为证明其余部分是数组的其余部分的测试,但不是由该数组的其余元素组成的新数组。
注意:下面的代码示例用于描述我想要的内容,但不是专门描述我想要的内容(即不是我想要执行的操作)。我想做的是在底部的every
算法中。
var arr = [3, 5, 8];
var rest = rest(arr); // rest is [5, 8]
rest.push(13); // rest is [5, 8, 13] and hence the arr is [3, 5, 8, 13]
我可能需要一个示例,我希望它遵循算法以及我在其中写的其他GitHub organization,在这两个示例中,我总是使用arr.slice(1)
:
function every(lst, f) {
if (lst.length === 0) {
return false;
} else {
if (f(lst[0]) === true) {
return every(lst.slice(1), f);
} else {
return false;
}
}
}
我认为使用我要的东西代替arr.slice(1)
可以保留此类算法的内存使用,并保留我想采用的递归功能样式。
答案 0 :(得分:1)
否,这通常是不可能的。没有“查看”或“指向”普通数组的指针 1 。
您可能使用Proxy
来伪造它,但我怀疑这是个好主意。
1:在typed arrays(这是备份缓冲区上的视图)上执行此操作很简单,但是请注意,您无法push
对其进行操作。
我可能需要它,并且我希望将其用于当前使用
arr.slice(1)
的递归功能样式算法,但希望保持较低的内存使用量
实际上,所有这些实现的内存使用率都很低-它们分配的内存不多于输入。但是,反复调用slice(1)
确实会给垃圾收集器造成很大压力。
如果您想提高效率,我建议
array[i]
而不是array[0]
。有关示例,请参见@Pointy的最新答案。如果您正在寻找更实用的样式,我建议您使用folds。 (在JavaScript中也称为reduce
,但如果您想懒惰则可能需要自己滚动)。用fold
来实现算法,然后可以很容易地将fold
换成更有效(例如迭代)的实现。
最后但并非最不重要的一点是,为了获得更高的效率,同时又保持递归样式,可以使用iterators。它们的界面可能看起来并不是特别有用,但是如果您坚持认为,可以轻松创建一个不可变的包装器,从而懒惰地生成一个链表。
答案 1 :(得分:0)
根据问题更新中发布的代码,很清楚为什么您可能希望“别名”数组的一部分。这是我将如何解决您的实施中(正确)感知到的效率问题的一种更典型的选择:
function every(lst, f) {
function r(index) {
if (index >= lst.length)
return true; // different from OP, but I think correct
return f(lst[index]) && r(index+1);
}
return r(0);
}
这仍然是该问题的递归解决方案,但是没有进行数组复制;数组完全不变。即使在更具特征性的功能编程语言中,通用模式也是常见的(Erlang亲自想到):用于某些递归代码的“公共” API由“内部”或“私有” API扩展,该API提供了一些额外的工具来跟踪递归的进度。
您正在寻找Array.prototype.shift
。
var arr = [1, 2, 3];
var first = arr.shift();
console.log(first); // 1
console.log(arr); // [2, 3]
这是一个线性时间操作:执行成本与原始数组的长度有关。对于大多数小型阵列来说,这并不重要,但是如果您在大型阵列上进行大量此类工作,则可能需要探索更好的数据结构。
请注意,使用普通数组无法创建与另一个数组重叠的新“阴影”数组。您可以对类型化数组执行类似的操作,但对于大多数代码而言,通用使用类型化数组有些尴尬。
类型化数组的第一个限制是它们当然是 typed ,这意味着在后备存储缓冲区中的数组“视图”仅提供一种一致类型的值。第二个限制是,唯一可用的类型是数字类型:各种“物理”(存储)大小的整数和浮点数。第三个限制是类型数组的大小是固定的。您必须先创建新的后备缓冲区并进行复制,才能扩展数组。
对于FORTRAN程序员来说,这样的限制是很熟悉的。
因此,要创建一个可容纳5个32位整数的数组,请编写
var ints = new Int32Array(5);
只要将类型正确(足够接近),就可以像将值放入普通数组一样将值放入数组:
for (let i = 0; i < 5; i++)
ints[i] = i;
console.log(ints); // [0, 1, 2, 3, 4]
现在:要执行OP所要求的操作,您需要从我们刚刚创建的数组中获取缓冲区,然后在同一缓冲区的顶部以一个偏移量创建一个新的类型化数组。不管用于创建原始数组的类型如何,执行此操作时的偏移量始终以字节为单位。这对于查看浮点值的各个部分以及其他“位敲打”类的工作非常有用,尽管在普通的JavaScript编码中当然不会太多。无论如何,要从原始问题中获得类似rest
的数组:
var rest = new Int32Array(ints.buffer, 4);
在该语句中,“ 4”表示新数组将是从缓冲区开始的4个字节的缓冲区视图。 32位整数为4个字节长,这意味着新视图将跳过原始数组的第一个元素。
答案 2 :(得分:0)
由于JavaScript无法做到这一点,因此唯一有效的解决方案是WebAssembly。否则,请使用Proxy。
答案 3 :(得分:0)
请测试此功能
function rest(arr) {
var a = arr.slice(1);
a.push = function() {
for (var i = 0, l = arguments.length; i < l; i++) {
this[this.length] = arguments[i];
arr[this.length] = arguments[i];
}
return this.length;
};
return a;
}