![](/user_photo/2706_HbeT2.jpg)
Иванова Г.С. - Основы программирования
.pdf
|
|
4, Структурные типы данных |
|
|
||
тень |
хк |
тень |
хк |
|
тень |
хк |
x[i,l] |
|
x[i,2] |
x[U] |
x[i,2] |
x[i,l] |
x[i,2] |
S:= S+x[i,2]-xk |
S:=S+x[i,2]-x[i,l] |
S и X- не |
||||
xk:=x[i,2] |
|
xk:=x[i,2] |
|
меняются |
Рис. 4.25. Три случая добавления i-ro отрезка к «тени»:
а- отрезок частично перекрыт «тенью»; б - отрезок не перекрыт «тенью»;
в- отрезок полностью перекрыт «тенью» (в);
S - уже накопленная «тень», хк - правая граница этой «тени»
екций.) Количество отрезков п. Отрезки заданы координатами начала и кон ца проекций на ось х.
Анализ условия задачи и возможных вариантов отрезков показывает, что решение задачи «в лоб» достаточно сложно. В то же время, если бы отрезки были сортированы по левой границе, то вычисление тени можно было вы полнять, добавляя отрезки по одному. При этом можно было бы выделить три случая (рис. 4.25).
Окончательно алгоритм будет включать сортировку отрезков по левой границе и цикл накопления тени, включающий анализ вариантов добавле ния. Программа в этом случае имеет вид
Program ten;
Var х:array[1. .100J..2] of real; iJ,n,k:integer; xk,SyW:real;
Begin
Write('Beedume количество отрезков: *); Readln(n);
WriteLnCВводите начала и концы отрезков. *); for i:==I to n do ReadLn(x[iyl],x[i,2]);
{сортировка отрезков по возрастанию левой границы} yV=7; к:=1;
while к<>0 do
begin
к:=0;
for i:=J to n-j do |
|
|
ifx[ij]>x[i-^lj]then |
|
|
begin k:=k-^I; |
{меняем отрезки местами } |
|
w:=x[ij]; x[ij]:^x[i-^lj]; |
x[i+l,l]:==w; |
w: =xfi, 2J; x[l 2]: =xfi+J, 2J; xfi+ 7,2J: =w;
end;
111
Часть L Основы алгоритмизации и процедурное программирование
end; |
|
{определение тени} |
|
S:'=x[l,2]'X[lJ]; |
(длина первого отрезка} |
xk:=x[]JJ; |
{правая граница первого отрезка} |
for i:=2 to п do |
|
ifxfi, 1]> =xk then |
{случай б} |
begin S: =S-^xfi,2]'X[i, 1]; xk:=x[i,2J;
end
else ifx[i,2J>xk then {случай a} begin S: =S+xfi, 2J'Xk; xk:=xfi,2J;
end;
WriteIn('Длина тени равна \ S:6:2); End.
Задания для самопроверки
Задание 1. Дана матрица вещественного типа D(n,m), п, m < 20. Разработайте программу, которая в заданной матрице вычеркивает все строки, содержащие более трех отрицательных элементов. Вывести на печать исходную матрицу и матрицу-ре зультат или соответствующие сообщения, если таких строк не окажется или все строки будут удовлетворять условию.
Задание 2. Дана матрица A(n,m), п, m < 15. Разработайте программу, формиру ющую одномерный массив В(п), элементами которого должно являться количество элементов каждой строки, превышающих среднее арифметическое значение матри цы в целом. Если в строке таких элементов нет, в соответствующий элемент одно мерного массива заносится 0. Вывести исходную матрицу, значение среднего ариф метического элементов матрицы и сформированный массив В.
Задание 3. Разработайте программу, формирующую квадратную матрицу D(n,n), п < 15, элементы которой определяются по формуле
|
I |
sin(i+j) |
npHi<j; |
D[iJ]= |
|
1 |
npHi=j; |
|
I |
(i+j)/(2i+3j) |
npHi>j, |
где i - номер строки, a j - номер столбца элемента матрицы. В сформированной ма трице поменять местами максимальный элемент среди элементов, лежащих ниже главной диагонали, с минимальным элементом среди элементов матрицы, лежащих ниже побочной его диагонали. Вывести исходную матрицу, соответствующие эле менты и их координаты, а также преобразованную матрицу.
112
4. Структурные типы данных
4.5. Строки
Уже на простом примере обработки символьной информации, рассмот ренном в параграфе 4.1, видно, что обработка строк с использованием одно мерных массивов представляет собой достаточно специфическую задачу. В то же время большинство операций, которые выполняют со строками текста, повторяются в разных программах: поиск, копирование, удаление и вставка фрагментов строки. Поэтому для упрощения работы со строками в Borland Pascal существует специальный тип данных - строковый, который приспо соблен для обработки символьной информации.
Синтаксическая диаграмма объявления строкового типа данных пред ставлена на рис. 4.26.
Целое без знака - это максимальная длина строки, которая не должна превышать 255 байт. Если длина не указана, то по умолчанию принимается максимальное значение - 255 символов.
Объявление переменных строкового типа, так же как и массивов, можно выполнить двумя способами:
• в операторе объявления переменных, например:
Var Sly S2:string[40]; {символьные строки длиной 40 байт} S3:strmg: {символьная строка длиной 255 байт}
• с предварительным объявлением типов, например:
Туре S40 = strmg[40]; {тип - строка длиной 40 байт}
ST = string; {тип - символьная строка длиной 255 байт} Var SJ,S2: S40; {символьные строки типа S40}
S3:ST; {символьная строка типа ST}
Внутреннее представление строки показано на рис 4.27, откуда видно, что строка представляет собой одномерный символьный массив, индексы ко торого изменяются от О до максимального значения, указанного при объяв лении строкового типа. Следовательно, физическая длина строки на единицу превышает максимальную.
Инициализация строк. Для инициализации строковых переменных, так же как и переменных других типов, можно использовать типизированные
— « / string V j - |
^ |
f |
\ J ^ |
I без знака | |
v L / |
Рис. 4.26. Синтаксическая диаграмма <Объявление строкового типа>
113
Часть 1. Основы алгоритмизации и процедурное программирование
Максимальная длина строки ^ 255
А
[ s i |
1 |
lAJщ с|_ |
_^н^II |
1 М 1 1 ) |
|||||||
0 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 11 |
12 13 ... |
|
V |
|
Текущая |
Незанятая |
|
часть строки |
||
длина строки |
||
|
Рис. 4.27. Внутреннее представление строки
константы, причем строка-литерал может быть короче инициализируемой строки, например:
Const S:strmgf40]= Типизированная константа V
SJ:string= '*;... {пустая строка нулевой длины}
Операции над переменными строкового типа. Над переменными строкового типа помимо операции доступа к символам определены операции присваивания, конкатенации (сцепления) и отношений.
Доступ к сглмволам строки. Обращение к символам строки выполняет ся как к элементам массива символов, т. е. с указанием имени строки и номе ра элемента, например st[l] или s[i]. Нулевой байт содержит текущее значе ние длины строки, но так как строка - это массив символов, длина автомати чески интерпретируется как символ. Для получения текущей длины строки в виде числа необходимо явное преобразование символьного типа в целый. Например, если i - переменная целого типа, а S3 - переменная строкового типа, то получить текущую длину строки S3 можно:
1)/. = byte(S3[0]); {явное преобразование с помощью автоопределения}
2)i:-ord(S3[0]): {явное преобразование с помощью специальной функции}
Однако лучше это сделать, используя специальную функцию Length, описанную ниже.
Присваивание строк. Можно присвоить строке значение строки и значе ние символа. При выполнении операции символы заполненной части строки и ее длина переписываются в CTpoity-результат, например:
S1:- 'ABCD V {присваиваем строке значение строковой константы}
52.= 'А V |
{присваиваем строке значение символа} |
5.-=57; |
{переписываем одну CTpoiQ^ в другую} |
При присваивании строке значения символа последний интерпретирует ся как строка единичной длины. Если строка-источник длиннее, то при при сваивании она усекается в соответствии с длиной строки-результата.
114
4. Структурные типы данных
Конкатенация, Операция конкатенации позволяет сцепить строки с дру гими строками или символами. При сцеплении длины строк суммируются, а символы объединяются в одну последовательность. Например:
*fdc'+ 'ghj V {получаем ' fdcghj'}
S4+ Vvv V {к строке S4 дописывается 'vvv'}
Результат этой операции можно присвоить какой-либо строке или выве сти на экран.
Отношения. Над строками допускается выполнять операции отноше ния: = , о , >, <, >=, <=. Сравнение строк при этом выполняется последова тельно слева направо с учетом внутренней кодировки символов до первого несовпадающего символа. Большей считается та строка, код несовпадающе го символа которой по таблице ASCII больше. Если длина одной строки меньше другой, то недостающие значения до длины большей строки запол няются символами #0. Результатом операций отношения для строк, как и для чисел, является значение false и true.
Допускается сравнение символов со строками, при этом символы преоб разуются в строки единичной длины.
Так, если
S4:='ABCD'; S3:='ADFH'; C:='L';
то при выполнении операций отношения:
S4 = S3 {получим false}
S4 > S3 {получим false}
S3 > S4 {получим true}
S3 = С {получим false}
Ввод-вывод строк. Ввод-вывод переменных строкового типа осуществ ляется одной операцией Read (ReadLn) или Write (WriteLn), например:
ReadLn(S]);
WriteLn(Sl):
При вводе за строку принимается последовательность символов до кода клавиши ENTER. Если длина введенной строки больше указанной макси мальной длины, то лишние символы отбрасываются, а в нулевой байт запи сывается значение максимальной длины. В противном случае в нулевой байт записывается количество введенных символов. Поскольку строкой считают ся все символы до кода клавиши ENTER, ввести в одной строке строковое значение, а затем, например, число нельзя.
Если при вводе строки просто нажать клавишу Enter, не вводя никаких символов, то считается, что введена пустая строка.
115
Часть L Основы алгоритмизации и процедурное программирование
Процедуры и функции для работы со строками. Все основные дейст вия над строками и символами реализуют с помощью стандартных процедур
ифункций.
1.Функция Length(st):word- возвращает длину строки st, например:
n:=Length(stl); {целочисленной переменной п присваивается значе ние длины строки}
2. Процедура Delete(sU index, count) - удаляет count символов строки st, начиная с символа с номером index, например:
S1: = ^dddddsssssfffff;
Delete(Sl,6,5); {получим результат 'dddddfflfff'}
3. UpoixQjjypa. Insert(St2,Stl,index) - вставляет подстроку символов St2 в строку Stl, начиная с символа с номером index. Процедура обычно использу ется при формировании строк, включающих числовую информацию, напри мер:
SJ = 'dddddddddd'; S2 = 'аааааа';
Insert(Sl, S2,6); {получим 'dddddaaaaaaddddd'} Insert(Tas\ S2,6); {получим 'dddddPasaaaaaaddddd'}
4. Процедура Str(x[:w [:d]], St) - преобразует результат выражения x в строку st, содержащую запись этого числа в виде последовательности симво лов (как при выводе).
Примечание. По правилам описания конструкций языков программирования используе мые в описании заголовков процедур и функций квадратные скобки означают, что соответст вующий параметр может быть опущен.
Значение w, если оно указано, интерпретируется как длина строки, а значение d, если оно указано - как количество цифр дробной части для веще ственных чисел, например:
х:=-5.67;
Str(x:7:3,sl); {получим строку ' -5.670'}
Процедура обычно используется для формирования строк, включающих числовую информацию.
5. Процедура Val(St, х, Code) - преобразует строку St с записью числа в виде последовательности символов во внутреннее представление целого или вещественного числа и помещает его в переменную х. В целочисленной пе ременной Code процедура возвращает код ошибки: О, если преобразование прошло успешно, и номер ошибочного символа, если строка st не являлась допустимой формой записи числа.
116
4. Структурные типы данных
Процедура обычно используется, если необходимо предотвратить не корректный ввод чисел, например:
Var S:strmg; Code:mteger; a:real; ...
.„repeat
Write('Beedume число a:');
ReadLn(S); |
{вводим строку} |
Val(S,afCode); |
{пытаемся преобразовать строку в число} |
ifCodeoOthen |
|
WriteLn('Число введено не верно *); |
|
until Code-0;... |
{до получения правильного значения числа} |
6. Функция Copy(St,index,count):string - возвращает фрагмент строки St длиной count символов, начиная с символа с номером index, например:
S1 = 'qqqEEEEEEuuuuu';
S:= Copy(SlJ,6); {получим строку 'ЕЕЕЕЕЕ'}
7. Функция Pos(St2,Stl):integer - возвращает номер позиции первого вхождения подстроки St2 в строку Stl. Если вхождение не найдено, то функ ция возвращает О, например:
S] = 'qqqEEppEEuuuuu':
i:= Pos('EE\Sl); {получим i=4}
8. Функция UpCase(ch):char - возвращает символ, соответствующий символу верхнего регистра для ch, если таковой имеется, либо сам символ ch, если для него не определен символ верхнего регистра.
В качестве первого примера посмотрим, как будет выглядеть решение задачи из примера 4.3 с использованием строковых типов.
Пример 4.14. Дана строка не более 40 символов, состоящая из слов, раз деленных пробелами. Разработать программу удаления «лишних» пробелов. Лишними считать пробелы в начале строки до первого символа, второй и бо лее пробелы между словами и пробелы в конце строки.
При решении данной задачи с использованием строкового типа отпада ет необходимость посимвольного анализа строки. Функция Pos, которой в качестве подстроки заданы два пробела подряд, позволит определить все ме ста в строке, где записаны несколько пробелов подряд. Поочередно удалив лишние пробелы, получим строку, в которой останется только проверить и при необходимости удалить пробел в начале и пробел в конце (рис. 4.28). Ни же приведен текст программы.
Program ex;
Var st:string[40]; k:byte;
117
Часть I. Основы алгоритмизации и процедурное программирование
|
1. |
|
нет |
delete |
|
(St, 1,1) |
||
|
||
k:=length{st) |
|
|
:st[k] = 'J> |
1 |
|
нет |
delete |
|
(st,k,l) |
( Конец J
Рис. 4.28. Схема алгоритма программы удаления «лишних» пробелов
Begin
WriteLnCВведите строку длиной <= 40 символов');
ReadLn(st): |
|
|
|
|
Write(*Введенная строка:'); |
|
|||
WriteLn(^); |
|
|
|
|
k:-posC^^ \st); |
{проверяем, есть ли сдвоенные пробелы?} |
|||
while к<>0 do |
{пока есть сдвоенные пробелы} |
|||
begin |
|
|
|
|
delete(st,k,l); |
{удаляем первый пробел} |
|||
k:=posC^^ ^,st); {проверяем, есть ли сдвоенные пробелы?} |
||||
end; |
|
|
|
|
ifst[l]= |
^^^ then delete(stJJ); |
{удалили пробел в начале} |
||
Л:;= length(st); |
|
|
|
|
ifst[kj= |
^^^ then delete(st,kj); |
{удалили пробел в конце} |
||
WriteLn('Результат:'); |
|
|||
iflength(st.)<>0 then |
WnteLn(st) |
else WriteLn('Строка codepjfcana только пробелы.'); End
118
4. Структурные типы данных
Пример 4Л5. Разработать программу, которая вводит строки, содержа щие фамилию, имя, отчество и год рождения, а выводит - строки, содержа щие фамилию, инициалы и возраст на текущий год. Например:
Иванов Иван Иванович 1956 => Иванов И.И. 45
Завершение ввода - при чтении пустой строки.
Для выполнения операций над строками используем строковые функ ции. Обработку строк будем выполнять в цикле до ввода пустой строки. Нач нем с определения местоположения первого пробела, который отделяет имя от фамилии. Для этого используем функцию Pos, а результат запишем в пе ременную с1. Затем перепишем в строку результата фамилию, пробел и пер вый инициал. Туда же дописываем точ!^.
Для поиска следующего пробела придется копировать в рабочую строку часть исходной строки, начиная с символа после первой буквы имени. В этой строке вновь определяем местоположение пробела и заносим результат в пе ременную с2. Теперь можно переписать в строку-результат второй инициал.
Удаляем из рабочей строки начало, включая второй инициал, и вновь оп ределяем местоположение пробела, выделяя подстро!^, содержащую год рождения. Удаляем из рабочей строки остаток отчества и преобразуем стро ку в число. Полученное значение вычитаем из текущего номера года, а ре зультат вновь преобразуем в строку и дописываем к строке результата. Вы водим результат на экран и вводим следующую строку.
Несколько первых шагов преобразования показаны на рис. 4.29.
Program stroka;
Var st,strez,strab:string[40]:
cl, c2,c3,n, old,code:word;
cl
[T|
ИсходнаястрокаSt T""^ |
| и | в | а | н | |
| и | в | а [ н [)(| в | и | ч | |
| 1 | 9 | 5 | б | |
|
1И|в 1 a 1HI 01 в 1 |
||||
^ |
|
с2 |
|
|
|
|
|
|
|
Рабочая строка |
1 4 1 |
|
||
|
strab |
^ |
' ' |
|
|
| в | а | н | |
|
| и | в | а | н | () РвУи|чУ |
1 1 ] 9] 5 |Т] |
г |
1 1 |
|
""^^ |
|
Строка резулbxaxastrez |
j ^ |
|
|
|
| И | в | а | н | о | в | |
\Щ.\2кШЖЁШ^ t ] 1 1 1 1 1 II |
Рис. 4.29. Начало решения задачи преобразования строк
119
Часть I. Основы алгоритмизации и процедурное программирование
Begin
WriteLn('Введите строку. Завершение - ввод пустой строки.'); ReadLn(st);
while sto^^ do {цикл ввода, преобразования и вывода строк} begin
cl:=Pos(' ',^/у);{определим местоположение первого пробела} strez:=Copy(stJ,c] + lJ+ \ V {перепишем фамилию, инициал
strab:=Copy(st,cl+2,Length(st)-cJ-l); |
и добавим точку} |
|
{копируем остаток строки |
||
c2:=Pos(' \strab); |
|
в рабочее поле} |
{определяем местоположение второго пробела} |
||
strez:=strez+strabfc2+lj+ \ V {добавляем к результату второй |
||
Delete(strabJ,c2+l); |
|
инициал и точку} |
{удаляем распознанную часть} |
c3:=PosC \strab); {onpmQ^HQM местоположение третьего пробела}
Delete(strabJ,c3); |
{удаляем остаток имени} |
Val(strab,n,code); |
{преобразуем год рождения в число} |
old:=2001'n; |
{определяем возраст} |
Str(old,strab); |
{преобразуем возраст в строку} |
strez:=strez-^ ' '+strab; |
{добавляем возраст в результат} |
WriteLn(strez); |
{выводим результат} |
WriteLnCВведите строку. Завершениеввод пустой строки. '); ReadLn(st);
end;
End.
4.6.Практикум. Обработка и поиск символьной информации
Воснове обработки символьной информации, как правило, лежит разби ение текста на слова и выполнение некоторых операций со словами.
Пример 4.16. Разработать программу, которая определяет в строке коли чество слов длиннее четырех символов. Слова разделены одним пробелом.
Решение задачи будем осуществлять следующим образом. Вначале убе димся, что в конце строки есть пробел. Если пробела нет, тогда вставим его. Затем, пока длина строки не станет равной нулю, будем определять местопо ложение пробела и, соответственно, длину слова, которая на единицу мень ше номера пробела. Если длина слова больше четырех символов, то добавим единицу к счетчику слов. Затем удалим обработанное слово вместе с пробе лом и перейдем к обработке следующего слова.
Program strokal; Var St: string;
p, spos:integer;
120