Что такое тип данных? Зачем мы про него говорим, едва ознакомившись с модулем и журналом? Дело в том, что типы данных — очень, очень важная штука в программировании.
Мы знаем, что компьютеры работают с данными. А что такое данные? Данные — это факты, сущности внешнего мира, представленные таким образом, чтобы компьютер их мог обрабатывать. Ну а типы данных суть множества значений этих данных. Типы данных также вводят ограничения на использование данных.
Например, числовой тип данных говорит нам о том, что переменная этого типа может содержать некий диапазон целых чисел. Переменную числового типа можно использовать только для числовых вычислений. Нельзя в число записать букву, это ограничение и нарушать его нельзя, иначе мы запутаемся в том, где у нас слова и где числа, где пирожки и где котята.
Это самые простые, неделимые типы. Для них в языке определены базовые операции для вычисления новых значений. Базовыми типами в Обероне являются
Попробуем вывести в журнал значения переменных всех типов
MODULE MyTypes; IMPORT Log; PROCEDURE Do*; BEGIN Log.Int(2000 + 14); Log.Ln; Log.Real(355 / 113); Log.Ln; Log.Bool(5 > 2); Log.Ln; Log.Char('A'); Log.Ln; Log.Set({0, 1, 2, 3, 4, 5, 20}); Log.Ln; END Do; END MyTypes.
Что же мы увидим в журнале после выполнения команды MyTypes.Do
? Посмотрим на результат.
2014 3.141592920353983 $TRUE A {0..5, 20}
Первой строчкой выведен результат вычисления операции сложения. Складываются целые числа, значит и результат будет тоже целочисленным.
На второй строчке выводится дробный результат деления двух целых чисел.
На третьей строчке мы видим, как выводится значение логического типа (очевидно, что пять больше двух и это истина, то есть TRUE).
Символ «А» выведен на четвёртой строчке.
И последним выведено перечисление SET. Это множество целых чисел из диапазона 0..31 (подробно на нём мы останавливаться не будем, потому что это выходит за рамки начального курса).
Из базовых типов полезно составлять другие, более сложные типы. Такие структурированные типы называются составными. Здесь мы рассмотрим только один из составных типов ‒ массив.
Массивы состоят из компонентов одинаковых типов. Это означает, что массив - однородная структура. Доступ к любому компоненту массива производится по индексу. Индекс может быть константой, переменной или вообще вычисляться в результате какой-то операции. Конечно же, индекс может быть только целым числом. В Обероне массивы индексируются с 0. Ну а размер массива задаётся только константой.
Массив сам по себе интересная штука. Он сильно облегчает программисту жизнь. Подумайте сами, что легче - тысячу раз объявить переменную типа INTEGER или один раз объявить массив из тысячи элементов типа INTEGER? Конечно, второе. Вот пример:
MODULE MyArrays; VAR i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35, i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65, i66, i67, i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80, i81, i82, i83, i84, i85, i86, i87, i88, i89, i90, i91, i92, i93, i94, i95, i96, i97, i98, i99: INTEGER; a: ARRAY 100 OF INTEGER; END MyArrays.
Как видите, разница просто бросается в глаза. В разделе VAR объявлена сотня целочисленных переменных и их так много, что они не умещаются в экран.
Следом объявлен массив a
размером тоже в сотню целых чисел, но это объявление куда компактнее. И, главное, использовать эту сотню в виде массива гораздо удобнее. Можно передавать её как переменную, можно вычислять индекс доступа, можно выбирать любой элемент как если бы это была одна переменная.
Конечно же, массивы могут состоять не только из целых чисел. Пожалуйста, любой базовый тип к вашим услугам. Делайте массивы типа REAL, и храните в них дробные числа. Можно хранить логические значения BOOLEAN. Можно хранить символы CHAR, в таком случае у нас образуются символьные цепочки, в которых можно хранить имена, названия и даже тексты.
Но элементами массива могут быть не только базовые типы, но и другие массивы. Вот пример многомерного массива:
matrix: ARRAY 10 OF ARRAY 10 OF ARRAY 10 OF ARRAY 10 CHAR;
Как этот четырёхмерный массив должен выглядеть, довольно сложно представить. Надо что-то попроще. Давайте попробуем увидеть, как выглядит содержимое двумерного массива символов из 10 строк и 10 столбцов
matrix10x10: ARRAY 10 OF ARRAY 10 OF CHAR;
Если такой двумерный массив заполнить произвольными символами и затем вывести в журнал, то получится что-то вроде этого:
Q Q T A A I E I U X V A D Y A O O O V B T P A H B [ L [ \ X Z L H K V E J I F \ N W J J U R O G Q A ] E O Y P T V U [ Y X B K S A T R T ^ J P Y A H D E L J Q Y S C F L Z J Q M W Q C U J Z I I Y F ] C
Программа, заполняющая двумерный массив и выводящая его в журнал так, как показано, выглядит так:
MODULE MyArrays; IMPORT ObxRandom, Log; VAR matrix10x10: ARRAY 10 OF ARRAY 10 OF CHAR; PROCEDURE Do*; VAR i, j: INTEGER; s: ARRAY 10 OF CHAR; BEGIN FOR i := 0 TO 9 DO FOR j := 0 TO 9 DO matrix10x10[i, j] := CHR(ORD('A') + ENTIER(ObxRandom.Uniform() * 30)) END END; FOR i := 0 TO 9 DO FOR j := 0 TO 9 DO Log.Char(matrix10x10[i, j]); Log.Char(' ') END; Log.Ln END END Do; END MyArrays.
Как видите, здесь уже используется вычисление индексов массива для доступа к его элементам. Причём это вычисление делается внутри двух циклов. Циклы мы рассмотрим на следующем уроке, а тут стоит прокомментировать использование модуля ObxRandom для вычисления случайного дробного числа в диапазоне 0..1. Мы увеличиваем это число в 30 раз, чтобы получить диапазон 0..30, затем используем встроенную в язык функцию ENTIER для преобразования числа из дробного в целое. После чего прибавляем результат к коду символа «A» и из полученного числа генерируем символ.
Индексация к двумерному массиву, естественно, делается с помощью двух индексов. Будь у нас трёхмерный массив, индексов бы понадобилось три и так далее.