День 1180. #ЗаметкиНаПолях
Выделяем Подмассивы. Окончание
Начало
Теперь рассмотрим второй способ.
Для него рассмотрим новый сценарий. Допустим, мы создаем модель машинного обучения для благотворительной организации, которая будет предсказывать, сделает ли человек донат, в зависимости от его возраста. У нас есть куча данных, которые мы хотим разбить на две секции: обучающие данные, на которых будет обучаться модель, и тестовые данные, по которым мы будем тестировать модель.
var data = new (int, bool)[] {
(20, true),
(50, true),
(35, false),
(55, true),
(16, false)
};
Заметьте, что в примере у нас всего 5 элементов. В реальной же модели обучения их могут быть миллионы, поэтому выделять новый массив будет очень затратно.
4. Использование ArraySegment<T>
var train = new ArraySegment<(int, bool)>(data, 0, 3);
var test = new ArraySegment<(int, bool)>(data, 3, 2);
Мы инициализируем новый экземпляр ArraySegment, передавая массив, который мы хотим разделить, начальный индекс и количество элементов. Это создает оболочку вокруг массива, которая ограничивает элементы между указанными позициями. Теперь мы можем выполнить итерацию по этому сегменту и прочитать его значения.
То же самое можно сделать с помощью метода ArraySegment<T>.Slice():
var all = new ArraySegment<(int, bool)>(data);
var train = all.Slice(0, 3);
var test = all.Slice(3, 2);
Мы создаем ArraySegment, который охватывает весь массив, затем вызываем метод Slice(), который создает ещё один ArraySegment с указанными границами.
Замечание: мы должны быть осторожны, потому что это не новый массив, это на самом деле переменная типа-значения, содержащая указатели на позиции элементов в массиве, поэтому любое изменение значений в сегменте массива будет отражаться в исходном массиве.
5. Использование ReadOnlySpan<T> и Span<T>
ReadOnlySpan<T> предоставляет оболочку, которая позволяет только чтение из массива:
var train = new ReadOnlySpan<(int, bool)>(data, 0, 3);
var test = new ReadOnlySpan<(int, bool)>(data, 3, 2);
ReadOnlySpan очень похож на ArraySegment. Он принимает массив, начальный индекс и количество элементов. Мы можем перечислить его, но, если мы попытаемся изменить какой-либо элемент через экземпляр ReadOnlySpan, мы получим ошибку компиляции.
Код, использующий Span<T> и его поведение аналогичны коду с ArraySegment<T>. Их отличие в том, что Span<T> можно использовать не только для массивов. Кроме того, ArraySegment<T> реализует IEnumerable<T> таким образом позволяет вернуть результат как IEnumerable<T>, тогда как ReadOnlySpan<T> и Span<T> возвращают соответствующие типы, хотя оба имеют метод GetEnumerator(), поэтому их также можно перечислить через foreach.
Сравнение быстродействия
На массиве из 10 строк быстродействие выделения подмассива из 5 элементов и их перечисления. Общий вид методов:
public int <Метод>()
{
// получение подмассива указанным методом
var slice = <подмассив>;
var cnt = 0;
foreach (var item in slice)
cnt++;
return cnt;
}
Результаты на картинке ниже.
Источник: https://code-maze.com/csharp-array-slicing/