|
||||
Меню:
Главная
Форум
Литература: Программирование и ремонт Импульсные блоки питания Неисправности и замена Радиоэлектронная аппаратура Микросхема в ТА Рубрикатор ТА Кабельные линии Обмотки и изоляция Радиоаппаратура Гибкие диски часть 2 часть 3 часть 4 часть 5 Ремонт компьютера часть 2 Аналитика: Монтаж Справочник Электроника Мощные высокочастотные транзисторы 200 микросхем Полупроводники ч.1 Часть 2 Алгоритмические проблемы 500 микросхем 500 микросхем Сортировка и поиск Монады Передача сигнала Электроника Прием сигнала Телевидиние Проектирование Эвм Оптимизация Автомобильная электроника Поляковтрансиверы Форт Тензодатчик Силовые полевые транзисторы Распределение частот Резисторные и термопарные Оберон Открытые системы шифрования Удк |
[68] (set! balance (- balance amount)) Здесь используется особая форма set!, синтаксис которой выглядит так: (set! (имя) {новое-значение}) Здесь {имя) - символ, а {новое-значение) - произвольное выражение. Set! заменяет значение {имени) на результат, полученный при вычислении {нового-значения). В данном случае, мы изменяем balance так, что его новое значение будет результатом вычитания amount из предыдущего значения balance.2 Кроме того, withdraw использует особую форму begin, когда проверка if выдает истину, и требуется вычислить два выражения: сначала уменьшить balance, а затем вернуть его значение. В общем случае вычисление выражения (begin (выражение (выражение) {выражение)) приводит к последовательному вычислению выражений от {выражения) до {выражения}, и значение последнего выражения {выражение} возвращается в качестве значения всей формы begin.3 Хотя процедура withdraw и работает так, как мы того хотели, переменная balance представляет собой проблему. Balance, как она описана выше, является переменной, определенной в глобальном окружении, и любая процедура может прочитать или изменить ее значение. Намного лучше было бы, если бы balance можно было сделать внутренней переменной для withdraw, так, чтобы только withdraw имела доступ к ней напрямую, а любая другая процедура - только посредством вызовов withdraw. Так можно будет более точно смоделировать представление о balance как о внутренней переменной состояния, с помощью которой withdraw следит за состоянием счета. Сделать balance внутренней по отношению к withdraw мы можем, переписав определение следующим образом: (define new-withdraw (let ((balance 100)) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Недостаточно денег на счете")))) Здесь мы, используя let, создаем окружение с внутренней переменной balance, которой вначале присваивается значение 100. Внутри этого локального окружения мы при помощи lambda определяем процедуру, которая берет в качестве аргумента amount и действует так же, как наша старая процедура 2Значение выражения set! зависит от реализации. Set! нужно использовать только ради эффекта, который оно оказывает, а не ради значения, которое оно возвращает. Имя set! отражает соглашение, принятое в Scheme: операциям, которые изменяют значения переменных (или структуры данных, как мы увидим в разделе 3.3) даются имена с восклицательным знаком на конце. Это напоминает соглашение называть предикаты именами, которые оканчиваются на вопросительный знак. 3Неявно мы уже использовали в своих программах begin, поскольку в Scheme тело процедуры может быть последовательностью выражений. Кроме того, в каждом подвыражении cond следствие может состоять не из одного выражения, а из нескольких. withdraw. Эта процедура - возвращаемая как результат выражения let, - и есть new-withdraw. Она ведет себя в точности так же, как, как withdraw, но ее переменная balance недоступна для всех остальных процедур.4 Set! в сочетании с локальными переменными - общая стратегия программирования, которую мы будем использовать для построения вычислительных объектов, обладающих внутренним состоянием. К сожалению, при использовании этой стратегии возникает серьезная проблема: когда мы только вводили понятие процедуры, мы ввели также подстановочную модель вычислений (раздел 1.1.5) для того, чтобы объяснить, что означает применение процедуры к аргументам. Мы сказали, что оно должно интерпретироваться как вычисление тела процедуры, в котором формальные параметры заменяются на свои значения. К сожалению, как только мы вводим в язык присваивание, подстановка перестает быть адекватной моделью применения процедуры. (Почему это так, мы поймем в разделе 3.1.3.) В результате, с технической точки зрения мы сейчас не умеем объяснить, почему процедура new-withdraw ведет себя именно так, как описано выше. Чтобы действительно понять процедуры, подобные new-withdraw, нам придется разработать новую модель применения процедуры. В разделе 3.2 мы введем такую модель, попутно объяснив set! и локальные переменные. Однако сначала мы рассмотрим некоторые вариации на тему, заданную new-withdraw. Следующая процедура, make-withdraw, создает «обработчики снятия денег со счетов». Формальный параметр balance, передаваемый в make-withdraw, указывает начальную сумму денег на счету.5 (define (make-withdraw balance) (lambda (amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Недостаточно денег на счете"))) При помощи make-withdraw можно следующим образом создать два объекта W1 и W2: (define Wi (make-withdraw i00)) (define W2 (make-withdraw i00)) (Wi 50) 50 (W2 70) 30 (W2 40) 4По терминологии, принятой при описании языков программирования, переменная balance инкапсулируется (is encapsulated) внутри процедуры new-withdraw. Инкапсуляция отражает общий принцип проектирования систем, известный как принцип сокрытия (the hiding principle): систему можно сделать более модульной и надежной, если защищать ее части друг от друга; то есть, разрешать доступ к информации только тем частям системы, которым «необходимо это знать». 5В отличие от предыдущей процедуры new-withdraw, здесь нам необязательно использовать let , чтобы сделать balance локальной переменной, поскольку формальные параметры и так локальны. Это станет яснее после обсуждения модели вычисления с окружениями в разделе 3.2. (См. также упражнение 3.10.) "недостаточно денег на счете" (W1 40) 10 Обратите внимание, что W1 и W2 - полностью независимые объекты, каждый со своей локальной переменной balance. Снятие денег с одного счета не влияет на другой. Мы можем создавать объекты, которые будут разрешать не только снятие денег, но и их занесение на счет, и таким образом можно смоделировать простые банковские счета. Вот процедура, которая возвращает объект-«банковский счет» с указанным начальным балансом: (define (make-account balance) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Недостаточно денег на счете")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch m) (cond ((eq? m withdraw) withdraw) ((eq? m deposit) deposit) (else (error "Неизвестный вызов -- MAKE-ACCOUNT" m)))) dispatch) Каждый вызов make-account создает окружение с локальной переменной состояния balance. Внутри этого окружения make-account определяет процедуры deposit и withdraw, которые обращаются к balance, а также дополнительную процедуру dispatch, которая принимает «сообщение» в качестве ввода, и возвращает одну из двух локальных процедур. Сама процедура dispatch возвращается как значение, которое представляет объект-банковский счет. Это не что иное, как стиль программирования с передачей сообщений (message passing), который мы видели в разделе 2.4.3, но только здесь мы его используем в сочетании с возможностью изменять локальные переменные. Make-account можно использовать следующим образом: (define acc (make-account 100)) ((acc withdraw) 50) 50 ((acc withdraw) 60) "недостаточно денег на счете" ((acc deposit) 40) 90 ((acc withdraw) 60) 30 |
Среды: 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 | ||