有没有办法返回JavaScript数组的其余部分

时间:2019-01-26 13:26:29

标签: javascript arrays

是否有一种方法可以在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)可以保留此类算法的内存使用,并保留我想采用的递归功能样式。

4 个答案:

答案 0 :(得分:1)

否,这通常是不可能的。没有“查看”或“指向”普通数组的指针 1

您可能使用Proxy来伪造它,但我怀疑这是个好主意。

1:在typed arrays(这是备份缓冲区上的视图)上执行此操作很简单,但是请注意,您无法push对其进行操作。


  

我可能需要它,并且我希望将其用于当前使用arr.slice(1)的递归功能样式算法,但希望保持较低的内存使用量

实际上,所有这些实现的内存使用率都很低-它们分配的内存不多于输入。但是,反复调用slice(1)确实会给垃圾收集器造成很大压力。

如果您想提高效率,我建议

  • 避免递归。 JS引擎仍未实现尾部递归,因此递归并不便宜。
  • 不传递数组(的新副本)。只需传递一个开始的索引,例如通过使用内部递归函数来关闭数组参数并访问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;
}