10 · Массивы
Литерал массива
Когда все элементы массива известны заранее, их не нужно объявлять с границами и заполнять по одному. Литерал массива перечисляет значения в квадратных скобках и сразу создаёт готовый одномерный массив с этими значениями.
Форма записи
Литерал начинается с открывающей квадратной скобки, затем идут значения через запятую и закрывающая скобка:
[<значение>, <значение>, …]Квадратная скобка в начале выражения означает перечисление значений. Квадратная скобка после имени или вызова означает обращение к массиву: fruits[2] выбирает элемент, fruits[] обозначает массив целиком. Эти две записи различаются наличием цели слева от скобки.
DIM fruits[] AS STRING = ["яблоко", "банан", "апельсин"]Литерал является значением, поэтому его можно присвоить существующему массиву, передать аргументом и вернуть из функции. У литерала нет собственного имени, но к его элементу можно обратиться сразу:
PRINT [10, 20, 30][2]Эта программа выводит 20.
Границы и нумерация
Литерал создаёт массив с нижней границей 1 и верхней границей, равной числу перечисленных значений. Так же начинаются массивы, которые возвращают SPLIT, ARRAYSLICE и другие функции, создающие результат. Первое значение имеет индекс 1, второе — 2, последнее — номер, совпадающий с длиной массива.
DIM fruits[] AS STRING = ["яблоко", "банан", "апельсин"]
PRINT fruits[2]
PRINT ARRAYLENGTH(fruits[])
PRINT LBOUND(fruits[]); " "; UBOUND(fruits[])Программа выводит:
банан
3
1 3Пустой литерал создаёт выделенный массив без элементов. Его границы имеют каноническую пустую форму: нижняя 1, верхняя 0, а ARRAYLENGTH равно 0.
Тип элементов
Тип элементов определяется по контексту. Если у объявления есть тип, он и задаёт тип элементов, а каждое значение проверяется на совместимость с ним:
DIM fruits[] AS STRING = ["яблоко", "банан"]Объявленный тип элементов не заменяется расширением. Запись DIM values[] AS INTEGER = [1, 2.5] отклоняется при проверке программы с MB2060: дробное значение несовместимо с INTEGER, и массив DOUBLE[] не подменяет объявленный INTEGER[]. Расширяющее значение при этом допустимо: DIM values[] AS DOUBLE = [1, 2.5] связывается как DOUBLE[].
Если тип не указан, он выводится из самих значений. Числовые значения разного вида получают общий тип по действующим правилам расширения: [1, 2] даёт INTEGER, [1, 2.5] — DOUBLE, [1, 3000000000] — LONG. Дробная часть при этом не теряется, потому что расширение идёт в сторону более широкого типа, а не сужения.
DIM mixed[] = [1, 2.5]
PRINT mixed[1] + mixed[2]Программа выводит 3.5.
Значение, несовместимое с остальными, отклоняется при проверке программы. Массив не может быть элементом литерала: вложенные массивы языком не поддерживаются.
Числовые константы и тип параметра
Известный тип элементов применяется к числовой константе по тем же правилам, что и при обычном присваивании. В частности, дробная константа для DECIMAL разбирается как DECIMAL, без промежуточного округления значения DOUBLE:
DIM amounts[] AS DECIMAL = [0.1, -0.2, 1]
PRINT amounts[1], amounts[2], amounts[3]Программа выводит 0.1 -0.2 1. Это правило не разрешает сужать произвольное выражение DOUBLE до DECIMAL: значение элемента должно отвечать обычному договору ожидаемого типа.
Параметр функции также может задавать тип пустого литерала. Скобки вокруг литерала сохраняют этот тип. У JOIN первый параметр имеет тип STRING[]; у ARRAYCONCAT тип второго массива определяется первым массивом:
PRINT LEN(JOIN(([]), "-"))
DIM initial[] AS INTEGER = [7]
DIM joined[] AS INTEGER = ARRAYCONCAT(initial[], ([]))
PRINT ARRAYLENGTH(joined[]), joined[1]Программа выводит 0, затем 1 7: пустая строка не содержит символов, а присоединение пустого массива сохраняет исходный элемент. Полная программа: contextual_values.axm.
Пустой набор
Пустой литерал не содержит ни одного значения, поэтому тип элементов из него вывести нельзя. Он допустим только там, где тип известен заранее: в объявлении с типом, в CONST с типом, в присваивании массиву известного типа или в аргументе, для которого объявлен тип массивного параметра.
DIM empty[] AS INTEGER = []
PRINT ARRAYLENGTH(empty[])Программа выводит 0.
Ожидаемый тип доходит до литерала и через круглые скобки, и через CONST:
DIM grouped[] AS INTEGER = ([])
CONST fixed[] AS INTEGER = []
PRINT ARRAYLENGTH(grouped[]); " "; ARRAYLENGTH(fixed[])Обе формы дают выделенный пустой массив, программа выводит 0 0. Скобки вокруг литерала не прерывают контекст объявления, а CONST связывает инициализатор сразу с объявленным типом.
Запись PRINT [] без такого контекста отклоняется: тип элементов не определён. Указать тип можно через объявление или через функцию с известным типом параметра.
Вычисление значений
Значения вычисляются ровно один раз каждое, слева направо, до создания массива. Побочные действия вроде вызова функции выполняются столько раз, сколько раз значение указано в литерале.
DIM counter AS INTEGER
FUNCTION Bump() AS INTEGER
counter = counter + 1
RETURN counter
END FUNCTION
DIM values[] AS INTEGER = [Bump(), Bump(), Bump()]
PRINT counterПрограмма выводит 3: функция вызвана три раза, по одному на каждое значение.
Каждое вычисление литерала создаёт независимый массив, даже если то же место программы выполняется повторно. Поэтому сохранённый прежний результат не изменяется, а представление на него остаётся действительным:
DIM first[] AS INTEGER
DIM current[] AS INTEGER
FOR i = 1 TO 2
current[] = [i]
IF i = 1 THEN first[] = current[]
NEXT i
PRINT first[1]Программа выводит 1: второе выполнение получило собственный массив и не затронуло первый.
Где допустим литерал
Литерал используется в объявлении, в присваивании массиву целиком, в аргументе функции и в RETURN:
FUNCTION Total(items[] AS INTEGER) AS INTEGER
RETURN ARRAYSUM(items[])
END FUNCTION
FUNCTION MakeFruits() AS STRING[]
RETURN ["груша", "слива"]
END FUNCTION
PRINT Total([4, 5, 6])
PRINT MakeFruits()[2]Программа выводит 15 и слива.
Созданный литералом массив участвует в массивных функциях на общих основаниях: ARRAYSUM([1, 2, 3]) возвращает 6, а ARRAYDISTINCT([1, 1, 2]) — массив из двух элементов.
Скалярный контекст литералу не подходит. Запись FUNCTION F() AS INTEGER с RETURN [1, 2] отклоняется при проверке программы с MB2003: массив не является значением INTEGER. То же относится к присваиванию скалярной переменной. Ошибка выдаётся при связывании, а не при исполнении байт-кода.
Диагностика
MB2059 сообщает, что пустой литерал не получил тип элементов: рядом нет объявления или параметра с известным типом массива. MB2060 сообщает о значении, несовместимом с типом элементов, и о попытке вложить массив в литерал. MB2061 сообщает, что литерал использован там, где ожидается массив ранга 2 и выше: литерал создаёт только одномерный массив. Подробности о массивных диагностиках собраны в статье Ошибки массивов.
Запускаемые примеры
fruit_values.axm — значения, длина и границы литерала. empty_typed_array.axm — пустой набор с типом из объявления. empty_literal_forms.axm — пустой набор в скобках и в CONST. mixed_numeric_values.axm — общий числовой тип. literal_as_value.axm — литерал в аргументе, в RETURN и при обращении по индексу. evaluated_once.axm — однократное вычисление слева направо. fresh_object_each_evaluation.axm — независимый массив при каждом вычислении. records_in_literal.axm — записи в литерале. narrowing_element_error.axm — отказ при несовместимом с объявлением элементе. scalar_return_error.axm — отказ при возврате массива из функции со скалярным результатом.
См. также
Одномерные массивы · Объявление DIM · Индексирование · Преобразования, поиск и вычисления