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


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




[29]

Представление рациональных чисел

Пары позволяют нам естественным образом завершить построение системы рациональных чисел. Будем просто представлять рациональное число в виде пары двух целых чисел: числителя и знаменателя. Тогда make-rat, numer и denom немедленно реализуются следующим образом:3

(define (make-rat n d) (cons n d))

(define (numer x) (car x))

(define (denom x) (cdr x))

Кроме того, когда нам требуется выводить результаты вычислений, мы печатаем рациональное число, сначала выводя его числитель, затем косую черту и затем знаменатель:4

(define (print-rat x) (newline)

(display (numer x)) (display "/") (display (denom x)))

Теперь мы можем опробовать процедуры работы с рациональными числами:

(define one-half (make-rat 1 2))

(print-rat one-half) 1/2

(define one-third (make-rat 1 3))

(print-rat (add-rat one-half one-third)) 5/6

(print-rat (mul-rat one-half one-third))

1/6

3 Другой способ определить селекторы и конструктор был бы

(define make-rat cons) (define numer car) (define denom cdr)

Первое определение связывает имя make-rat со значением выражения cons, то есть элементарной процедурой, которая строит пары. Таким образом, make-rat и cons становятся именами для одного и того же элементарного конструктора.

Такое определение конструкторов и селекторов эффективно: вместо того, чтобы заставлять make-rat вызывать cons, мы делаем make-rat и cons одной и той же процедурой, так что когда вызывается make-rat, происходит вызов только одной процедуры, а не двух. С другой стороны, это не дает работать отладочным средствам, которые отслеживают вызовы процедур или устанавливают на них контрольные точки: Вам может потребоваться следить за вызовами make-rat, но Вы уж точно никогда не захотите отслеживать каждый вызов cons.

В этой книге мы решили не использовать такой стиль определений.

4Display - элементарная процедура языка Scheme для печати данных. Другая элементарная процедура, newline, переводит строку при печати. Эти процедуры не возвращают никакого полезного значения, так что в примерах использования print-rat ниже, мы показываем только то, что печатает print-rat, а не то, что интерпретатор выводит как значение print-rat.


(print-rat (add-rat one-third one-third)) 6/9

Как показывает последний пример, наша реализация рациональных чисел не приводит их к наименьшему знаменателю. Мы можем исправить это упущение, изменив make-rat. Если у нас есть процедура gcd, вычисляющая наибольший общий делитель двух целых чисел, вроде той, которая описана в разделе 1.2.5, мы можем с помощью gcd сокращать числитель и знаменатель, прежде, чем построить пару:

(define (make-rat n d)

(let ((g (gcd n d))) (cons (/ n g) (/ d g))))

Теперь мы имеем

(print-rat (add-rat one-third one-third))

2/3

как нам того и хотелось. Эта модификация была произведена путем изменения конструктора make-rat, и мы не тронули ни одну из процедур (скажем, add-rat или mul-rat), которые реализуют сами операции.

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

Определите улучшенную версию mul-rat, которая принимала бы как положительные, так и отрицательные аргументы. Make-rat должна нормализовывать знак так, чтобы в случае, если рациональное число положительно, то и его числитель, и знаменатель были бы положительны, а если оно отрицательно, то чтобы только его числитель был отрицателен.

2.1.2 Барьеры абстракции

Прежде чем мы перейдем к другим примерам работы с составными данными и абстракцией данных, рассмотрим несколько вопросов, относящихся к примеру с рациональными числами. Мы определили операции над рациональными числами через конструктор make-rat и селекторы numer и denom. В общем случае основная идея абстракции данных состоит в том, чтобы определить для каждого типа объектов данных набор базовых операций, через которые будут выражаться все действия с объектами этого типа, и затем при работе с данными использовать только этот набор операций.

Мы можем представить себе структуру системы работы с рациональными числами так, как это показано на рис.2.1. Горизонтальные линии обозначают барьеры абстракции (abstraction barriers), которые отделяют различные «уровни» системы друг от друга. На каждом из этих уровней барьер отделяет программы, которые используют абстрактные данные (сверху) от программ, которые реализуют эту абстракцию данных (внизу). Программы, использующие рациональные числа, работают с ними исключительно в терминах процедур, которые пакет работы с рациональными числами предоставляет «для общего пользования»: add-rat, sub-rat, mul-rat, div-rat и equal-rat?. В свою очередь, эти процедуры используют только конструктор и селекторы make-rat, numer и denom, которые сами реализованы при помощи пар. Детали реализации пар не


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

add-rat sub-rat ...

Рациональные числа как числители со знаменателями

make-rat numer denom

Рациональные числа как пары

cons car cdr

То, как реализуются пары

Рис. 2.1: Барьеры абстракции данных в пакете для работы с рациональными числами.

имеют значения для остальной части пакета работы с рациональными числами; существенно только, что с парами можно работать при помощи cons, car и cdr. По существу, процедуры на каждом уровне являются интерфейсами, которые определяют барьеры абстракции и связывают различные уровни.

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

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

(define (make-rat n d) (cons n d))

(define (numer x)

(let ((g (gcd (car x) (cdr x))))

(/ (car x) g)))

(define (denom x)

(let ((g (gcd (car x) (cdr x))))

(/ (cdr x) g)))

Разница между этой реализацией и предыдущей состоит в том, когда мы вычисляем НОД с помощью gcd. Если при типичном использовании рациональных чисел к числителю и знаменателю одного и того же рационального



[стр.Начало] [стр.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]