Присваивание функции результата - 7. Статические структуры: классы

Лекция 87 мин.



Это окончание невероятной информации про класс.

...

кроме документирования. Для использования компонента f из другого класса C, данный класс должен быть клиентом или потомком этого класса. В первом случае это означает, что f используется как

a.f

но тогда должно присутствовать объявление a:

a: C

недвусмысленно показывающее, что f компонента C. В случае классов потомков информация будет доступна из официальной документации класса, его плоской краткой формы.

Следовательно, нет необходимости в предложении import. ("Плоская краткая форма", )

Тем не менее, удобная графическая среда разработки должна обладать возможностью предоставления программисту информации о поставщиках и предках данного класса и их поставщиках и предках, следуя далее по цепочке.

Присваивание функции результата

Присваивание функции результата является интересной языковой проблемой, обсуждение которой было начато ранее в данной лекции. Стоит изучить ее подробнее ввиду ее важности и для языков, не использующих ОО-подход.

Рассмотрим функцию - подпрограмму, возвращающую результат. Целью любого вызова функции является вычисление некоторого результата и возвращение его в вызывающую подпрограмму. Вопрос в том, каким образом обозначить этот результат в тексте самой функции, в частности в инструкциях инициализирующих и изменяющих результат.

Введенное в данной лекции соглашение использует специальную сущность Result. Она рассматривается как локальная сущность, инициализируется соответствующим значением по умолчанию, а возвращаемое значение равно окончательному значению Result. В соответствии с правилами инициализации это значение всегда определено, даже если в теле функции нет присваивания Result значения. Так функция

f: INTEGER is

do

if some_condition then Result := 10 end

end

возвратит 10 при выполнении условия some_condition на момент вызова и 0 (значение по умолчанию при инициализации INTEGER) в противном случае. Насколько известно автору, техника использования Result была впервые предложена в данной книге. С момента выхода первого издания она была включена по крайней мере в один язык - Borland Delphi. Надо заметить, что она неприемлема для языков, допускающих объявление функций внутри других функций, поскольку имя Result становится двусмысленным. В различных языках наиболее часто используются следующие приемы:

  • - (A) Заключительные инструкции return (C, C++/Java, Ada, Modula-2).
  • - (B) Использование имени функции в качестве переменной (Fortran, Algol 60, Simula, Algol 68, Pascal).

Соглашение A основано на инструкции вида return e , выполнением которой завершается функция, возвращая e в качестве результата. Преимущество этого метода в его ясности, поскольку возвращаемое значение четко выделено в тексте функции. Однако он имеет и отрицательные стороны:

  • - (A1) На практике результат часто определяется в процессе вычислений, включающих инициализацию и ряд промежуточных изменений значения. Возникает необходимость во временной переменной для хранения промежуточных результатов.
  • - (A2) Методика имеет тенденцию к использованию модулей с несколькими точками завершения. Это противоречит принципам хорошего структурирования программ.
  • - (A3) В языке должна быть предусмотрена ситуация, когда последняя инструкция, выполненная при вызове функции, не является return. В программах Ada в этом случае возбуждается исключение времени выполнения.

Две последние проблемы разрешаются, если рассматривать return не как инструкцию, а как синтаксическое предложение, являющееся обязательной частью текста любой функции:

function name (arguments): TYPE is

do

...

return

expression

end

Это решение развивает идею инструкции return и устраняет ее наиболее серьезные недостатки. Тем не менее, ни один язык его не использует, оставляя проблему A1 открытой.

Методика B использует имя функции как переменную в тексте функции. Возвращаемое значение совпадает с окончательным значением этой переменной. Это избавляет от необходимости объявления временной переменной, упомянутой в A1.

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

f := x

а если f является частью выражения, то подразумевается рекурсивный вызов функции

x := f

который допустим только при отсутствии у f параметров. Однако присваивания вида

f := f + 1

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

Соглашение, основанное на предопределенной сущности Result, устраняет проблемы приемов A и B. В языках, предусматривающих инициализацию по умолчанию всех сущностей, включая Result, достигается дополнительное преимущество. Упрощается написание функций, так как часто функция должна во в всех случаях, кроме специально обусловленных, возвращать значение по умолчанию. Например, функция

do

if some_condition then Result := "Some specific value" end

end

не нуждается в предложении else. Подразумевается, что язык должен строго определить значения по умолчанию. Такие соглашения будут введены в следующей лекции.

Последнее преимущество соглашения Result вытекает из принципа проектирования по контракту (см. гл. 11). Можно использовать Result для выражения абстрактного свойства результата функции, не зависящего от реализации в постусловии подпрограммы. Никакой другой подход не позволит написать следующее:

prefix "|_": INTEGER is

-- Целая часть числа

do

... Реализация опущена ...

ensure

no_greater: Result <= Current

smallest_possible: Result + 1 > Current

end

В предложении ensure содержатся постусловия, утверждающие два свойства результата: результат не должен быть больше значения, к которому применяется операция, и это значение должно быть меньше чем результат плюс единица.

Дополнение: точное определение сущности

Будет полезно в процессе обсуждения проблем нотации уточнить понятие сущности, которое мы постоянно использовали. Это в значительной степени техническое понятие, обобщающее традиционное понятие переменной.

Сущности, в том смысле, в котором они используются в данной книге, обозначают имена некоторых величин времени выполнения, связанных с объектами. Можно выделить три возможных случая:

Определение: сущность (entity)

Сущность может представлять собой:

  • - (E1) Атрибут класса
  • - (E2) Локальную сущность подпрограммы, включая предопределенную сущность Result для функции
  • - (E3) Формальный аргумент подпрограммы

Случай E2 подчеркивает, что сущность Result всегда рассматривается как локальная. Другие локальные сущности введены в объявлении local. Result и другие локальные сущности заново инициализируются при каждом вызове подпрограммы.

Все сущности, за исключением формальных аргументов (E3), доступны для записи, то есть могут присутствовать как цель x в присваивании x := some_value.

Ключевые концепции

  • - Фундаментальная концепция объектной технологии основана на понятии класса. Класс это абстрактный тип данных, частично или полностью реализованный.
  • - Класс может иметь экземпляры, называемые объектами.
  • - Нельзя путать объекты (динамические элементы) с классами (статическим описанием свойств, общих для множества объектов времени выполнения).
  • - При последовательном подходе к объектной технологии каждый объект является экземпляром класса.
  • - Класс одновременно служит модулем и типом. Оригинальность и мощь ОО-модели следует частично из интеграции этих понятий.
  • - Класс характеризуется компонентами, включая атрибуты, представляющие поля в экземплярах класса, и подпрограммы, представляющие вычисления с участием данных экземпляров. Подпрограмма может быть функцией возвращающей результат или процедурой, если результат не возвращается.
  • - Базовым механизмом ОО-вычислений является вызов компонентов (обращение к компонентам) класса. Вызов компонента применяет компонент к экземпляру класса (возможно с аргументами).
  • - При вызове именованных компонентов используется точечная нотация, а при вызове компонент-операций - инфиксная или префиксная нотация.
  • - Каждая операция относительна к "текущему экземпляру" класса.
  • - Для клиентов класса (других классов, которые используют его компоненты) атрибут ничем не отличается от функции без аргументов, в соответствии с принципом унифицированного доступа.
  • - Исполняемый ансамбль классов называется системой. Система содержит корневой класс и все классы, которые необходимы корневому прямо или косвенно через клиентские отношения или наследование. Выполнение системы сводится к созданию экземпляра корневого класса и вызову процедуры создания для данного экземпляра.
  • - Системы имеют децентрализованную архитектуру. Порядок действий несущественен для разработки.
  • - Уточнение процесса сборки достигается с помощью простого языка описания систем Lace. В спецификации Lace, называемой файлом Ace, указывается корневой класс и набор каталогов, в которых размещены кластеры системы.
  • - Процесс компоновки может быть автоматизирован без использования Make-файлов и директив Include.
  • - Механизм скрытия информации требует гибкости. Наряду с неограниченным доступом и полным скрытием может потребоваться экспорт только для части клиентов. Атрибуты могут быть доступны только для чтения, для чтения и ограниченной модификации и в режиме полного доступа.
  • - Экспорт атрибута означает доступ к нему только для чтения. Модификация требует вызова соответствующей экспортированной процедуры.
  • - Селективный экспорт дает возможность группам родственных классов обеспечить специальный режим доступа для каждого компонента.
  • - Необходимость в надстройках над классами - супермодулей - отсутствует. Классы должны оставаться независимыми программными компонентами.
  • - Модульный стиль ОО-разработок требует большого числа небольших подпрограмм. Потенциальная опасность снижения производительности может быть достигнута путем встраивания этих подпрограмм оптимизирующим компилятором. Ответственность за поиск таких фрагментов следует возложить на компилятор, а не на разработчиков.

Библиографические замечания

Понятие класса пришло из языка Simula 67 (см. библиографические ссылки к курса "Основы объектно-ориентированного проектирования"). Класс в Simula является одновременно модулем и типом, однако эта особенность специально не подчеркивалась и была утрачена у преемников Simula.

Принцип единственности цели может рассматриваться как аналог приема, хорошо известного в математической логике и теоретической компьютерной науке: редукция (currying). Редукция функции двух переменных f означает замену ее функцией g одной переменной, возвращающей в качестве результата функцию одной переменной. В результате редукции для любых допустимых значений x и y:

(g (x)) (y)= f (x, y)

Редуцировать функцию это, другими словами, специализировать ее по первому аргументу. Этот прием аналогичен использованной в данной лекции замене традиционной процедуры rotate, имеющей два параметра:

rotate (some_point, some_angle)

на функцию с одним параметром, имеющую цель:

some_point.rotate (some_angle)

В [M 1990] описана редукция и некоторые из ее применений в информатике, в частности, при формальном изучении синтаксиса и семантики языков программирования. Редукция будет еще рассматриваться при обсуждении графического интерфейса пользователя ( курса "Основы объектно-ориентированного проектирования").

В отличие от положений данной лекции в некоторых языках объект рассматривается как языковая конструкция, а не как понятие времени выполнения. Такой подход предназначен для исследовательских целей и не нуждается в понятии класса. Наиболее известным представителем этой школы является язык Self [Chambers 1991], в котором вместо классов используются "прототипы".

Детали соглашения об инфиксных и префиксных операциях, в частности таблица приоритетов, приведены в [M 1992].

James McKim обратил мое внимание на последний аргумент в пользу соглашения Result (использование для постусловий).

Упражнения

У7.1 POINT как абстрактный тип данных

Напишите спецификацию абстрактного типа данных для описания точки на плоскости.

У7.2 Завершение реализации POINT

Завершите исходный текст класса POINT. Заполните недостающие фрагменты, добавьте процедуру rotate (вращение точки вокруг начала координат), а также другие компоненты, которые считаете необходимыми.

У7.3 Полярные координаты

Перепишите класс POINT таким образом, чтобы в качестве базового использовалось бы представление точки в полярных, а не декартовых координатах.

Вау!! 😲 Ты еще не читал? Это зря!

  • ООП
  • Класс
  • Объект
  • Унифицированный доступ
  • принцип проектирования по контракту
  • Требования к сборщику мусора
  • Подход C++ к связыванию
  • Использование утверждений в документации: краткая форма класса
  • Предопределение функции в качестве атрибута
  • Преждевременное упорядочение
  • Универсальные классы
  • Функциональная декомпозиция
  • Роль развернутых типов
  • Наследование функциональных возможностей
  • Самодокументирование
  • Заметки об indexing
  • Война вокруг точек с запятой
  • Категории функций

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

Продолжение:


Часть 1 7. Статические структуры: классы
Часть 2 Объектно-ориентированный стиль вычислений - 7. Статические структуры: классы
Часть 3 Программа main отсутствует - 7. Статические структуры: классы
Часть 4 Присваивание функции результата - 7. Статические структуры: классы

создано: 2020-07-23
обновлено: 2026-03-08
411



Помог ли вам этот ответ?
Нажмите оценку и напишите коротко почему. Так мы сможем сделать следующие ответы точнее и полезнее.
Насколько вы довольны ответом?
Ваш отзыв напрямую влияет на качество следующих подсказок и ответов.


Поделиться:
Пожаловаться

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выполнения задания
  • Возможно применение функции гаранта на сделку
  • Приоритетная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можете продать (как исполнитель) или купить (как заказчик) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно применение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии

Оставить комментарий

Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Лекции и учебник по "Объектно-ориентированное программирование ООП"

Термины: Объектно-ориентированное программирование ООП