Axiom Справочник языка

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 · Индексирование · Преобразования, поиск и вычисления