|
||||
Меню:
Главная
Форум
Литература: Программирование и ремонт Импульсные блоки питания Неисправности и замена Радиоэлектронная аппаратура Микросхема в ТА Рубрикатор ТА Кабельные линии Обмотки и изоляция Радиоаппаратура Гибкие диски часть 2 часть 3 часть 4 часть 5 Ремонт компьютера часть 2 Аналитика: Монтаж Справочник Электроника Мощные высокочастотные транзисторы 200 микросхем Полупроводники ч.1 Часть 2 Алгоритмические проблемы 500 микросхем 500 микросхем Сортировка и поиск Монады Передача сигнала Электроника Прием сигнала Телевидиние Проектирование Эвм Оптимизация Автомобильная электроника Поляковтрансиверы Форт Тензодатчик Силовые полевые транзисторы Распределение частот Резисторные и термопарные Оберон Открытые системы шифрования Удк |
[71] ((lambda (amount) (- 25 amount)) 20) Теперь мы применяем оператор к операнду, подставляя 20 вместо amount в теле lambda-выра-жения: (- 25 20) Окончательный результат равен 5. Посмотрим, однако, что произойдет, если мы попробуем применить подобный подстановочный анализ к make-simplified-withdraw: ((make-simplified-withdraw 25) 20) Сначала мы упрощаем оператор, подставляя вместо balance 25 в теле make-simplified-withdraw. Таким образом, наше выражение сводится к9 ((lambda (amount) (set! balance (- 25 amount)) 25) 20) Теперь мы применяем оператор к операнду, подставляя в теле lambda-выраже-ния 20 вместо amount: (set! balance (- 25 20)) 25 Если бы мы следовали подстановочной модели, нам пришлось бы сказать, что вычисление процедуры состоит в том, чтобы сначала присвоить переменной balance значение 5, а затем в качестве значения вернуть 25. Но это дает неверный ответ. Чтобы получить правильный ответ, нам пришлось бы как-то отличить первое вхождение balance (до того, как сработает set! ) от второго (после выполнения set! ). Подстановочная модель на это не способна. Проблема здесь состоит в том, что подстановка предполагает, что символы в нашем языке - просто имена для значений. Но как только мы вводим set! и представление, что значение переменной может изменяться, переменная уже не может быть всего лишь именем. Теперь переменная некоторым образом соответствует месту, в котором может храниться значение, и значение это может меняться. В разделе 3.2 мы увидим, как в нашей модели вычислений роль этого «места» играют окружения. Тождественность и изменение Проблема, который здесь встает, глубже, чем просто поломка определенной модели вычислений. Как только мы вводим в наши вычислительные модели понятие изменения, многие другие понятия, которые до сих пор были ясны, становятся сомнительными. Рассмотрим вопрос, что значит, что две вещи суть «одно и то же». Допустим, мы два раза зовем make-decrementer с одним и тем же аргументом, и получаем две процедуры: (define Di (make-decrementer 25)) (define D2 (make-decrementer 25)) 9Мы не производим подстановку вхождения balance в выражение set!, поскольку {имя) в set! не вычисляется. Если бы мы провели подстановку, получилось бы (set! 25 (-25 amount)), а это не имеет никакого смысла. Являются ли D1 и D2 одним и тем же объектом? Можно сказать, что да, поскольку D1 и D2 обладают одинаковым поведением - каждая из этих процедур вычитает свой аргумент из 25. В сущности, в любом вычислении можно подставить D1 вместо D2, и результат не изменится. Напротив, рассмотрим два вызова make-simplified-withdraw: (define W1 (make-simplified-withdraw 25)) (define W2 (make-simplified-withdraw 25)) Являются ли W1 и W2 одним и тем же? Нет, конечно, потому что вызовы W1 и W2 приводят к различным результатам, как показывает следующая последовательность вычислений: (W1 20) 5 (W1 20) -15 (W2 20) 5 Хотя W1 и W2 «равны друг другу» в том смысле, что оба они созданы вычислением одного и того же выражения (make-simplified-withdraw 25), неверно, что в любом выражении можно заменить W1 на W2, не повлияв при этом на результат его вычисления. Язык, соблюдающий правило, что в любом выражении «одинаковое можно подставить вместо одинакового», не меняя его значения, называется референ-циально прозрачным (referentially transparent). Если мы включаем в свой компьютерный язык set!, его референциальная прозрачность нарушается. Становится сложно определить, где можно упростить выражение, подставив вместо него равносильное. Следовательно, рассуждать о программах, в которых используется присваивание, оказывается гораздо сложнее. С потерей референциальной прозрачности становится сложно формально описать понятие о том, что два объекта - один и тот же объект. На самом деле, смысл выражения «то же самое» в реальном мире, который наши программы моделируют, сам по себе недостаточно ясен. В общем случае, мы можем проверить, являются ли два как будто бы одинаковых объекта одним и тем же, только изменяя один из них и наблюдая, изменился ли таким же образом и другой. Но как мы можем узнать, «изменился» ли объект? Только рассмотрев один и тот же объект дважды и проверив, не различается ли некоторое его свойство между двумя наблюдениями. Таким образом, мы не можем определить «изменение», не имея заранее понятия «идентичности», а идентичность мы не можем определить, не рассмотрев результаты изменений. В качестве примера того, как эти вопросы возникают в программировании, рассмотрим ситуацию, где у Петра и у Павла есть по банковскому счету в 100 долларов. Здесь не все равно, смоделируем мы это через (define peter-acc (make-account 100)) (define paul-acc (make-account 100)) (define peter-acc (make-account i00)) (define paul-acc peter-acc) В первом случае, два счета различны. Действия, которые производит Петр, не меняют счет Павла, и наоборот. Однако во втором случае мы сказали, что paul-acc - это та же самая вещь, что и peter-acc. Теперь у Петра и у Павла есть совместный банковский счет, и если Петр возьмет сколько-то с peter-acc, то у Павла на paul-acc будет меньше денег. При построении вычислительных моделей сходство между этими двумя несовпадающими ситуациями может привести к путанице. В частности, в случае с совместным счетом может особенно мешать то, что у одного объекта (банковского счета) есть два имени (peter-acc и paul-acc); если мы ищем в программе все места, где может меняться paul-acc, надо смотреть еще и где меняется peter-acc.10 В связи с этими замечаниями обратите внимание на то, что если бы Петр и Павел могли только проверять свой платежный баланс, но не менять его, то вопрос «один ли у них счет?» не имел бы смысла. В общем случае, если мы никогда не меняем объекты данных, то можно считать, что каждый объект представляет собой в точности совокупность своих частей. Например, рациональное число определяется своим числителем и знаменателем. Однако при наличии изменений такой взгляд становится ошибочным, поскольку теперь у каждого объекта есть «индивидуальность», которая отличается от тех частей, из которых он состоит. Банковский счет останется «тем же самым» счетом, даже если мы снимем с него часть денег; и наоборот, можно иметь два разных счета с одинаковым состоянием. Такие сложности - следствие не нашего языка программирования, а нашего восприятия банковского счета как объекта. Скажем, рациональное число мы обычно не рассматриваем как изменяемый объект со своей индивидуальностью, у которого можно было бы изменить числитель и по-прежнему иметь дело с «тем же» числом. Ловушки императивного программирования В противоположность функциональному программированию, стиль программирования, при котором активно используется присваивание, называется императивное программирование (imperative programming). Кроме того, что возникают сложности с вычислительными моделями, программы, написанные в императивном стиле, подвержены таким ошибкам, которые в функциональных программах не возникают. Вспомним, к примеру, итеративную программу для вычисления факториала из раздела 1.2.1: (define (factorial n) (define (iter product counter) (if (> counter n) product 10Когда у вычислительного объекта имеется несколько имён, эти имена называются псевдонимами (aliasing). Ситуация с совместным банковским счетом - простой пример псевдонимов. В разделе 3.3 мы увидим значительно более сложные примеры, скажем, «различные» составные структуры с общими частями. Если мы забудем, что «побочным эффектом» в результате изменения одного объекта может стать изменение «другого» объекта, поскольку «разные» объекты - на самом деле один и тот же под разными псевдонимами, то могут возникнуть ошибки. Эти так называемые ошибки побочных эффектов (side-effect bugs) настолько трудно обнаруживать и анализировать, что некоторые исследователи выступали с предложениями не допускать в языках программирования побочные эффекты и псевдонимы (Lampson et al. 1981; Morris, Schmidt, and Wadler 1980). |
Среды: Smalltalk80 MicroCap Local bus Bios Pci 12С ML Микроконтроллеры: Atmel Intel Holtek AVR MSP430 Microchip Книги: Емкостный датчик 500 схем для радиолюбителей часть 2 (4) Структура компьютерных программ Автоматическая коммутация Кондиционирование и вентиляция Ошибки при монтаже Схемы звуковоспроизведения Дроссели для питания Блоки питания Детекторы перемещения Теория электропривода Адаптивное управление Измерение параметров Печатная плата pcad pcb Физика цвета Управлении софтверными проектами Математический аппарат Битовые строки Микроконтроллер nios Команды управления выполнением программы Перехода от ahdl к vhdl Холодный спай Усилители hi-fi Электронные часы Сердечники из распылённого железа Анализ алгоритмов 8-разрядные КМОП Классификация МПК История Устройства автоматики Системы и сети Частотность Справочник микросхем Вторичного электропитания Типы видеомониторов Радиобиблиотека Электронные системы Бесконтекстный язык Управление техническими системами Монтаж печатных плат Работа с коммуникациями Создание библиотечного компонента Нейрокомпьютерная техника Parser Пи-регулятор ч.1 ПИ-регулятор ч.2 Обработка списков Интегральные схемы Шина ISAВ Шина PCI Прикладная криптография Нетематическое: Взрывной автогидролиз Нечеткая логика Бытовые установки (укр) Автоматизация проектирования Сбор и защита Дискретная математика Kb радиостанция Энергетика Ретро: Прием в автомобиле Управление шаговым двигателем Магнитная запись Ремонт микроволновки Дискретные системы часть 2 | ||