Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ещё одна методичка по ЛО.doc
Скачиваний:
18
Добавлен:
23.03.2016
Размер:
433.15 Кб
Скачать

2.1.5. Поколения языков

Средства описания данных в языках программирования имеют две стороны: машинную (или реализационную), связанную с реализацией, и человеческую (или общематематическую).

Конкретная система типов представляет собой некоторый баланс, компромисс между этими двумя сторонами. История развития языков программирования представляет собой переход от языков с превалирующей первой стороной к языкам с превалирующей второй стороной. С этой точки зрения они делятся на три поколения.

К первому поколению относятся языки с минимальными возможностями типизации (например, Фортран, Алгол-60). Они предоставляют лишь средства для описания переменных простых типов и массивов; никаких новых типов вводить нельзя.

Ко второму поколению относятся языки, предоставляющие программисту основные конструкторы типов: массивы, записи, объединения (например, ПЛ/1, Алгол-68, Паскаль,С). В них тип рассматривается как множество значений, получаемых из базисных множеств с помощью конструкторов. Все операции над типами данных предопределенные определяемые языком, а не программистом. В этих языках определяемые программистом новые типы могут получать имена, но с ними нельзя связывать новых, специально вводимых операций.

К третьему поколении относятся языки, предоставляющие программисту средства определения абстрактных типов данных (например, Симула-67, Модула, Ада). Здесь типы понимаются как множества с операциями. Для этих языков действуют все те проблемы и преимущества типизации, которые мы рассмотрели.

2.2. Простые типы данных

Объекты простых типов не имеют внутренней структуры, они могут содержать лишь одно неделимое значение.

2.2.1. Числовые типы

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

Описание целых типов имеет вид

type имя_типа is integer

range нижняя_граница.. верхняя_граница;

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

type BIG_INT is integer range -2147483648..2147483647,

INT is integer range -32768..32767,

SMALL_INT is integer range -128..127;

компилятор обладает достаточной информацией для реализации различных множеств целых чисел на любой машине независимо от длины ее слова. На 32-разрядных ЭВМ все эти три типа могут быть представлены в рамках одного слова, на 16-разрядных для BIG_INT требуется представление и использование двух слов, а на 8-разрядных требуется двухсловное представление для INT и четырехсловное для BIG_INT. Таким образом, указание диапазона позволяет строить мобильные программы, сохраняя эффективность их реализации на мощных ЭВМ.

Естественно было бы предоставить возможность вообще не указывать диапазона (задавать бесконечные границы), когда программист не знает заранее ограничений на множество возможных значений или таких ограничений вообще нет. Например, описание типов с бесконечными диапазонами:

type MAX_INT is integer,

POS_INT is integer 1..,

NEG_INT is integer ..-1;

Однако реализация таких типов сложна (требует программного моделирования арифметических операций с использованием многословного представления чисел), поэтому обычно предполагается, что отсутствие диапазона эквивалентно использованию некотором максимально предопределенного диапазона и все диапазоны, указываемые программистом, должны лежать внутри этот диапазона.

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

имя_типа' FIRST

имя_типа' LAST

(вместо имени типа можно указывать имя переменной). Они дают соответственно значения нижней и верхней границы возможных значений указанного типа. Для описанных выше типов

NT' FIRST дает значение -32768, NEG_INT' LAST дает -1.

Тип этих значений совпадает с указанным. Использование атрибутов позволяет повысить мобильность программ.

Константы целого типа имеют предопределенные имена, совпадающие с принятыми в математике, например

1,-153, 2147483647.

С целыми числами можно выполнять следующие операции: сложение (+), вычитание (-), умножение (*), деление (/), остаток от деления (mod), возведение в степень (**), сравнение на равенство (=), сравнение на неравенство (/=), сравнение на меньше (<), сравнение на меньше или равно (<=), сравнение на больше (>), сравнение на больше или равно (>=), унарный плюс (+), унарный минус (-), абсолютное значение (abs).

Семантика всех операций, кроме / и mod, очевидна и не требует объяснений. Операции / и mod, однако, нужно пояснить.

Результат операции деления всегда округляется (т.е. целый).

Важно, в какую сторону производится округление. При делении положительных чисел общепринятой практикой (как в математике, так и при машинной реализации) является округление в сторону нуля. Однако в большинстве ЭВМ, когда один из операндов отрицательный, результат также округляется в сторону нуля, что уже отличается от математического понятия целой части. Здесь возможны четыре решения:

включить проверку знака и сделать корректировку для отрицательных операндов;

запретить отрицательные операнды;

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

оставить так, как есть.

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

Установив семантику операции деления, операцию нахождения остатка от деления можно определить следующим соотношением:

Х mod Y = Х-(Х/Y)*Y, где Х, Y -- целые числа.

Описание действительных типов имеет вид

type имя_типа is real

range нижняя_граница..верхняя_граница

digits количество значащих цифр;

или

type имя_типа is real

range нижняя_граница..верхняя_граница

delta абсолютная_точность;

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

──────────────────────

П р и м е р 2. 3. Указание границ и точности представления действительных типов:

type FLOAT is real range -7.2Е75..7.2Е75 digits 6,

FIXED is real range -2.0..2.0 delta 0.01;

Числа типа FLOAT представляются в форме с плавающей точкой, которая состоит из мантиссы, содержащей 6 десятичных цифр, и порядка, содержащего числа до 76. Порядок имеет и минимально возможное значение, которое обычно полагается равным максимально возможному с отрицательным знаком, поэтому тип FLOAT имеет минимальное представимое значение. Кроме этого, тип FLOAT содержит и 0.0

Числа типа FIXED представляются в форме с фиксированной точкой, которая состоит из целой части, содержащей одну десятичную цифру, и дробной части, содержащей две десятичные цифры.

──────────────────────────

В общем случае числа с фиксированной точкой представляются в таком виде, чтобы можно было хранить числа вида i*d, где i -- целые числа в диапазоне от (нижняя_граница/d) до (верхняя_граница/d); d -- абсолютная точность. С фиксированными типами возникает много проблем, связанных с их представлением и реализацией операций, которые усложняют семантику языка, поэтому использовать их следует лишь на машинах без аппаратной реализации арифметики с плавающей точкой для быстрого выполнения действий с нецелыми числами.

Как и для целых типов, указание диапазона значений и количества значащих цифр обеспечивает мобильность и эффективность языка. Действительные типы можно использовать без указания диапазона или количества значащих цифр, например:

type MAX_REAL is real,

POS_REAL is real range О..,

NEG_REAL is real range ..О,

APPR_REAL is real digits 6;

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

Чтобы узнать характеристики некоторого пользуются следующие атрибуты типа:

имя_типа' FIRST-- нижняя граница значений

имя_типа' LAST-- верхняя граница значений

имя_типа' DIGITS -- количество цифр мантиссы

имя_типа' EPSILON -- абсолютное значение разности между

1.0 и ближайшим к 1.0 большим его

числом данного типа

имя_типа' SMALL значение минимального положитель-

ном числа

имя_типа' LARGE значение максимального положитель-

ного числа

имя_типа' DELTA -- абсолютная точность

(вместо имени_типа можно указывать имя переменной).

Константы действительного типа имеют три вида предопределенных имен:

запись с точкой: 10. 4, -О. 000000000145

запись с порядком: 104Е-1, -145Е-12

запись с точкой и порядком:1.04Е1 , -1.45Е-10

Над действительными числами можно выполнять следующие операции: сложение (+), вычитание (-), умножение (*), деление (/), возведение в степень (**), сравнение на равенство (=), сравнение на неравенство (/=), сравнение на меньше (<), сравнение на меньше или равно (<=), сравнение на больше (>), сравнение на больше или равно (>=), унарный плюс (+), унарный минус (-), абсолютное значение (abs).

Семантика этих операций очевидна и не вызывает трудностей. Однако при обработке действительных чисел с конечной точностью представления следует учитывать возможные ошибки округления (особенно при сравнении их на равенство и неравенство). Например, может оказаться, что (1Е0/ЗЕ0)*ЗЕО /= 1Е0. Поэтому следует сравнивать действительные числа лишь на приближенное равенство и неравенство, например

if abs(Х-Y)<0.00001*abs(Х) then.....

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

складывать наименьшие члены сумм первыми;

определять все положительные и отрицательные слагаемые и складывать их поочередно;

избегать вычитания двух почти равных чисел; если это все же необходимо, производить вычитание до умножения или деления (т.е. лучше записать А*(В-С), чем А*В-А*С);

избегать показателей степени в форме действительных чисел, так как в этом случае степень вычисляется через log и exp;

напротив, С**2 (если такая операция допускается) вычисляется как С*С;

использовать операцию извлечения квадратного корня sqrt(если она есть) вместо С**0. 5, так как sqrt обычно вычисляется точнее;

использовать в процессе вычисления максимально возможную точность, если это необходимо;

уменьшать число операций;

избегать цепочек операций, в которых используются неточные значения;

использовать алгоритмы, для которых известны оценки и границы ошибок.