Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Демидов Основы программирования в примерах на языке ПАСЦАЛ 2010

.pdf
Скачиваний:
128
Добавлен:
16.08.2013
Размер:
1.28 Mб
Скачать

Аналогично функции fib(n) факториал вычисляется в постоянном объеме памяти, и в последней рекурсивной ветви результат уже вычислен.

Иллюстрация работы обычной рекурсии для 4!:

fact(4) 4*fact(4-1) 4*fact(3) 4*(3*fact(3-1)) 4*(3*fact(2)) 4*(3*(2*fact(2-1))) 4*(3*(2*fact(1))) 4*(3*(2*1)) 4*(3*2)

4*6

24

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

101

Глава 10. Строки и множества

Символы и строки в языке Паскаль

Строка в языке Паскаль представляет собой цепочку символов длиной от 0 до 255, причем программисту предоставляется возможность самому определять максимальную длину строки, но не более 255. Известно, что любые символы кодируются числами в соответствии с ASCII-таблицей и для работы со строками можно было бы обойтись массивами. Однако в силу того, что область применения строк сильно отличается от области применения массивов, в Паскаль были введены два новых типа: char для символов и string для цепочек символов.

Для решения простых задач 255 символов в строке оказывается достаточно, но в сложных задачах такое ограничение на длину только снижает общность алгоритма. В Паскале имеется другой тип данных PChar, представляющий собой цепочку символов, которая замыкается нулевым символом (с ASCII-кодом 0). Длина такой строки ограничивается максимальным объемом памяти, который система может адресовать. В 32-разрядных системах адрес занимает 4 байта (32 бита), т.е. мощность пространства адресов равна 2^32 = 4 Гб. В реальности операционная система не может отвести программе всё адресное пространство, так как ей самой требуется определенный ресурс. В языке Си такие строки носят название ASCII-z, т.е. строки символов ASCII, завершающиеся 0 (zero). Для определения длины такой строки достаточно вычислить порядковый номер нулевого символа относительно первого символа строки.

В современных версиях языка Паскаль поддерживается кодировка символов Unicode, в которой под каждый символ отводится по два байта. Данное представление позволяет закодировать уже 2^16 = 65536 различных символов, что оказывается достаточным для кодировки символов всех языков планеты. Для удобства работы с различными представлениями строк программисту предоставляются процедуры преобразования строк одного типа в другой.

Примеры объявления строковых переменных:

var s: string;

const n = 15;

102

var s1: string[n];

type string4 = string[4]; // строка, занимающая 5 байт var s2: string4;

Если строковой переменной присваивается строка, длина которой превышает допустимую длину строковой переменной, то строка усекается справа до нужной длины.

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

s := 'Hello world';

// обычная строка

s

:= '';

// пустая строка

s

:= 'Don''t do it';

// строка с одним апострофом внутри

К символам строки можно обращаться как к элементам массива по индексу, причём символы в строке нумеруются с 1. Взятый по индексу символ можно трактовать и как значение типа char, и как значение типа string. Изменение символа в строке по его индексу допустимо, однако не приветствуется:

ch := s[3]; s2 := s[4]; s[3] := 'm';

Поскольку символы, по сути, являются кодами в ASCII-таблице, то в Паскале имеется возможность задания символа по его коду, а также определен ряд функций для работы с кодами символов:

ch := #100; // 100 – код буквы 'd'

ch := chr(100); // получение символа по коду code := ord(ch); // получение кода по символу

Таким образом, chr(ord(ch)) = ch и ord(chr(code)) = code.

Строки можно выводить на экран и вводить с клавиатуры с помощью стандартных процедур writeln() и readln(), символы – с помощью write() и read() соответственно.

103

Строковые операции

Важной операцией для работы со строками является конкатенация (склеивание) строк. Её результатом является строка, образованная в результате стыковки конца первой строки с началом второй строки. Обозначается операция знаком + и записывается в инфиксной форме, т.е. между аргументами. Например:

message := 'Length = ' + IntToStr(Length(s)) + ' symbols';

Для символов и строк определены операции отношения. При сравнении символов реально сравниваются их ASCII-коды, поэто-

му, например, 'W' < 'w', '$' < '?', 'я' <'Ё'. Символы цифр от '0' до '9'

упорядочены и имеют коды от 48 до 57 соответственно.

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

1)пустая строка меньше любой другой;

2)две строки равны тогда и только тогда, когда они имеют одинаковую длину и все символы с равными индексами в строках совпадают. Если одна из строк полностью совпадает с началом другой, то по первому правилу она считается меньшей;

3)если строки не равны, то сравниваются первые различные символы от начала строк. Меньшей считается та строка, у которой символ имеет меньший ASCII-код.

Примеры:

'abc' = 'abc',

правило 2

 

'abc' > 'ab',

правило 1 ('с' > '')

'abc' < 'abc ',

правило 1 ('' < ' ')

'abc' < 'xyz',

правило 3 ('a' < 'x')

'a' < 'abc',

правило 3

('' < 'bc')

'1200' < '45',

правило 3

('1' < '4')

'Anny' < 'anny',

правило 3

('A' < 'a')

Подпрограммы для работы со строками

В стандартной библиотеке реализован ряд полезных подпрограмм для работы со строками. Рассмотрим некоторые из них:

procedure Insert(Source: string; var S: string; Index: Integer)

вставка строки внутрь другой строки. Процедура имеет три аргу-

104

мента: Source – вставляемая строка, S – исходная строка, Index – позиция в исходной строке, в которую следует вставить другую строку. Меняется строка S:

S := 'Hello

world';

S = 'Hello my world'

Insert(' my

', S, 6)

procedure Delete(var S: string; Index, Count: Integer)

удаление части строки. Процедура имеет три аргумента: S – исходная строка, Index – позиция в исходной строке, начиная с которой следует удалять символы, Count – максимальное число удаляемых символов. Меняется строка S:

S := 'Hello world';

S = 'Helrld'

Delete(S, 4, 5)

Delete(S, 10, 5)

S = 'Hello wor'

function Copy(S: string; Index, Count: Integer): string – копи-

рование подстроки. Функция принимает три аргумента: S – строка, Index – индекс начального символа, с которого начинается копирование, Count – количество копируемых символов. Возвращается участок строки S от индекса Index длиной не более Count:

Copy('Hello world', 4, 5) = 'lo wo'

Copy('Hello', 4, 5) = 'lo'

function Pos(Substr: string; S: string): Integer – поиск подстро-

ки в строке. Функция принимает два аргумента: Substr – подстрока для поиска, S – строка, в которой следует искать подстроку. Возвращается первая слева позиция найденной подстроки. Если подстрока не найдена, то возвращается 0:

Pos('llo', 'Hello world') = 3

Pos('low', 'Hello world') = 0

procedure Str(X [: Width [: Decimals ]]; var S) – преобразование числа в строковое представление. Число X может быть как целого, так и вещественного типа. Форматирующие модификаторы Width и Decimals указывают общую длину строки и число цифр после запятой, аналогично writeln. В S возвращается полученная строка:

Str(36.6:7:2, S)

S = ' 36.60'

105

procedure Val(S; var V; var Code: Integer) – преобразование строки в числовое представление. Процедура имеет три параметра: S – символьное представление числа, V – числовая переменная, в которую записывается результат преобразования, Code – числовая переменная для возврата ошибки. В случае успешного преобразования Code равна нулю, в случае ошибки в Code помещается индекс символа, который привел к возникновению ошибки:

Val('36.6', n, code) n = 36.6, code = 0

Val('780*2', n, code) code = 4

Процедуру Val удобно использовать для безопасного чтения числа с экрана. Например:

begin

//запрашиваем пользователя до тех пор,

//пока не будет введено число

repeat

writeln('Enter a number: ');

readln(str); // безопасное чтение в строку

Val(str, number, code); // безопасное преобразование if code > 0 then writeln('Wrong input');

until code = 0;

end.

Можно оформить этот фрагмент кода в виде функции.

Примеры подпрограмм редактирования строк

1. Реализовать процедуру insert:

procedure Insert(sub: string; var s: string; ind: integer); var

i: integer; res: string;

begin

res := '';

for i:=1 to ind -1 do res := res + s[i];

res := res + sub;

for i := ind to length(s) do res := res + s[i];

s := res;

106

end;

2. Удалить лишние пробелы между словами в строке. Рассмотрим следующее решение:

procedure Filter(var S: string); var

i: integer; begin

for i:=1 to length(s)-1 do begin

if (s[i] = ' ') and (s[i+1] = ' ') then Delete(s,i,1); end;

end;

В ходе работы со строкой уменьшается её длина, а граничные значения счётчика цикла for вычисляются один раз ещё до первого витка. Значит, функция length(s) выполнится один раз, и цикл for не узнает об изменении длины строки внутри цикла. В данном случае произойдет ошибочное обращение к несуществующему элементу строки. В общем случае, если строка укорачивается, то цикл будет выполняться лишнее число раз, а если строка удлиняется, то цикл завершится преждевременно. Вместо цикла for следует использовать while, так как условие цикла while вычисляется перед каждым витком:

procedure Filter(var S: string); var

i: integer; begin

i:=1;

while i<length(s)-1 do begin

if (s[i] = ' ') and (s[i+1] = ' ') then Delete(s,i,1) else i := i+1; // нюанс

end; end;

Если убрать else, то из трех пробелов будет удаляться лишь второй.

Множества и типы множеств

В Паскале множество – совокупность элементов. Типмножество – тип данных, определяющий множества на конечном наборе элементов. Элементы множества считаются неупорядочен-

107

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

Множества-переменные описываются следующим образом:

var <имя множества>: set of <тип элементов множества>;

Например:

var

//множество символов (256 элементов) CharSet: set of char;

//множество букв латинского алфавита

LettersSet: set of 'a'..'z','A'..'Z';

//множество цифр (максимум 10 элементов) DigitSet: set of 0..9;

//множество значений истинности (максимум 2 элемента) BoolSet: set of boolean;

Тип-множество определяется по общим правилам. Например,

type

TCharSet = set of char;

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

[] – пустое множество;

[e1, e2, e5, en] – непустое множество.

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

Теоретико-множественные операции в языке Паскаль

S1 * S2 Пересечение множеств S1 и S2.

S1 + S2 Объединение множеств S1 и S2.

S1 – S2 Разность множеств S1 и S2.

E in S Проверка принадлежности элемента E множеству S. Результат – значение истинности.

108

S1=S2 Проверка на равенство множеств S1 и S2. Результат – значение истинности.

S1<S2 Проверка на включение множества S1 в S2. Результат

– значение истинности.

S1>S2 Проверка на включение множества S2 в S1. Результат

– значение истинности.

Задания на множества и строки

1. Выразить множества D, F на диаграмме (рис. 8) через множества А, B, C:

A D B

F

C

Рис. 8. Диаграмма пересечения множеств

Ответ:

D := A*B-C;

F := A*B*C;

Чему равно значение выражений C > D, F < C*B, D+F = A*B?

Ответ: False, True, True.

2. Вывести на экран содержимое множества. для вывода на экран содержимого множества необходимо вывести по отдельности каждый элемент множества:

var

s: set of char; k: byte;

begin

for k := 0 to 255 do

if chr(k) in s then write(chr(k), ' ');

end.

109

3. Оставить в строке только первое вхождение каждого символа, взаимный порядок оставленных символов сохранить. Использовать множества:

var

s: set of char; input, result: string; i: byte;

begin

s := []; result := '';

for i := 1 to length(input) do if not (input[i] in s)

then begin

result := result + input[i];

s := s+[input[i]]; // добавление элемента к множеству end;

end.

4. Дана строка символов, содержащая путь к файлу. Выделить имя диска, расширение файла и имя файла, если они есть. При отсутствии соответствующих данных возвращаются пустые строки:

procedure SplitPath(Path: string; var disk, name, ext: string);

var i: integer; begin

disk := copy(Path, 1, pos(':\',Path)); i:=length(Path);

while (i>0) and (Path[i] <> '.') do i := i-1; ext := copy(Path, i+1, length(Path)-i); while (i>0) and (Path[i] <> '\') do i := i-1;

name := copy(Path, i+1, length(Path)-i-length(ext)-1); end;

5. Реализовать функцию преобразования строки в число с плавающей запятой:

function Evaluate(s: string): real; var

i: integer; int, frac: real; comma: boolean;

begin

int := 0; frac := 0;

110

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]