В разных языках программирования это реализовано в основном через слайсы или итераторы.
golang:
a = a[1:]
C:
int* base_a = a;
a = a+1;
// ...
free(base_a);
C++:
vector<int> a = {1,2,3};
vector<int>::iterator it = a.begin();
it += 1;
C#:
// List
var a = new List<int>(){1,2,3};
// ... or static array
// var a = new int[]{1,2,3};
a = a.AsSpan().Slice(1);
// a = a.AsSpan()[1..];
Особый интерес в рамках этой задачи представляет js, в котором нет в общем случае единого способа получить сдвинутый на 1 подмассив, но он предоставляет такие способы в частном случае и возможности для реализации в общем.
// TypedArray
let a = new Int32Array([1, 2, 3]);
a = a.subarray(1);
// Array
const range = (arr, start, len) => {
len = len || Math.max(0, arr.length - start);
const end = Math.min(arr.length, start + len);
return {
current: start,
length: len,
next: function() {
if (this.current >= end) return {done: true};
else return {done: false, value: arr[this.current++]};
},
[Symbol.iterator]: function() {
this.current = start;
return this;
},
at(i) {
return arr.at(start + i);
},
};
};
const range_get = (arr, start, len) => {
len = len || Math.max(0, arr.length - start);
return new Proxy(range(arr, start, len), {
get(r, key) {
if (key && key.constructor === Symbol) return r[key];
if (key === 'length') return len;
if (Number.isSafeInteger(+key)) return arr[+key + start];
return r[key]; // call range methods
},
});
};
let a = [1, '2', {a: 3}];
let aa = range_get(a, 1);
console.log('a[0]=' + aa[0]); // '2'
console.log('a.at(0)=' + aa.at(0)); // '2'
console.log('a.len=' + aa.length); // 2
for (let ai of aa) console.log(ai); // '2', {a:3}
Как видно в представленном выше примере, при пролучении эначения элемента по индексу через скобки имеет значительный оверхеад по сравнению с получением через метод "at", поэтому предпочтителен 2-й вариант.
На практике лучше использовать функцию range
и поменять в коде получение элемента через скобки на метод at
или итерирование for ... of
.
Неоднократно видел, как люди делают так
let arr = [1,2,3]
let r = new Proxy(arr, {/* .... */})
Такой способ предоставляет для подмассива методы оригинального массива (с индексацией оригинального массива), например, insert(index)
, splice(i, l, v)
и так далее. Это может иметь плачевные последствия в сложных приложениях, поскольку место инициализации массива от места его использования может находиться далеко и неправильное поведение "массива" может привести в ступор любого, кто будет поддерживать такой код.
Аналогичный код на typescript (без функции range_get
):
interface IArrayRange<T> extends IterableIterator<T> {
current: number;
readonly length: number;
next(): IteratorResult<T>;
at(i: number): T | undefined;
}
function range<T>(
arr: T[],
start: number,
len: number = arr.length
): IArrayRange<T> {
len = len || Math.max(0, arr.length - start);
const end = Math.min(arr.length, start + len);
return {
current: start,
length: len,
next(): IteratorResult<T> {
if (this.current >= end) return {value: undefined, done: true};
else return {done: false, value: arr[this.current++]};
},
[Symbol.iterator](): IterableIterator<T> {
this.current = start;
return this;
},
at(i): T | undefined {
return arr.at(start + i);
},
};
}
let a = [1, '2', {a: 3}];
let aa = range(a, 1);
console.log('a.at(0)=' + aa.at(0)); // '2'
console.log('a.len=' + aa.length); // 2
for (let ai of aa) console.log(ai);
Данный код можно найти в соответствующем gist.