Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
Считаем, наконец, слова
Строка состоит из слов, разделенных пробелами. Символы, из которых
состоят слова – не пробелы. То есть, запятая у нас не разделитель, а часть
слова. Впрочем, решение (когда мы его получим) тривиальным образом
обобщается на совершенно произвольный набор разделителей.
Сначала решение на пальцах. Обнуляем счетчик слов. Движемся по
строке от на
чала до конца. Как только начинается новое слов
о,
увеличиваем счетчик на единицу.
Очень хорошее решение, главное – абсолютно правильное. Или выглядит
таким. Осталось разъяснить не вполне ясные моменты, до полного
превращения их, неясных моментов, в моменты абсолютно ясные. Если
вдруг окажется, что какой-то неясный момент не хочет разъясняться,
значит, мы были неправы, и на
Это называется метод пошаговой детализации. Она же разработка сверху вниз.
до начинать сначала.
Неясный момент у нас ровно один – как понять, что началось новое
слово? Встретился символ, в смысле не пробел, а до того был пробел -
значит новое слово. Логично, но если перед первым словом нет ни одного
пробела, получается особый случай. А особый случай на то и особый, что
обрабатывать его приходится особо. Это плохо. Нет, не надо нам особых
случаев.
Введём логическую переменну
ю – есть слово или нет. Есть – значит,
слово встретили и движемся по нему. То есть, текущий символ, тот на
который указывает переменная цикла – не пробел. Тогда наша логическая
переменная имеет значение истина. А если движемся по пробелам,
переменная имеет значение ложь. Таким образом, есл
и переменная ложна
и встретился не пробел, то вот оно, новое слово.
Думаем дальше. Наша логическая переменная принимает два значения
,
как ей и положено. Наш текущий символ, по сути, тоже два – или пробел,
или не пробел. Подробности, какой именно не пробел, нам совершенно не
интересны. Итого имеем - два умножить на два - четыре сочетания.
Объявим переменные, напишем цикл
по строке и по условному оператору
на каждый вариант. А там поглядим.
var
s : string; {строка для разбора}
111

inWord : boolean; {признак – в слове или нет}
count : integer; {результат – количество слов}
i : integer; {это, понятно, переменная цикла}
……………………………………………
count:=0;
inWord:=false;
for i:=1 to Length(s) do begin
if inWord and (s[i]=’ ‘) then begin {в слове и пробел}
end;
if inWord and (s[i]<>’ ‘) then begin {в слове и не пробел}
end;
if not inWord and (s[i]=’ ‘) then begin {не в слове и пробел}
end;
t inWord and (s[i]<>’ ‘) then begin
if no
end;
end;
Теперь опять думаем. Как будет выполняться наша программа? Рано или
поздно встретится первый не пробел. Это ситуация номер четыре. Наша
реакция? Значит, началось новое слово – следовательно, увеличиваем
счётчик на единицу.
Слово началось, слово когда-то и кончится. То есть, мы нах
слова и налетели на пробел. Это ситуация номер один. Наша реакция –
установ
Смотрим, что получилось.
inWord:=false;
for i:=1 to Length(s) do begin
if inWord and (s[i]=’ ‘) then begin {в слове и пробел}
inWord:=false;
ить логическую переменную в ноль.
count:=0;
end;
if inWord and (s[i]<>’ ‘) then begin {в слове и не пробел}
end;
t inWord and (s[i]=’ ‘) then begin {не в
if no
end;
if not inWord and (s[i]<>’ ‘) then begin {не в слове и не пробел}
{не в слове и не пробел}
одимся внутри
слове и пробел}
112

inWord:=true;
count:=count + 1;
end;
end;
А дальше? А всё. Оставшиеся две ситуации нас не интересуют. А зачем
мы их вообще выделили? Во-первых, для наглядности. Во-вторых, сейчас
мы исходную задачу усложним.
Время от времени встречаются операции типа увеличить счетчик на единицу. На этот случай предусмотрен особый
оператор. Вместо count:=count + 1; можно написать Inc(count). Разумеется, предусмотрен и обратный вариант.
Dec(count) уменьшит нашу переменную на единицу.
А пока, оформите наш программный код в виде законченной программы и
протестируйте её.
Кстати, секций var может быть несколько. Только зачем?
А теперь усложняем. Вернее, доводим до практического применения.
Теперь наша задача – извлечь из строки слово с заданным номером. Если
нет такого слова – вернуть пустую строку. Несколько позже мы (Вы)
оформим (оформите) это в виде процедуры (функции).
Почти все переменные у нас уже есть, объявляем недостающее:
var
needCount : integer; {номер нужного слова}
theWord : string; {результат - то сам
ое слово}
Теперь надо как-то определи
ться с новыми переменными. theWord перед
циклом должна быть проинициализирована пустой строкой, а значение
needCount где-то перед этим уже введено пользователем программы.
Снова глядим на уже обработанные ситуации.
Номер четыре – началось новое слово. В дополнение к тому, что уже там
есть – проверить, а вдруг но
слов
а заказанного? Тогда текущий символ отправляем в результат.
мер начавшегося слова совпадает с номером
Номер один – пожалуй, ничего не изменилось. Кончилось слово, ну и
ладно.
Первый символ слова мы уже пристроили. А остальные? Ясно, что номер
третий совершенно ни к месту. А вот номер два подойдёт – если мы по-
113

прежнему в слове и номер слова тот, что надо, приклеиваем к результату
текущий символ.
count:=0;
inWord:=false;
theWord:=’’;
for i:=1 to Length(s) do begin
if inWord and (s[i]=’ ‘) then begin {в слове и пробел}
inWord:=false;
end;
if inWord and (s[i]<>’ ‘) then begin {в слове и не пробел}
if count = needCount
then theWord:=theWord + s[i];
end;
if not inWord and (s[i]=’ ‘) then begin {не в слове и пробел}
end;
t inWord and (s[i]<>’ ‘) then begin
if no
{не в слове и не пробел}
inWord:=true;
count:=count + 1;
if count = needCount
then theWord:=s[i];
end;
end;
Доделайте. Проверьте. Подумайте, как быть, если разделитель может быть
не один? Например, пробел и запятая? Никаких проблем?
А если разделители – пробел, запятая, точка, двоеточие, тире, точка с
запятой, восклицательный знак, во
просительный знак?
Увы, бананів немає.
114
TP. В чём разница

Глава 7, продолжение пятой.
Ещё циклы и массивы
Массивы двумерные и далее
Не знаю, что вам и сказать…
Массивы без циклов – вещь бесполезная, это я уже говорил, а двумерные
массивы без вложенных циклов – ну даже и не знаю, нечто бесполезное в
квадрате, и в высших степенях, в зависимости от размерности массива. Но
раз уж выбрал излагать в таком порядке – значит так тому и быть.
Три составляющие пр
присвоить и как вывести. Ну, ещё неплохо понять, что это такое и зачем
надо. Что такое и зачем целые и дробные переменные – понятно. Строки –
в целом тоже. А массивы, тем более двумерные?
Массивы, как уже упомянуто ранее, используются для хранения (и
обработки) большого количества однотипных данных. Пример из любого
учебника – если наша программа помнит и что-то делает с темп
воздуха сегодня, вчера и позавчера, достаточно объявить три переменные
типа real. Если надо обрабатывать температуру за месяц, то объявлять
тридцать одну переменную – ну неправильно это как-то.… Для этого
массивы есть – array[1..31] of real;.
А если
каждому дню, но ещё и по каждому часу дня? – оцените, как плавно и
ненавязчиво я подвёл разговор к теме двумерных массивов.
Объявляем двумерный массив – почти как одномерный.
var
deg : array[1..31,1..24] of real;
of real – это понятно, массив состои
квадратных скобках содержим
может меняться от 1 до 31 – это, как можно догадаться, дни. Второй, от 1
до 24, очевидно часы. А сколько всего элементов массива? Правильно, 31
умножить на 24 равняется 744.
Теперь присваиваем
deg[1,1]:=10.5;
deg[31,24]:=12.5;
всё ещё хуже и требуется хранить температуру не просто по
именения любых
ое удвоилось. Индексов стало два. Первый
данных – как объявить, как
ературой
т из дробных чисел. А вот в
115

Всё точно так же, как и с одномерным массивом, только работать
вручную ещё неудобнее – элементов в массиве намного больше.
С выводом разберитесь сами. Переходим к циклам.
Вложенные циклы
Напоминаем. Вот цикл:
for i:=1 to 3 do begin
Writeln(‘Au’);
end;
Внутри цикла может находиться любой оператор, или несколько
операторов. А раз любой – значит, этим оп
ератором может быть и
оператор цикла, внутри которого может быть и так далее. Называется это
– вложенный цикл. Тот цикл, что сн
аружи – внешний цикл, тот цикл, что
внутри – внутренний цикл.
На самом деле, не внутри оператора цикла, а внутри операторных скобок, – но какая разница…
Например, можно вот так:
for i:=1 to 3 do begin
for j:=1 to 3 do begin
Writeln(‘Au!’);
end;
end;
После запуска получим:
Au!
Au!
Au!
Au!
Au!
Au!
Au!
116

Au!
Au!
Очень важно! Во внешнем и внутреннем циклах переменные цикла
должны быть обязательно разные!
По традиции, это I и J.
Как нетрудно убедиться, наша строка выведена ровно девять раз.
Внешний цикл выполняется три раза, при каждом его выполнении три
раза выполняется внутренний цикл - три раза по три будет девять. А если
написать вот так:
for i:=1 to 9 do begin
Writeln(‘Au!’);
end;
получим абсолю
один? Совершенно верно, в
незачем.
Слегка подправим программу:
for i:=1 to 3 do begin
for j:=1 to 3 do begin
Writeln(‘Au!’);
end;
Writeln;
end;
Получим вот что:
Au!
Au!
Au!
Au!
Au!
тно тот же результат. Так зачем два цикла, если можно
данном конкретном случае абсолютно
117

Au!
Au!
Au!
Au!
Три группы по три строки, разделённые пустой строкой. Конечно, можно
было бы слегка извратиться и обойтись один циклом – где-то вот так:
for i:=1 to 9 do begin
Writeln(‘Au!’);
if (i mod 3) =0 then Writeln;
end;
Но лично мне кажется, что вариант с двумя циклами естественнее – но
допускаю, что я неправ. Тогда ещё чуть-чуть усложним – пусть в нач
каждой непустой строки стоит номе
for i:=1 to 3 do begin
for j:=1 to 3 do begin
Writeln(i,‘.Au!’);
end;
Writeln;
end;
На выходе:
1.Au!
1.Au!
1.Au!
2.Au!
2.Au!
2.Au!
3.Au!
3.Au!
р группы, к которой она относится.
але
118

3.Au!
Можно, конечно, ещё слегка извратиться и опять обойтись одним циклом,
но, по-моему, не стоит.
А теперь простенькая программка. Смысл очевиден – выводим значения
переменных цикла, смотрим, как они меняются и думаем.
for i:=1 to 3 do begin
for j:=1 to 3 do begin
Writeln(‘i=’,i, ‘ j= ‘,j);
end;
Writeln;
end;
i=1 j=1
i=1 j=2
i=1 j=3
i=2 j=1
i=2 j=2
i=2 j=3
i=3 j=1
i=3 j=2
i=3 j=3
Если Вам это совершенно очев
думаем…
А теперь пишем три вложенны
ими переменных и снова думаем, думаем…
По традиции, третью переменную цикла зовут K.
идно, то очень хорошо, иначе – думаем,
х цикла, выводим значения управляющих
Пример посложнее
Номер 121 из задачника Культина. Вывести таблицу умножения девять на
девять в виде квадрата, она же почему-то называется таблицей Пифагора.
119

Берём тетрадку в клеточку, переворачиваем обратной стороной и вот она,
таблица:
1 2 3 4 5 6 7 8 9
1 1 2 3 4 5 6 7 8 9
2 2 4 6 8 10 12 14 16 18
3 3 6 9 12 15 18 21 24 27
4 4 8 12 16 20 24 28 32 36
5 5 10 15 20 25 30 35 40 45
6 6 12 18 24 30 36 42 48 54
7 7 14 21 28 35 42 49 54 63
8 8 16 24 32 40 48 56 64 72
9 9 18 27 36 45 54 63 72 81
Я это чудо только что нарисовал ручками в Word, и мне это занятие не
понравилось. Попробуйте сами, чтобы оценить, насколько легче будет в
Паскале.
Конечно, те читатели, у кого есть за
есть ответы. В задачнике Культина они тоже есть.
путь. Наша цель не в том, чтобы получить решение (оно ведь в ответах
есть), а в том, чтобы понять, как до него, до решения, добраться наиболее
естественным путём. Ну, приступим.
Начнем с изготовления того, что внутри у таблицы, а строчку сверху и
столбец слева, обозначающие, чт
Первым делом, надо решить вопрос, как рисовать будем – по строкам или
по столбцам? Мне кажется, что лучше построчно, аргументировать даже
не буду.
Внешний цикл выполняется девять раз, и каждый раз при этом выводится
по одной строке, внутренний цикл выполняется девять раз и тоже при
этом каждый раз выводится по одном
равняется восемьдесят один, а в таблице чисел как раз столько. Всё
сходится.
Так что имеем:
for i:=1 to 9 do begin {цикл по строкам}
for j:=1 to 9 do begin {цикл по столбцам}
дачник, знают, что в конце задачника
Это, разумеется, не наш
о на что умножать, пририсуем после.
у числу. Проверка: девять на девять
120
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
