В компьютерном программировании нарезка массива — это операция, которая извлекает подмножество элементов из массива и упаковывает их как другой массив, возможно, в другом измерении , чем исходный.
Распространенными примерами нарезки массива являются извлечение подстроки из строки символов, « ell » в «h ell o», извлечение строки или столбца из двумерного массива или извлечение вектора из матрицы .
В зависимости от языка программирования , срез массива может быть создан из непоследовательных элементов. Также в зависимости от языка, элементы нового массива могут быть псевдонимами (т.е. совместно использовать память) элементов исходного массива.
Для «одномерных» (одноиндексных) массивов — векторов, последовательностей, строк и т. д. — наиболее распространенной операцией среза является извлечение нуля или более последовательных элементов. Таким образом, если у нас есть вектор, содержащий элементы (2, 5, 7, 3, 8, 6, 4, 1), и мы хотим создать срез массива с 3-го по 6-й элемент, мы получим (7, 3, 8, 6). В языках программирования , использующих схему индексации, начинающуюся с 0, срез будет с индекса 2 по 5 .
Сокращение диапазона любого индекса до одного значения фактически устраняет этот индекс. Эта функция может быть использована, например, для извлечения одномерных срезов (векторов: в 3D, строк, столбцов и трубок [1] ) или двумерных срезов (прямоугольных матриц) из трехмерного массива. Однако, поскольку диапазон может быть указан во время выполнения, для языков с проверкой типов может потребоваться явная (во время компиляции) нотация для фактического устранения тривиальных индексов.
Общая нарезка массива может быть реализована (независимо от того, встроена ли она в язык) путем ссылки на каждый массив через dope-вектор или дескриптор — запись, которая содержит адрес первого элемента массива, а затем диапазон каждого индекса и соответствующий коэффициент в формуле индексирования. Этот метод также позволяет выполнять немедленную транспозицию массива , реверсирование индекса, подвыборку и т. д. Для таких языков, как C , где индексы всегда начинаются с нуля, dope-вектор массива с d индексами имеет по крайней мере 1 + 2 d параметров. Для языков, которые допускают произвольные нижние границы для индексов, таких как Pascal , dope-вектору требуется 1 + 3 d записей.
Если абстракция массива не поддерживает истинные отрицательные индексы (как, например, массивы Ada и Pascal ), то отрицательные индексы для границ среза для заданного измерения иногда используются для указания смещения от конца массива в этом измерении. В схемах с 1-й базой -1 обычно указывает на предпоследний элемент, тогда как в системе с 0-й базой это будет означать самый последний элемент.
Концепция срезки была, несомненно, известна еще до изобретения компиляторов . Срезка как языковая функция, вероятно, началась с FORTRAN (1957), скорее как следствие несуществующей проверки типов и диапазонов, чем по замыслу. Эта концепция также упоминалась в предварительном отчете для IAL (ALGOL 58) в том, что синтаксис позволял опускать один или несколько индексов элемента массива (или, если на то пошло, вызова процедуры) при использовании в качестве фактического параметра.
В языке APL Кеннета Айверсона ( 1957) реализована очень гибкая многомерная срезка массива, что внесло большой вклад в выразительную мощь и популярность языка.
В ALGOL 68 (1968) были введены комплексные функции нарезки и усечения многомерных массивов.
Возможности среза массивов были включены в несколько современных языков, таких как Ada 2005 , Cobra , D , Fortran 90 , Go , Rust , Julia , MATLAB , Perl , Python , S-Lang , Windows PowerShell , а также в математические/статистические языки GNU Octave , S и R.
PL/I предоставляет две возможности для нарезки массива.
Y
как одномерный срез, состоящий из диагональных элементов двумерного массива X
.DECLARE X(5,5);
DECLARE Y(5) DEFINED(X(1SUB,1SUB));
Ссылка на Y(2)
— это ссылка на X(2,2)
и т. д.
X
ноль. Один или несколько нижних индексов могут быть указаны звездочками в выражении. [2] : стр.43 DECLARE X(5,5);
X(*,1)=0;
Программисты Fortran 66 могли воспользоваться преимуществом нарезки матриц только по строкам и только при передаче этой строки в подпрограмму :
ПОДПРОГРАММА PRINT V ( VEC , LEN ) REAL VEC ( * ) PRINT * , ( VEC ( I ), I = 1 , LEN ) END ГЛАВНЫЙ ПАРАМЕТР ПРОГРАММЫ ( LEN = 3 ) ВЕЩЕСТВЕННАЯ МАТРИЦА ( LEN , LEN ) МАТРИЦА ДАННЫХ / 1 , 1 , 1 , 2 , 4 , 8 , 3 , 9 , 27 / ВЫЗОВ ПЕЧАТИ V ( MATRIX ( 1 , 2 ), LEN ) КОНЕЦ
Результат:
2.000000 4.000000 8.000000
Обратите внимание, что в FORTRAN 66 нет вектора допинга , поэтому длина среза также должна быть передана в качестве аргумента (или каким-либо другим способом) в SUBROUTINE
. В 1970-х годах в Pascal и C были схожие ограничения.
Окончательный отчет Algol68 содержит ранний пример нарезки, срезы указаны в форме:
[нижняя граница:верхняя граница] ¢ для компьютеров с расширенными наборами символов ¢
или:
(НИЖНЯЯ ГРАНИЦА..ВЕРХНЯЯ ГРАНИЦА) # ДЛЯ КОМПЬЮТЕРОВ ТОЛЬКО С 6-БИТНЫМИ СИМВОЛАМИ. #
Оба предела являются инклюзивными и могут быть опущены, в этом случае они по умолчанию соответствуют объявленным пределам массива. Ни свойство шага, ни псевдонимы диагонального среза не являются частью пересмотренного отчета.
Примеры:
[3, 3]real a := ((1, 1, 1), (2, 4, 8), (3, 9, 27)); # объявление переменной матрицы # [,] real c = ((1, 1, 1), (2, 4, 8), (3, 9, 27)); # константная матрица, размер подразумевается #
ref[]real row := a[2,]; # псевдоним/ ссылка на срез строки # ref[]real col2 = a[, 2]; # постоянный псевдоним/ ссылка на второй столбец #
print ((a[:, 2], newline)); # второй срез столбца # print ((a[1⌈a, :], newline)); # последний срез строки # print ((a[:, 2⌈a], newline)); # последний срез столбца # print ((a[:2, :2], newline)); # ведущая подматрица 2 на 2 "срез" #
+1,0000 10 +0 +4,0000 10 +0 +9,0000 10 +0+3,0000 10 +0 +9,0000 10 +0 +2,7000 10 +1+1,0000 10 +0 +8,0000 10 +0 +2,7000 10 +1+1,0000 10 +0 +1,0000 10 +0 +2,0000 10 +0 +4,0000 10 +0
Системы HP 2000 , представленные в ноябре 1968 года, использовали HP Time-Shared BASIC в качестве основного интерфейса и языка программирования. Эта версия BASIC использовала срезы для большинства операций по обработке строк. Одной из странностей языка было то, что он допускал взаимозаменяемые круглые и квадратные скобки, и то, что использовалось на практике, обычно зависело от используемого компьютерного терминала .
Пример:
10 A$ = "ПРИВЕТ, МИР" 20 ПЕЧАТЬ A$ ( 1 , 5 ) 30 ПЕЧАТЬ A$ [ 7 , 11 ]
Будет производить:
ПРИВЕТМИР
Системы HP широко использовались в начале 1970-х годов, особенно в технических вузах и во многих небольших промышленных и научных учреждениях. [3] Когда в середине 1970-х годов появились первые микрокомпьютеры , HP часто использовался в качестве шаблона для их диалектов BASIC. Известными примерами являются Apple BASIC 1977 года , Atari BASIC 1978 года и Sinclair BASIC 1979 года . Этот стиль манипуляции обычно дает преимущества с точки зрения использования памяти и часто выбирался в системах, которые поставлялись с небольшим объемом памяти. Только диалект Синклера отличался каким-либо значимым образом, используя TO
ключевое слово вместо списка, разделенного запятыми:
10 ПУСТЬ a$ = "ABCDE" ( от 2 до 4 ) 20 РАСПЕЧАТАЙТЕ a$
Срезы также были выбраны в качестве основы для стандарта ANSI Full BASIC , в котором двоеточие используется в качестве разделителя и, таким образом, проводится различие между срезами и доступом к массиву:
10 DIM A$ ( 5 ) 20 LET A$ ( 2 ) = "ПРИВЕТ, МИР" 30 PRINT A$ ( 2 )( 1 : 5 )
Хотя этот стиль доступа предлагал ряд преимуществ, особенно для небольших машин той эпохи, где-то после 1970 года Digital Equipment Corporation представила свою собственную вариацию BASIC, которая использовала строковые функции LEFT$
, RIGHT$
и MID$
. Microsoft BASIC был написан на PDP-10 , и его BASIC использовался в качестве шаблона. До конца 1970-х годов оба стиля широко использовались, но к началу 1980-х годов функции в стиле DEC стали фактическим стандартом.
> A = round ( rand ( 3 , 4 , 5 ) * 10 ) % 3x4x5 трехмерный или кубический массив > A (:, :, 3 ) % 3x4 двумерный массив по первому и второму измерениямответ = 8 3 5 7 8 9 1 4 4 4 2 5> A (:, 2 : 3 , 3 ) % 3x2 двумерный массив по первому и второму измерениямответ = 3 5 9 1 4 2> A ( 2 : end , :, 3 ) % двумерный массив 2x4 с использованием ключевого слова 'end'; работает с GNU Octave 3.2.4ответ = 6 1 4 6 10 1 3 1> A ( 1 , :, 3 ) % одномерный массив по второму измерениюответ = 8 3 5 7> A ( 1 , 2 , 3 ) % одно значение ans = 3
Оператор :
реализует синтаксис шага ( lower_bound:upper_bound[:stride]
) путем генерации вектора. 1:5
оценивает как [1, 2, 3, 4, 5]
. 1:9:2
оценивает как [1, 3, 5, 7, 9]
. A bare :
оценивает то же самое, что и 1:end
, с end
определением по контексту.
Массивы в S и GNU R всегда имеют единичную основу, поэтому индексы нового среза будут начинаться с единицы для каждого измерения, независимо от предыдущих индексов. Измерения длиной в один будут удалены (если только drop = FALSE). Имена измерений (если они есть) будут сохранены.
> A <- array ( 1 : 60 , dim = c ( 3 , 4 , 5 )) # трехмерный или кубический массив 3x4x5 > A [, , 3 ] # двумерный массив 3x4 по первому и второму измерениям [, 1] [, 2] [, 3] [, 4] [1,] 25 28 31 34 [2,] 26 29 32 35 [3,] 27 30 33 36 > A [, 2 : 3 , 3 , drop = FALSE ] # подмножество кубического массива 3x2x1 (сохраненные измерения) , , 1 [, 1] [, 2] [1,] 28 31 [2,] 29 32 [3,] 30 33 > A [, 2 , 3 ] # одномерный массив по первому измерению [1] 28 29 30 > A [ 1 , 2 , 3 ] # одно значение [1] 28
Стандарт Fortran 77 представил возможность нарезки и конкатенации строк:
ПРОГРАММА ГЛАВНАЯ ПЕЧАТЬ * , 'ABCDE' ( 2 : 4 ) КОНЕЦ
Производит:
БКД
Такие строки можно передавать посредством ссылки в другую подпрограмму, длина также будет передаваться в подпрограмму прозрачно как своего рода короткий вектор допинга.
ПОДПРОГРАММА ПЕЧАТЬ S ( STR ) СИМВОЛ * ( * ) STR ПЕЧАТЬ * , STR КОНЕЦ ПРОГРАММА ГЛАВНЫЙ ВЫЗОВ ПЕЧАТЬ S ( 'ABCDE' ( 2 : 4 )) КОНЕЦ
Снова выдает:
БКД
Ada 83 поддерживает срезы для всех типов массивов. Подобно Fortran 77, такие массивы могут передаваться по ссылке в другую подпрограмму, длина также будет передаваться прозрачно в подпрограмму как своего рода короткий вектор допинга.
с Text_IO ; Процедура Main is Text : String := "ABCDE" ; begin Text_IO . Put_Line ( Text ( 2 .. 4 )); end Main ;
Производит:
БКД
Примечание: поскольку в Ada индексы имеют базовую основу n, результатом Text (2 .. 4)
будет массив с базовым индексом 2.
Определение для Text_IO.Put_Line
:
Пакет Ada.Text_IO — это процедура Put_Line ( Элемент : в Строке );
Определение для String
:
Стандартный пакет подтип Positive — это диапазон целых чисел 1 .. Integer ' Last ; тип String — массив ( положительный диапазон <>) символов ; прагма Pack ( String ) ;
Так как Ada поддерживает истинные отрицательные индексы, как в type History_Data_Array is array (-6000 .. 2010) of History_Data;
, она не придает особого значения отрицательным индексам. В приведенном выше примере термин Some_History_Data (-30 .. 30)
будет резать History_Data
от 31 г. до н.э. до 30 г. н.э. (поскольку нулевого года не было, год номер 0 фактически относится к 1 г. до н.э. ).
Если у нас есть
@a = ( 2 , 5 , 7 , 3 , 8 , 6 , 4 );
как указано выше, то первые 3 элемента, средние 3 элемента и последние 3 элемента будут следующими:
@a [ 0 .. 2 ]; # (2, 5, 7) @a [ 2 .. 4 ]; # (7, 3, 8) @a [ - 3 ..- 1 ]; # (8, 6, 4)
Perl поддерживает отрицательные индексы списка. Индекс -1 — последний элемент, -2 — предпоследний элемент и т. д. Кроме того, Perl поддерживает срезы на основе выражений, например:
@a [ 3 .. $#a ]; # 4-й элемент до конца (3, 8, 6, 4) @a [ grep { ! ( $_ % 3 ) } ( 0 ... $#a ) ]; # 1-й, 4-й и 7-й элемент (2,3,4) @a [ grep { ! (( $_ + 1 ) % 3 ) } ( 0 .. $#a ) ]; # каждый 3-й элемент (7,6)
Если у вас есть следующий список:
>>> числа = [ 1 , 3 , 5 , 7 , 8 , 13 , 20 ]
Затем можно выполнить срез, используя нотацию, похожую на извлечение элемента:
>>> nums [ 3 ] # без нарезки 7 >>> nums [: 3 ] # от индекса 0 (включительно) до индекса 3 (исключая) [1, 3, 5] >>> nums [ 1 : 5 ] [3, 5, 7, 8] >>> nums [ - 3 :] [8, 13, 20]
Обратите внимание, что Python допускает отрицательные индексы списка. Индекс -1 представляет последний элемент, -2 предпоследний элемент и т. д. Python также допускает свойство шага, добавляя дополнительное двоеточие и значение. Например:
>>> nums [ 3 :] [7, 8, 13, 20] >>> nums [ 3 ::] # == nums[3:] [7, 8, 13, 20] >>> nums [:: 3 ] # начиная с индекса 0 и получая каждый третий элемент [1, 7, 20] >>> nums [ 1 : 5 : 2 ] # от индекса 1 до индекса 5 и получая каждый второй элемент [3, 7]
Синтаксис stride ( nums[1:5:2]
) был введен во второй половине 1990-х годов в ответ на просьбы, высказанные научными пользователями в Python "matrix-SIG" (специальная группа интересов). [4]
Семантика среза потенциально различается для каждого объекта; новая семантика может быть введена, когда оператор перегружает оператор индексации. В стандартных списках Python (которые являются динамическими массивами ) каждый срез является копией. Срезы массивов NumPy , напротив, являются представлениями одного и того же базового буфера.
В Fortran 90 срезы задаются в форме
нижняя_граница : верхняя_граница [: шаг ]
Обе границы являются включающими и могут быть опущены, в этом случае они по умолчанию равны объявленным границам массива. Шаг по умолчанию равен 1. Пример:
real , dimension ( m , n ) :: a ! объявление матрицы print * , a (:, 2 ) ! второй столбец print * , a ( m , :) ! последняя строка print * , a (: 10 , : 10 ) ! ведущая подматрица 10 на 10
Каждое измерение значения массива в Analytica идентифицируется переменной Index. При срезе или подписке синтаксис идентифицирует измерение(я), по которому вы делаете срез или подписку, называя измерение. Например:
Индекс I := 1..5 { Определение числового индекса }Индекс J := ['A', 'B', 'C'] { Определение текстового индекса }Переменная X := Array(I, J, [[10, 20, 30], [1, 2, 3], ....]) { Определение двумерного значения }X[I = 1, J = 'B'] -> 20 { Нижний индекс для получения одного значения }X[I = 1] -> Array(J, [10, 20, 30]) { Вырезаем одномерный массив. }X[J = 2] -> Array(I, [20, 2, ....]) { Вырезаем одномерный массив по другому измерению. }X[I = 1..3] {Вырезаем первые четыре элемента над I со всеми элементами над J}
Именование индексов в срезах и подписках похоже на именование параметров в вызовах функций вместо того, чтобы полагаться на фиксированную последовательность параметров. Одним из преимуществ именования индексов в срезах является то, что программисту не нужно запоминать последовательность индексов в многомерном массиве. Более глубокое преимущество заключается в том, что выражения обобщаются автоматически и безопасно, не требуя переписывания при изменении числа измерений X.
Срез массива был введен в версии 1.0. Более ранние версии не поддерживали эту функцию.
Предположим, что A — это одномерный массив, такой как
А = [1:50]; % А = [1, 2, 3, ...49, 50]
Тогда массив B из первых 5 элементов A может быть создан с помощью
В = А[[:4]];
Аналогично, B может быть назначен массиву из последних 5 элементов A с помощью:
В = А[[-5:]];
Другие примеры одномерной нарезки включают в себя:
A[-1] % Последний элемент A A[*] % Все элементы A A[[::2]] % Все четные элементы A A[[1::2]] % Все нечетные элементы A A[[-1::-2]] % Все четные элементы в обратном порядке A[[[0:3], [10:14]]] % Элементы 0-3 и 10-14
Разбиение многомерных массивов работает аналогичным образом:
A[-1, *] % Последняя строка A A[[1:5], [2:7]] % двумерный массив, использующий строки 1-5 и столбцы 2-7 A[[5:1:-1], [2:7]] % То же, что и выше, за исключением того, что строки перевернуты
Индексы массива также могут быть массивами целых чисел. Например, предположим, что I = [0:9]
это массив из 10 целых чисел. Тогда A[I]
эквивалентно массиву из первых 10 элементов A
. Практическим примером этого является операция сортировки, такая как:
I = array_sort(A); % Получить список индексов сортировки B = A[I]; % B — отсортированная версия A C = A[array_sort(A)]; % То же, что и выше, но более кратко.
Рассмотрим массив:
целое число [] а = [ 2 , 5 , 7 , 3 , 8 , 6 , 4 , 1 ];
Возьмем кусочек:
int [] b = a [ 2 .. 5 ];
и содержимое b
будет [7, 3, 8]
. Первый индекс среза является включающим, второй — исключающим.
авто с = а [$ - 4 .. $ - 2 ];
означает, что динамический массив c
теперь содержит [8, 6]
, поскольку внутри [] $
символ ссылается на длину массива.
Срезы массива D ссылаются на исходный массив, поэтому:
б [ 2 ] = 10 ;
означает, что a
теперь имеет содержимое [2, 5, 7, 3, 10, 6, 4, 1]
. Чтобы создать копию массива данных, а не только псевдоним, выполните:
авто b = а [ 2 .. 5 ]. dup ;
В отличие от Python, границы среза D не насыщаются, поэтому код, эквивалентный этому коду Python, является ошибкой в D:
>>> д = [ 10 , 20 , 30 ] >>> д [ 1 : 5 ] [20, 30]
Язык программирования SuperCollider реализует некоторые концепции из J / APL . Нарезка выглядит следующим образом:
a = [ 3 , 1 , 5 , 7 ] // присвоить массив переменной a a [ 0 .. 1 ] // вернуть первые два элемента a a [ .. 1 ] // вернуть первые два элемента a: ноль можно опустить a [ 2 ..] // вернуть элементы с 3 по последний a [[ 0 , 3 ]] // вернуть первый и четвертый элементы aa [[ 0 , 3 ]] = [ 100 , 200 ] // заменить первый и четвертый элементы a a [ 2 ..] = [ 100 , 200 ] // заменить два последних элемента a// присвоить многомерный массив переменной a a = [[ 0 , 1 , 2 , 3 , 4 ], [ 5 , 6 , 7 , 8 , 9 ], [ 10 , 11 , 12 , 13 , 14 ], [ 15 , 16 , 17 , 18 , 19 ]]; a . slice ( 2 , 3 ); // взять срез с координатами 2 и 3 (возвращает 13) a . slice ( nil , 3 ); // взять ортогональный срез (возвращает [3, 8, 13, 18])
Массивы в fish всегда начинаются с единицы, поэтому индексы нового среза будут начинаться с единицы , независимо от предыдущих индексов.
> set A ( seq 3 2 11 ) # $A — это массив со значениями 3, 5, 7, 9, 11 > echo $A [( seq 2 )] # Вывести первые два элемента $A 3 5 > set B $A [ 1 2 ] # $B содержит первый и второй элементы $A, т.е. 3, 5 > set -e A [ $B ] ; echo $A # Удалить третий и пятый элементы $A, вывести $A 3 5 9
Cobra поддерживает нарезку в стиле Python. Если у вас есть список
числа = [ 1 , 3 , 5 , 7 , 8 , 13 , 20 ]
тогда первые 3 элемента, средние 3 элемента и последние 3 элемента будут:
nums [: 3 ] # равно [1, 3, 5] nums [ 2 : 5 ] # равно [5, 7, 8] nums [ - 3 :] # равно [8, 13, 20]
Cobra также поддерживает синтаксис в стиле срезов для «числовых циклов for»:
для i в 2 : 5 вывести i # выводит 2, 3, 4для j в 3 вывести j # выводит 0, 1, 2
В PowerShell массивы начинаются с нуля и могут быть определены с помощью оператора запятой:
PS> $a = 2 , 5 , 7 , 3 , 8 , 6 , 4 , 1 PS> # Вывести первые два элемента $a: PS> Write-Host -NoNewline $a [ 0 , 1 ] 2 5 PS> # Вырезать из него часть с помощью оператора диапазона: PS> Write-Host -NoNewline $a [ 2 .. 5 ] 7 3 8 6 PS> # Получить последние 3 элемента: PS> Write-Host -NoNewline $a [- 3 ..- 1 ] 6 4 1 PS> # Вернуть содержимое массива в обратном порядке: PS> Write-Host -NoNewline $a [( $a . Length - 1 ).. 0 ] # Длина — это свойство System.Object[] 1 4 6 8 3 7 5 2
Go поддерживает синтаксис в стиле Python для срезов (за исключением отрицательных индексов, которые не поддерживаются). Массивы и срезы могут быть срезаны. Если у вас есть срез
числа := [] целое число { 1 , 3 , 5 , 7 , 8 , 13 , 20 }
тогда первые 3 элемента, средние 3 элемента, последние 3 элемента и копия всего среза будут такими:
nums [: 3 ] // равно []int{1, 3, 5} nums [ 2 : 5 ] // равно []int{5, 7, 8} nums [ 4 :] // равно []int{8, 13, 20} nums [:] // равно []int{1, 3, 5, 7, 8, 13, 20}
Срезы в Go являются ссылочными типами, что означает, что разные срезы могут ссылаться на один и тот же базовый массив.
Cilk Plus поддерживает синтаксис для среза массива как расширение C и C++.
array_base [ нижняя_граница : длина [ : шаг ]] *
Нарезка Cilk Plus выглядит следующим образом:
A [ : ] // Весь вектор A B [ 2 : 6 ] // Элементы 2–7 вектора B C [ : ][ 5 ] // Столбец 5 матрицы C D [ 0 : 3 : 2 ] // Элементы 0, 2, 4 вектора D
Разбиение массива в Cilk Plus отличается от Fortran двумя моментами:
Разбиение массива Julia похоже на MATLAB , но использует квадратные скобки. Пример:
джулия> x = rand ( 4 , 3 ) 4x3 Массив {Float64,2}: 0,323877 0,186253 0,600605 0,404664 0,894781 0,0955007 0,223562 0,18859 0,120011 0,149316 0,779823 0,0690126 julia> x [ : , 2 ] # получить второй столбец. 4-элементный массив {Float64,1}: 0,186253 0,894781 0,18859 0,779823 julia> x [ 1 , : ] # получить первую строку. 1x3 Array{Float64,2}: 0.323877 0.186253 0.600605 julia> x [ 1 : 2 , 2 : 3 ] # получить подматрицу, охватывающую строки 1,2 и столбцы 2,3 2x2 Array{Float64,2}: 0.186253 0.600605 0.894781 0.0955007