Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[58]

Передача сообщений

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

Альтернативой такой стратегии реализации будет разбить таблицу по столбцам и вместо «умных операций», которые диспетчируют по типам данных, работать с «умными объектами данных», которые диспетчируют по именам операций. Мы можем этого добиться, если устроим все так, что объект данных, например комплексное число в декартовом представлении, будет представляться в виде процедуры, которая в качестве входа воспринимает имя операции и осуществляет соответствующее ей действие. При такой организации можно написать make-from-real-imag в виде

(define (make-from-real-imag x y) (define (dispatch op)

(cond ((eq? op real-part) x) ((eq? op imag-part) y) ((eq? op magnitude)

(sqrt (+ (square x) (square y)))) ((eq? op angle) (atan y x)) (else

(error "Неизвестная оп. - MAKE-FROM-REAL-IMAG" op))))

dispatch)

Соответствующая процедура apply-generic, которая применяет обобщенную операцию к аргументу, просто скармливает имя операции объекту данных и заставляет его делать всю работу:48

(define (apply-generic op arg) (arg op))

Обратите внимание, что значение, возвращаемое из make-from-real-imag, является процедурой - это внутренняя процедура dispatch. Она вызывается, когда apply-generic требует выполнить обобщенную операцию.

Такой стиль программирования называется передача сообщений (message passing). Имя происходит из представления, что объект данных - это сущность, которая получает имя затребованной операции как «сообщение». Мы уже встречались с примером передачи сообщений в разделе 2.1.3, где мы видели, как cons, car и cdr можно определить безо всяких объектов данных, с одними только процедурами. Теперь мы видим, что передача сообщений не математический трюк, а полезный метод организации систем с обобщенными операциями. В оставшейся части этой главы мы будем продолжать пользоваться программированием, управляемым данными, а не передачей сообщений, и рассмотрим обобщенные арифметические операции. Мы вернемся к передаче

48У такой организации есть ограничение: она допускает обобщенные процедуры только от одного аргумента.


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

Упражнение 2.75.

Реализуйте в стиле передачи сообщений конструктор make-from-mag-ang. Он должен быть аналогичен приведенной выше процедуре make-from-real-imag.

Упражнение 2.76.

Когда большая система с обобщенными операциями развивается, могут потребоваться новые типы объектов данных или новые операции. Для каждой из трех стратегий - обобщенные операции с явной диспетчеризацией, стиль, управляемый данными, и передача сообщений, - опишите, какие изменения нужно произвести в системе, чтобы добавить новый тип или новую операцию. Какая организация лучше подходит для системы, в которую часто добавляются новые типы? Какая для системы, где часто появляются новые операции?

2.5 Системы с обобщенными операциями

В предыдущем разделе мы увидели, как проектировать системы, где объекты данных могут быть представлены более чем одним способом. Основная идея состоит в том, чтобы связать код, который определяет операции над данными, и многочисленные реализации данных, при помощи обобщенных процедур интерфейса. Теперь мы увидим, что ту же самую идею можно использовать не только для того, чтобы определять обобщенные операции для нескольких реализаций одного типа, но и для того, чтобы определять операции, обобщенные относительно нескольких различных типов аргументов. Мы уже встречались с несколькими различными пакетами арифметических операций: элементарная арифметика (+, -, *, /), встроенная в наш язык, арифметика рациональных чисел (add-rat, sub-rat, mul-rat, div-rat) из раздела 2.1.1 и арифметика комплексных чисел, которую мы реализовали в разделе 2.4.3. Теперь мы, используя методы программирования, управляемого данными, создадим пакет арифметических операций, который включает все уже построенные нами арифметические пакеты.

На рисунке 2.23 показана структура системы, которую мы собираемся построить. Обратите внимание на барьеры абстракции. С точки зрения человека, работающего с «числами», есть только одна процедура add, которая работает, какие бы числа ей ни дали. Add является частью обобщенного интерфейса, который позволяет программам, пользующимся числами, одинаковым образом обращаться к раздельным пакетам обыкновенной, рациональной и комплексной арифметики. Всякий конкретный арифметический пакет (например, комплексная арифметика) сам по себе доступен через обобщенные процедуры (например, add-complex), которые связывают пакеты, предназначенные для различных реализаций (таких, как декартовы и полярные числа). Более того, структура системы аддитивна, так что можно проектировать отдельные арифметические пакеты независимо и сочетать их, получая обобщенную арифметическую систему.


Глава 2. Построение абстракций с помощью данных Программы, использующие числа

add sub mul div

Пакет обобщенной арифметики

add-rat sub-rat ~~ mul-rat div-rat

add-complex sub-complex mul-complex div-complex

+ - * / -

Рациональная арифметика

Комплексная арифметика

Обыкновенная арифметика

Декартова

Полярная

Списковая структура и элементарная арифметика машины

Рис. 2.23: Обобщенная арифметическая истема.

2.5.1 Обобщенные арифметические операции

Задача проектирования обобщенных арифметических операций аналогична задаче проектирования обобщенных операций с комплексными числами. К примеру, нам бы хотелось иметь обобщенную процедуру сложения add, которая действовала бы как обычное элементарное сложение + по отношению к обычным числам, как add-rat по отношению к рациональным числам и как add-complex по отношению к комплексным. Реализовать add и прочие обобщенные арифметические операции мы можем, следуя той же стратегии, которую мы использовали в разделе 2.4.3 для обобщенных селекторов комплексных чисел. К каждому числу мы прикрепим метку типа и заставим обобщенную процедуру передавать управление в нужный пакет в соответствии с типами своих аргументов.

Обобщенные арифметические процедуры определяются следующим образом:

(define(add x y)(apply-generic add x y))

(define(sub x y)(apply-generic sub x y))

(define(mul x y)(apply-generic mul x y))

(define(div x y)(apply-generic div x y))

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

(define (install-scheme-number-package) (define (tag x)

(attach-tag scheme-number x)) (put add (scheme-number scheme-number)

(lambda (x y) (tag (+ x y)))) (put sub (scheme-number scheme-number)



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32] [стр.33] [стр.34] [стр.35] [стр.36] [стр.37] [стр.38] [стр.39] [стр.40] [стр.41] [стр.42] [стр.43] [стр.44] [стр.45] [стр.46] [стр.47] [стр.48] [стр.49] [стр.50] [стр.51] [стр.52] [стр.53] [стр.54] [стр.55] [стр.56] [стр.57] [стр.58] [стр.59] [стр.60] [стр.61] [стр.62] [стр.63] [стр.64] [стр.65] [стр.66] [стр.67] [стр.68] [стр.69] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]