|
||||
Меню:
Главная
Форум
Литература: Программирование и ремонт Импульсные блоки питания Неисправности и замена Радиоэлектронная аппаратура Микросхема в ТА Рубрикатор ТА Кабельные линии Обмотки и изоляция Радиоаппаратура Гибкие диски часть 2 часть 3 часть 4 часть 5 Ремонт компьютера часть 2 Аналитика: Монтаж Справочник Электроника Мощные высокочастотные транзисторы 200 микросхем Полупроводники ч.1 Часть 2 Алгоритмические проблемы 500 микросхем 500 микросхем Сортировка и поиск Монады Передача сигнала Электроника Прием сигнала Телевидиние Проектирование Эвм Оптимизация Автомобильная электроника Поляковтрансиверы Форт Тензодатчик Силовые полевые транзисторы Распределение частот Резисторные и термопарные Оберон Открытые системы шифрования Удк |
[60] не было возможности, поскольку наша система меток типов требует, чтобы каждый объект данных был снабжен меткой. Однако на самом деле все реализации Лиспа имеют систему типов, которую они используют внутри себя. Элементарные процедуры вроде symbol? или number? определяют, относится ли объект к определенному типу. Измените определения type-tag, contents и attach-tag из раздела 2.4.2 так, чтобы наша обобщенная система использовала внутреннюю систему типов Scheme. То есть, система должна работать так же, как раньше, но только обычные числа должны быть представлены просто в виде чисел языка Scheme, а не в виде пары, у которой первый элемент символ scheme-number. Упражнение 2.79. Определите обобщенный предикат равенства equ? , который проверяет два числа на равенство, и вставьте его в пакет обобщенной арифметики. Операция должна работать для обычных чисел, рациональных и комплексных. Упражнение 2.80. Определите обобщенный предикат =zero?, который проверяет, равен ли его аргумент нулю, и вставьте его в пакет обобщенной арифметики. Предикат должен работать для обычных, рациональных и комплексных чисел. 2.5.2 Сочетание данных различных типов Мы видели, как можно построить объединенную арифметическую систему, которая охватывает обыкновенные числа, комплексные числа, рациональные числа и любые другие типы чисел, которые нам может потребоваться изобрести, но мы упустили важный момент. Операции, которые мы до сих пор определили, рассматривают различные типы данных как совершенно независимые. Таким образом, есть отдельные пакеты для сложения, например, двух обыкновенных чисел и двух комплексных чисел. Мы до сих пор не учитывали того, что имеет смысл определять операции, которые пересекают границы типов, например, сложение комплексного числа с обычным. Мы затратили немалые усилия, чтобы воздвигнуть барьеры между частями наших программ, так, чтобы их можно было разрабатывать и понимать по отдельности. Нам бы хотелось добавить операции со смешанными типами по возможности аккуратно, так, чтобы мы их могли поддерживать, не нарушая всерьез границ модулей. Один из способов управления операциями со смешанными типами состоит в том, чтобы определить отдельную процедуру для каждого сочетания типов, для которых операция имеет смысл. Например, мы могли бы расширить пакет работы с комплексными числами и включить туда процедуру сложения комплексных чисел с обычными, занося ее в таблицу с меткой (complex scheme-number) :49 ;; включается в пакет комплексных чисел (define (add-complex-to-schemenum z x) (make-from-real-imag (+ (real-part z) x) (imag-part z))) (put add (complex scheme-number) (lambda (z x) (tag (add-complex-to-schemenum z x)))) 49Придется к тому же написать почти такую же процедуру для типа (scheme-number complex). Этот метод работает, но он очень громоздок. При такой системе стоимость введения нового типа не сводится к тому, чтобы построить пакет процедур для этого типа, но включает еще построение и установку процедур, осуществляющих операции со смешанными типами. Это запросто может потребовать больше кода, чем нужно, чтобы определить операции над самим типом. Кроме того, этот метод подрывает нашу способность сочетать отдельные пакеты аддитивно, или, по крайней мере, ограничивать степень, в которой реализация отдельного пакета должна принимать другие пакеты в расчет. Скажем, в вышеприведенном примере, кажется естественным, чтобы ответственность за обработку смешанных операций с обычными и комплексными числами лежала на комплексном пакете. Однако сочетание рациональных и комплексных чисел может осуществляться комплексным пакетом, рациональным пакетом, или каким-нибудь третьим, который пользуется операциями, извлеченными из этих двух. Формулировка ясных правил разделения ответственности между пакетами может стать непосильной задачей при разработке систем с многими пакетами и многими смешанными операциями. Приведение типов В ситуации общего вида, когда совершенно несвязанные друг с другом операции применяются к совершенно друг с другом не связанным типам, явное написание операций со смешанными типами, как бы это ни было громоздко, -все, на что мы можем рассчитывать. К счастью, обычно мы можем воспользоваться дополнительной структурой, которая часто в скрытом виде присутствует в нашей системе типов. Часто различные типы данных не совсем независимы, и каким-то образом объекты одного типа можно рассматривать как объекты другого. Такой процесс называется приведением типов (coercion). Например, если нас просят найти некоторую арифметическую комбинацию обычного числа и комплексного, то мы можем рассматривать обычное число как такое комплексное, у которого мнимая часть равна нулю. Это сводит нашу задачу к сочетанию двух комплексных чисел, а с этим может стандартным способом справиться пакет комплексной арифметики. В общем случае мы можем реализовать эту идею, проектируя процедуры приведения типа, которые переводят объект одного типа в эквивалентный ему объект другого типа. Вот типичная процедура приведения типов, которая преобразует данное обыкновенное число в комплексное, у которого есть действительная часть, а мнимая равна нулю: (define (scheme-number->complex n) (make-complex-from-real-imag (contents n) 0)) Мы записываем процедуры приведения типа в специальную таблицу приведения типов, проиндексированную именами двух типов: (put-coercion scheme-number complex scheme-number->complex) (Предполагается, что для работы с этой таблицей существуют процедуры put-coercion и get-coercion.) Как правило, часть ячеек этой таблицы будет пуста, потому что в общем случае невозможно привести произвольный объект произвольного типа ко всем остальным типам. К примеру, нет способа привести произвольное комплексное число к обыкновенному, так что в таблице не появится общая процедура complex->scheme-number. Когда таблица приведения типов построена, мы можем работать с приведением стандартным образом, приспособив для этого процедуру apply-generic из раздела 2.4.3. Когда нас просят применить операцию, мы первым делом, как и раньше, проверяем, не определена ли уже операция для типов аргументов. Если да, мы вызываем процедуру, найденную в таблице операций и типов. Если нет, мы пробуем применить приведение типов. Для простоты мы рассматриваем только тот случай, когда аргументов два.50 Мы проверяем таблицу преобразования типов и смотрим, можно ли объект первого типа привести ко второму типу. Если да, осуществляем приведение и снова пробуем операцию. Если объекты первого типа в общем случае ко второму не приводятся, мы пробуем приведение в обратном направлении и смотрим, нет ли способа привести второй аргумент к типу первого. Наконец, если нет никакого известного способа привести один тип к другому, мы сдаемся. Вот эта процедура: (define (apply-generic op . args) (let ((type-tags (map type-tag args))) (let ((proc (get op type-tags))) (if proc (apply proc (map contents args)) (if (= (length args) 2) (let ((typel (car type-tags)) (type2 (cadr type-tags)) (al (car args)) (a2 (cadr args))) (let ((tl->t2 (get-coercion typel type2)) (t2->tl (get-coercion type2 typel))) (cond (tl->t2 (apply-generic op (tl->t2 al) a2)) (t2->tl (apply-generic op al (t2->tl a2))) (else (error "Нет метода для этих типов" (list op type-tags)))))) (error "Нет метода для этих типов" (list op type-tags))))))) Такая схема приведения типов имеет много преимуществ перед методом явного определения смешанных операций, как это описано выше. Хотя нам по-прежнему требуется писать процедуры приведения для связи типов (возможно, n2 процедур для системы с n типами), для каждой пары типов нам нужно написать только одну процедуру, а не по процедуре на каждый набор типов и каждую обобщенную операцию.51 Здесь мы рассчитываем на то, что требуемая трансформация типов зависит только от самих типов, и не зависит от операции, которую требуется применить. 50Обобщение см. в упражнении 2.82. 51 Если мы умные, мы обычно можем обойтись меньше, чем n2 процедурами приведения типа. Например, если мы знаем, как из типа 1 получить тип 2, а из типа 2 тип 3, то можно использовать это знание для преобразования из 1 в 3. Это может сильно уменьшить количество процедур, которые надо явно задавать при введении нового типа в систему. Если нам не страшно ввести в свою систему требуемый уровень изощренности, мы можем заставить ее искать по «графу» отношений между типами и автоматически порождать все процедуры приведения типов, которые можно вывести из тех, которые явно заданы. |
Среды: 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 | ||