Опубликован: 17.10.2005 | Доступ: свободный | Студентов: 8823 / 588 | Оценка: 4.38 / 4.10 | Длительность: 41:16:00
ISBN: 978-5-7502-0255-3
Специальности: Программист
Лекция 10:

Универсализация

Массивы

В заключение этой дискуссии полезно рассмотреть пример контейнерного класса ARRAY, представляющего одномерный массив.

Массивы как объекты

Понятие массив обычно является частью определения языка программирования. В объектной технологии нет необходимости нагружать нотацию специальными заранее определенными конструкциями: массив - контейнерный объект, экземпляр класса, который можно назвать ARRAY.

ARRAY хороший пример родового класса. Рассмотрим первый набросок этого класса3Улучшенная версия класса рассмотрена в "Проектирование по контракту: построение надежного ПО"

indexing
      description: "Последовательность значений одного типа или согласуемых типов,%
               %доступных через целые индексы в заданном диапазоне"
class ARRAY [G] creation
      make
feature
      make (minindex, maxindex: INTEGER) is
            -- Размещение массива с границами minindex и maxindex
            -- (пустой, если minindex > maxindex)
         do ... end
      lower, upper, count: INTEGER
            -- Минимальный и максимальный допустимый индекс; размер массива.
      put (v: G; i: INTEGER) is
            -- Присвоить v элементу массива с индексом i
         do ... end
      infix "@", item (i: INTEGER): G is
            -- Элемент с индексом i
         do ... end
end -- класса ARRAY

Для создания массива a с границами m и n, тип объявления которого ARRAY [T] с заданным типом T, нужно выполнить инструкцию создания

create a.make (m, n)

Для задания значений элементов массива используется процедура put: вызов a.put(x, i) присваивает значение x i -ому элементу. Для доступа к элементам можно использовать функцию item (синоним инфиксной операции @, поясняемой позже), например:

x := a.item (i)
Вот схема того, как этот класс может быть использован клиентом:
pa: ARRAY [POINT]; p1: POINT; i, j: INTEGER
...
create pa.make (-32, 101) -- Разместить массив с указанными границами.
pa.put (p1, i) -- Присвоить значение p1 элементу с индексом i.
...
p1 := pa.item (j) -- Присвоить сущности p1 значение элемента с индексом j.

В обычной нотации (скажем, в Pascal) нужно писать:

pa [i] := p1 вместо pa.put (p1, i)

p1 := pa [i] вместо p1 := pa.item (i)

Свойства массива

Некоторые замечания о классе.

  • Подобные классы существуют для массивов большей размерности: ARRAY2 и т. д.
  • Компонент Count может быть реализован и как атрибут и как функция, поскольку count = upper - lower+1. В реальном классе это выражается инвариантом, как объясняется в следующей лекции.
  • Техника утверждений позволяет связывать точные условия согласования с put и item, отражая тот факт, что вызовы допустимы, только если индекс i лежит между lower и upper.
  • Идея описания массивов как объектов (и ARRAY как класс) - хороший пример мощности унификации и упрощения объектной технологии, позволяющей сократить нотацию до минимума и уменьшить количество узкоспециализированных конструкций. Здесь массив рассматривается как обычный пример контейнерной структуры с собственными методами доступа, представленными компонентами put и item.
  • Так как ARRAY - обычный класс, он может участвовать во всем, что в предыдущих лекциях называлось ОО-играми; в частности, другие классы могут быть его наследниками. Класс ARRAYED_LIST, описывающий реализацию абстрактного понятия - списка массивов может быть наследником классов LIST и ARRAY. Подобные конструкции будут рассматриваться далее.

Как только мы изучим механизм утверждений, этот унифицированный подход даст возможность развития нашего класса. Предусловия позволят управлять проверкой корректного задания индексов, что обычно требует узко специализированных механизмов.

Размышления об эффективности

  • Может ли элегантность и простота нанести удар по эффективности выполнения? Одна из причин широкого использования массивов состоит в том, что основные операции - доступ и изменение элемента - проходят быстро. Надо ли платить за каждый вызов подпрограммы при использовании item или put? Нет. То, что ARRAY для ничего не подозревающего разработчика выглядит как нормальный класс, не запрещает компилятору опираться на скрытую информацию. Она позволяет компилятору находить вызовы item и put и переопределять их так, чтобы сгенерировать такой же код, как сделает компилятор Fortran, Pascal или C для эквивалентных инструкций ( p1 := pa [i] и pa [i] := p1 в синтаксисе Pascal). Поэтому разработчик получит лучшее: универсальность, общность, упрощенность, простоту использования ОО-решения, сочетаемую с сохранением производительности традиционного решения.
  • Работа компилятора не тривиальна. Как выяснится при изучении наследования, для потомка класса ARRAY возможно переопределить любой компонент класса и эти переопределения могут быть косвенно вызваны через динамическое связывание. Поэтому компилятор должен выполнять тщательный анализ для проверки корректности изменений массива. Для научных приложений, интенсивно использующих массивы, современные компиляторы от ISE и других компаний сегодня могут генерировать код, столь же эффективный, как написанный вручную на C или Fortran.

Синонимичная инфиксная операция

Класс ARRAY предоставляет возможность, косвенно относящуюся к вопросам этой лекции, но полезную на практике. Объявление компонента item фактически определяет и его синоним - инфиксную операцию4Нотация инфиксных операций была введена в "Статические структуры: классы" следующим образом:

infix "@", item (i: INTEGER): G is...

Здесь задаются два имени компонента: infix "@" и item как синонимы, обозначающие один и тот же компонент, заданный определением.

В общем, объявление компонентов в форме:

a, b, c... "Описание компонента"

рассматривается как краткая форма записи последовательности объявлений:

a "Описание компонента"
b "Описание компонента"
c "Описание компонента"
...

с одним и тем же "Описанием компонента".

Это применимо как для атрибутов (где "Описание компонента" имеет форму: некоторый_тип ), так и для подпрограмм ( is тело_программы ).

Нотация, применяемая в этом примере для доступа к массиву, достаточно проста. Она совместима с механизмами доступа для других структур, хотя, заметим, инструкция a.item(i) более многословна, чем традиционное a[i], встречающееся с некоторыми вариациями в Pascal, C, Fortran и других языках. Определяя " @ " как синоним item, можно превзойти традиционные языки в их собственной игре за краткость записи. Написав a @ i, реализуем мечту, - запись требует на одно нажатие клавиши меньше, чем даже С++!. Заметим снова, что это не специальный механизм языка, но прямое применение общей ОО-концепции, компонент-оператора, скомбинированного с нотацией синонима.

Александр Шалухо
Александр Шалухо
Анатолий Садков
Анатолий Садков

При заказе pdf документа с сертификатом будет отправлен только сертификат или что-то ещё?