Опубликован: 03.10.2011 | Уровень: для всех | Доступ: свободно
Лекция 10:

Переменные, присваивание и ссылки

Аннотация: Программы используют имена или сущности для обозначения значений периода выполнения. Отличительным свойством большинства программ является то, что некоторые сущности, называемые "переменными сущностями" или просто переменными, могут обозначать значения, изменяющиеся во время выполнения. Предыдущие примеры неявно исходили из этого предположения, хотя базисная операция — присваивание — до сих пор формально еще не введена. Эта концепция, обманчиво простая с первого взгляда, полна удивительных следствий. Мы будем изучать ее в этой главе наряду с несколькими связанными приемами, в частности, с использованием ссылок, определяющих структуру объектов в период выполнения.
Ключевые слова: конструирование, CoS, Бесконечная последовательность, число Фибоначчи, свойства программы, побочный эффект, eiffel, императивный язык, присваивание, значение, класс, assign, предусловие, псевдокод, интервалы, variant, оператор присваивания, target, значение выражения, инвариант цикла, инвариант, позиция курсора, вычисленное значение, максимум, локальная переменная, атрибут класса, feature, метод класса, область видимости, вызов функции, тело функции, real, семантика, swapping, свопинг, рекуррентного соотношения, булевское выражение, условные операторы, FORTRAN, algol-like, место, установка значений, setter, OBJ, синтаксически корректный, эволюция, управление доступом, атрибут записи, invariable, extending, наследование, measurement, компромисс, ясность, формальный аргумент, over-current, именованная константа, систематический, структура данных, список, end-station, ссылка, объект, моделирование, поддержка, интерпретация, CAR, engine, программная модель, коллекция объектов, связная структура, альтернатива, тело процедуры, элемент списка, создание элемента списка, 1-разбор, итеративность, pivot, итерация, clone, twin, тело цикла, рекурсия, reversibility, вариант цикла, доказательство, алгоритмическая, удаление элемента, кластер, среда разработки, базисный тип, стек, псевдоним, рейтинг, имя переменной, область действия, incremental, куча, постусловие, элемент данных, абстракция, мышление, variability

Математика - статична, ПО - динамично

Способность программ изменять собственное окружение представляет наиболее важное отличие конструирования ПО от математического вывода -двух видов деятельности, во многом схожих в других отношениях. Математики используют преобразования, но они являются механизмом описания одних значений в терминах других, не изменяя при этом значений уже существующих объектов. Если я пишу "Пусть y = cos (x)", я ничего не изменяю и даже не создаю, просто даю имя значению косинуса от x, которое существует само по себе, не беспокоясь, собирается ли кто-либо говорить о нем. Даже, если я потом говорю: "Предположим теперь, что y принимает значение sin (x)", - то для удобства имя y повторно используется, но обозначает совсем другой математический объект. Когда математик описывает последовательность "Пусть f_1 и f_2равны 1, и f_{i+2}=f+f_{i+1} для каждого i > 0", то речь идет о бесконечной последовательности чисел. У программиста, работающего с числами Фибоначчи, скорее всего, появятся две переменные, меняющие свои значения.

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

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

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

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

9.1. Присваивание

Присваивание — это оператор, разрешающий изменять значение переменной.

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

Суммируем время поездки

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

Алгоритм прямолинеен: последовательно проходя остановки на линии, следует к общему времени добавлять время до предыдущей остановки. Необходимая информация может быть получена по запросу из класса STOP:

time_to_next: REAL
    - Ожидаемое время проезда до следующей остановки: 
    - от отправления до отправления,
    - исключение для последней остановки: от отправления до прибытия.
  require
    has_next: is_linked

Предусловие метода is_linked требует, чтобы остановка была связана со следующей (не была последней).

Наша желаемая функция total_time имеет следующую общую форму:

 
total_time: REAL
    — Оценка времени поездки по всей линии
  do
    from
      start
      — "Установить Result в   ноль"
    invariant
      — "Значение Result - это время поездки от первой станции
      — до текущей станции, заданной позицией курсора"
    until
      is_last
    loop
      — "Увеличить Result на время до следующей станции"
      forth
    variant
      count - index
    end
  end

Переменная Result обозначает результат, возвращаемый функцией. Две команды псевдокода будут заменены присваиваниями.

Булевская функция is_last говорит нам, установлен ли курсор на последней станции. Заметьте разницу между схемой цикла, рассмотренной в предыдущей главе, где в конце цикла курсор имел значение after, а не is_last.

Запросы к базисной форме списка

Рис. 9.1. Запросы к базисной форме списка
Время теста
Когда выходить из цикла

Почему цикл для total_time использует is_last в качестве условия выхода, а не обычный after?

(Подсказка: сравните число остановок с числом интервалов. Сравните также выражение "variant" для двух циклов)

Команды псевдокода должны обновлять значение Result. Присваивание предназначено именно для этого.

Оператор присваивания имеет вид:

target := source

Здесь source — это выражение, а target — переменная, такая как Result. Левая часть присваивания target называется целью присваивания, а правая часть — sourceисточником. Эффект выполнения состоит в замене значения target на source. Чтобы быть точным:

Почувствуй семантику
Эффект присваивания

Выполнение оператора присваивания target:= source состоит из:

А1. вычисления (computing) значения выражения source;

А2. переменная target получает это значение, начиная с момента присваивания, и сохраняет его до очередного присваивания target.

Это единственный эффект оператора. В частности, это никак не отражается на source.

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

Выражение, такое как source, обычно включает переменные; "вычисление" его (A1) будет использовать их значения, полученные как результат предыдущих присваиваний.

Используем присваивание для уточнения функции нашего примера:

total_time: REAL
    — Оценка времени проезда по всей линии.
  do
    from
      start
      Result : = 00.0
    invariant
      — "Значение Result — это время поездки от первой станции
      — до текущей станции, заданной позицией курсора"
    until
      is_last
    loop
      Result := Result + item.time_to_next
      forth
    variant
      count - index
    end
  end

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

Время программирования!
Оцените время проезда по линии метро

Напишите функцию total_time8 для вычисления и показа времени проезда по 8-й линии метро. Используйте вышеприведенную модель, но не изменяйте класс LINE. Новую функцию сделайте частью класса ASSIGNMENTS, применив ее к Line8.

Кирилл Юлаев
Кирилл Юлаев
Как происходит отслеживание свободного экстента?
Федор Антонов
Федор Антонов
Оплата и обучение
Наталья Алмаева
Наталья Алмаева
Россия
Андрей Лучицкий
Андрей Лучицкий
Россия