Паскаль для школьников_Ушаков Д.М, Юркова Т.А_2011 -320с.pdf

  • pdf
  • 29.04.2020
Публикация на сайте для учителей

Публикация педагогических разработок

Бесплатное участие. Свидетельство автора сразу.
Мгновенные 10 документов в портфолио.

Иконка файла материала Паскаль для школьников_Ушаков Д.М, Юркова Т.А_2011 -320с.pdf

Д. М. Ушаков, Т. А. Юркова

ПАСКАЛЬ


для школьников

2-е издание                            Улито '


 


Д. М. Ушаков, Т. А. Юркова

ПАСКАЛЬ

для школьников

2-е издание

7пптеро

Москва • Санкт-Петербург • Нижний Новгород • Воронеж Ростов-на-Дону • Екатеринбург • Самара • Новосибирск Киев • Харьков • Минск

201 1

ББК 32.973.2-018.1я7 удк оо4.4з(о75)

У9З

Д. Ушаков, Т. Юркова

У9З  Паскаль для школьников. 2-е изд. — СПб.: Питер, 2011 . — 320 с.:

ил.

lSBN 978-5-4237-0170-3

Эга книга — не учебник, а скорее помощник в освоении языка программирования Паскаль, с которым на уроках информатики знакомятся все школьники. Она состоит из уроков, посвященных практическим вопросам программирования и решения задач. Многочисленные примеры позволяют лучше понять, как разработать алгоритм, написать собственную программу, правильно оформить ее текст. Советы и примечания помогают читателю обратить внимание на важные детали, позволяя избежать подводных камней и более эффективно писать программы.

Книга подготовлена преподавателями информатики в школе, имеющими большой опыт многолетней практической работы.

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

ББК 32.973.2-018.1я7 удк 004.43(075)

Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.

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


ем книги.

lSBN 978-5-4237-01 70-3                                              О ООО Издательство «Питер», 2011

Содержание

Предисловие ко второму изданию

Вступление1 6

От издательства16

ТЕМА Л . Как написать простую программу на Паскале

Урок 1.1. Выводим сооощение на экран дисплея             

18

Урок 1.2. Как заложить эту программу в компьютер .                                                                                                      

19

                Этапы создания компьютерной программы .       

20

Урок 1.3. Оформление текста на экране .        

28

Выводы .                                                     

34

Контрольные вопросы .                                                                                  

34

ТЕМА 2. Как включить в работу числовые данные36

Урок 2.1. Начнем с простого: целые числа37

Понятие переменной .38

Тип Integer. Оператор присваивания. Вывод на экран38

Операции с типом Integer40

Стандартные функции типа Integer42

Как представляются переменные целого типа в памяти компьютера43 Урок 2.2. Включаем в работу вещественные числа45 Описание вещественного типа данных (real)45

Форматы записи вещественных переменных .46


Содержа ние

       Вещественные операции                                                

46

            Стандартные функции типа real    

47

            Запись математических выражений  

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

48

            в памяти компьютера                                                

Урсж 2.3. Как совместить переменные целого

50

        и вещественного типа     

51

            Преобразование типов  

51

           Правила приоритета в выполняемых действиях .    

52

               Действия над данными разных типов       

53

Урок 2.4. Ввод и вывод данных        

56

           Вводим переменные с клавиатуры .                                                                                          

57

            Красивый вывод на экран  

Задание значений переменных датчиком

57

             случайных чисел                                                             

61

Урок 2.5. Зачем нужны константы в программе?               

62

Выводы

64

Контрольные вопросы .                                                                                  

64

ТЕМА З. Учимся работать с символами66

Урок 3.1. Как компьютер понимает символы .                                                                                                       

67

        Кодовая таблица ASCII                                              

67

            Описание типа Char и стандартные функции    

68

Урок 3.2. Тип Char — порядковый тип!       

70

Выводы .                                                     

71

Контрольные вопросы .                                                                                  

72

ТЕМА 4. Джордж Буль и его логика73

Урок 4.1. Необходим еще один тип — логический!                                                                                                      

74

           Логический тип данных (Boolean)    

75

            Операции отношения .  

75

               Ввод-вывод булевских переменных        

76

Урок 4.2. Логические (булевские) операции                                                                                               

76

           Логическое умножение (конъюнкция)    

76

            Логическое сложение (дизъюнкция)    

77

          Исключающее ИЛИ (сложение по моду.лю 2)                                                                                                      

77

            Логическое отрицание (инверсия)    

78

Применение логических операций в программе78

Приоритет логических операций80

Выводы81

Контрольные вопросы .81

ТЕМА 5. Анализ ситуации и последовательность выполнения команд82

Урок 5.1. Проверка условия и ветвление в алгоритме .83

Полная и неполная форма оператора if84

Оформление программ86

Урок 5.2. Блоки операторов88

Урок 5.3. Ветвление по ряду условий (оператор case)92

Выводы96

Контрольные вопросы .96

ТЕМА 6. Многократно повторяющиеся действия98

Урок 6.1. Оператор цикла for99

Оператор for с последовательным увеличением счетчика 100 Оператор for с последовательным уменьшением счетчика 101

Урок 6.2. Применение циклов со счетчиком .101

Цикл в цикле .102

Трассировка103

Вычисление суммы ряда105

Выводы .108

Контрольные вопросы .109

ТЕМА 7. Циклы с условием1 1 0

Урок 7.1. Цикл с предусловием .111 Описание цикла с предусловием111

Приближенное вычисление суммы бесконечного ряда112

Возведение числа в указанную целую степень115

Урок 7.2. Цикл с постусловием .119 Описание цикла с постусловием120 Использование циклов repeat и while120

Относительность выбора операторов while и repeat123

.129

129

Содержа ние

ТЕМА 8. Массивы — структурированный тип данных

Урок 8.1. Хранение однотипных данных в виде таблицы132 Основные действия по работе с массивами133

Описание массива на языке Паскаль133

Заполнение массива случайными числами и вывод массива на экран134

Создание пользовательского типа данных137

Поиск максимального элемента массива

Вычисление суммы и количества элементов массива с заданными свойствами144 Урок 8.2. Поиск в массиве .

Определение наличия в массиве отрицательного элемента с использованием флажка

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

                 элементов путем вычисления их количества                                                                                                      

149

          Нахождение номера отрицательного элемента массива                                                                                                                   

150

Урок 8.3. Двумерные массивы .        

154

Выводы

156

Контрольные вопросы                                                                                

157

ТЕМА 9. Вспомогательные алгоритмы. Процедуры и функции. Структурное программирование1 58

Урок 9.1. Конструирование алгоритма «сверху вниз»           Практическая задача с использованием

159

                     вспомогательных алгоритмов       

Урок 9.2. Пример работы с функцией: Поиск максима.льного

160

    элемента

167

Выводы .

168

Контрольные вопросы .                                                                                

169

ТЕМА Л О. Как работать с символьными строками1 70

Урок 10.1. Работаем с цепочками символов: тип String171

Описание строковой переменной .171

Основные действия со строками172

Урок 10.2. Некоторые функции и процедуры Паскаля для работы со строками173 Использование библиотечных подпрограмм работы со строками173

Выводы175

Контрольные вопросы .175

ТЕМА Л Л . Процедуры и функции с параметрами1 76

Урок 11.1. Простые примеры использования подпрограмм

      с параметрами

177

           Простейшие процедуры с параметрами                                                                                       

177

            Формальные и фактические параметры

179

           Простейшие функции с параметрами

179

Урок 11.2. Способы передачи параметров     

181

Выводы   

183

Контрольные вопросы .                                                                               

184

ТЕМА Л 2. Файлы: сохраняем результаты работы до следующего разал 85

Урок 12.1. Как работать с текстовым файлом186

Открытие файла для чтения186

Открытие файла для записи .188 Урок 12.2. Сохранение двумерного массива чисел в текстовом файле . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

               Сохранение числовых данных в текстовом файле      

192

           Сохранение массива чисел в текстовом файле  

192

           Дописывание информации в конец файла  

196

Выводы .

197

Контрольные вопросы .                                                                               

197

Тема Л З. Графический режим работы. Модуль 6raph       1 99

Урок 13.1. Включаем графический режим работы .200

Особенности работы с графикой200

Переключение в графический режим видеоадаптера201 Урок 13.2. Продолжаем изучать возможности модуля Graph203 Рисование линий средствами модуля Graph203 Рисование окружностей средствами модуля Graph205 206

207


Содержание

Тема 1 4. Операторы, изменяющие естественный ход программы208

Урок 14.1. Использование оператора безусловного перехода goto210

Урок 14.2. Операторы, изменяющие ход выполнения цикла213

Оператор break213

Оператор continue214

Выводы .215

Контрольные вопросы215

Тема Л 5. Группируем данные: записи21 6

Урок 15.1. Описание типа данных record218

Урок 15.2. Когда и как разумно использовать записи220

Создание сооственного типа данных — запись220

Массив записей220

Оператор присоединения with221

Пример выбора структуры данных223

Записи записей224

Выводы225

Контрольные вопросы и задания .. 225

Тема Л 6. Динамические переменные226

Урок 16.1. Выделение памяти227

Урок 16.2. Адреса229

Урок 16.3. Указатели .230

Указатели на отдельные переменные230 Указатели на блоки переменных . 232

Урок 16.4. Динамическое выделение памяти .232

New и Dispose233

Динамическое выделение памяти для массивов235

GetMem и FreeMem .236

Обращение к элементам массива, созданного динамически237

Массив переменной длины . 238

241

242

Тема Л 7. Динамические структуры данных. Стек

Урок 17.1. Опишем тип данных245

Урок 17.2. Создание стека и основные операции со стеком247

Добавление элемента в стек (Push)248 Извлечение элемента из стека (Рор)251

Проверка стека на пустоту (StackIsEmpty)252

Урок 17.3. Использование стека253

Программирование стека при помощи массива255

Выводы256 Контрольные вопросы и задания256

Тема Л 8. Динамические структуры данных.

258

Урок 18.1. Принцип работы и описание типа данных259 Урок 18.2. Основные операции с очередью261 Добавление элемента в очередь (EnQueue)261 Извлечение элемента из очереди (DeQueue)263

Проверка очереди на пустоту (QueueIsEmpty)264

Урок 18.3. Использование очереди264

Программирование очереди при помощи массива267

Выводы269 Контрольные вопросы .269

Тема Л 9. Динамические структуры данных.

Однонаправленный список270

Урок 19.1. Описание типа данных и принцип работы271 Урок 19.2. Основные операции с однонаправленным списком272 Последовательный просмотр всех элементов списка272

Помещение элемента в список273 Удаление элемента из списка275

Урок 19.3. Обработка списков .276

Целесообразность использования однонаправленного списка278 .280

280

Содержание

Тема 20. Рекурсия                       

281

     Урок 20.1. Описание принципа   

 282

        Урок 20.2. Ханойские башни      

 285

         Урок 20.3. Структура рекуррентной подпрограммы       

Урок 20.4. Пример рекуррентного решения нерекуррентной

 287

         задачи  

Урок 20.5. Пример рекуррентного решения рекуррентной

 288

         задачи  

 289

    Выводы  

 291

   Контрольные вопросы .                         

 291

Приложение Л . Элементы блок-схем                      

292

Приложение 2. Задачи                         

295

     Integer. Описание. Ввод. Вывод. Операции   

 296

    Real. Описание. Ввод. Вывод. Операции и функции                                                         

 296

     Real. Запись и вычисление выражений   

 297

     Char. Описание. Ввод. Вывод. Функции   

 298

      Boolean. Запись выражений   

 298

     Boolean. Вычисление выражений .   

 299

      If. Простые сравнения.                                                                                  

 зоо

         If. Уравнения и неравенства с параметрами      

 зоо

   For. Перечисления .                                                            

 зоо

     For. Вычисления со счетчиком цикла .                                                                                              

 301

      For. Перебор со сравнениями   

 302

      While-Repeat. Поиск   

 302

       While-Repeat. Ряды     

 303

   Графика. Прямые .                                                              

 зоз

   Графика. Окружности                                                           

 304

     Массивы. Заполнение, вывод, сумма/количество   

 305

      Массивы. Перестановки     

 305

    Массивы. Поиск .                                                              

 306

       Массивы. Проверки   

 307

      Массивы. Максимумы .     

 307

Подпрограммы без параметров .                                                    

 307

   Строки. Часть I .                                                              

 308

      Строки. Часть II   

 309


Подпрограммы с параметрами. Часть I  309 Подпрограммы с параметрами. часть II  310 Подпрограммы с параметрами. часть III  310

Файлы  311

Однонаправленный список  312 Рекурсия  313

Предисловие ко второму изданию

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

В при.ложении мы решили отказаться от сборника домашшлх заданий с множеством вариантов по нескольким темам. Вместо этого мы поместили в приложение большое число тематических заданий, организованных блоками по 5—8 задач. Задания в каждом блоке распо.ложены от простого к сложному. Мы испо.льзуем их на наших уроках д.ля организации практических занятий при закреп.лении теоретического материала (одно занятие — один б.лок).

Авторы выражают глубочайшую признательность одному из „лучших своих учеников, доценту кафедры безопасности информационных систем СПбГУАП, к. т. н. Евгению Михай.ловичу Линскому за поддержку, множество полезных советов бо.льшую помощь при работе над вторым изданием книги.

Вступление

Что такое язык программирования? Любая задача, которую решает компьютер, записывается в виде последовате.льности команд. Такая пос.ледовательность называется программой. Команды, конечно, до.лжны быть представлены на языке, понятном компьютеру. Один из таких языков — язык программирования Паска.ль. Он разработан швейцарским профессором Нико.лаусом Виртом специально д.ля обучения студентов программированию. К особенностям языка относится также и его структурность. То есть программа легко разбивается на более простые, непересекающиеся б.локи, те, в свою очередь, на еще более простые б.локи. Это также об.легчает программирование. В 1979 году язык бы.л утвержден в качестве стандартного. Вирт назвал его в честь французского ученого Б.леза Паскаля, изобретателя счетной машины. Язык Паскаль прост, логичен и эффективен. Он по.лучи.л распространение во всем мире. Материал книги построен на конкретных примерах программ. Длительных теоретических пояснений нет, поэтому крайне необходимо внимательно читать комментарии в текстах программ!

Итак, начинаем обучение сразу с написания первой программы на Паскале.

Желаем удачи!

От издательства

Ваши замечания, предложения, вопросы отправ.ляйте по адресу

э.лектронной почты comp@piter.com (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

Все фай.лы, описываемые в книге, вы сможете найти по адресу http:/www.piter.com/down[oad.

Подробную информацию о наших книгах вы найдете на веб сайте издате.льства http:/www.piter.com

ТЕМА 1

Как написать простую программу на Паскале

Изучение Паскаля мы начнем сразу с конкретных примеров, которые помогут вам почувствовать процесс программирования вживую. Мы проведем вас шаг за шагом через все стадии программирования. Мы хотим, чтобы вы сразу ощутили уверенность, чтооы вы поняли: «Если я смог написать одну программу, значит, смогу и вторую!»

Урок 1 . 1 . Выводим сообщение на экран дисплея

Нашей первой программой будет программа-приветствие. Она просто выведет текст на экран компьютера и завершит свою работу. В этом уроке мы рассмотрим основные прави.ла оформ.леш,ш программы.

СОВЕТ

ДЗнитатепьно читайте комментарии, они указываются в фтурных скобках { }

Пример 1.1. Первая программа program Fjrst: { Первая строка заголовок программы


рродрат — служебное слово:

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

Дальше идет тело программы.

оно всегда начинается со слова begjn } begj п { Здесь в конце строки нет


1

точки с запятой.

Следующая команда, или оператор, выводит слово ПРИВЕТ на экран:

после вывода курсор остается на той же строке в конце текста:

текст для вывода всегда заключается в апострофы. } wrjte ( [Привет, { В конце строки обязательна точка с запятой}

{ Следующий оператор выведет на экран слово ДРУЗЬЯ! и переведет курсор на следующую строку, так как символы в операторе wrjte1n означают

”1Јпе” по-английски ”строка” } wrjte1n (' друзья!

wrjte1n (' это вторая строка ' ) { Здесь в конце строки не обязательна точка с запятой, так как это последний оператор.

Проще говоря, перед end точку с запятой можно не ставить }

{ словом end кончается тело программы:

в конце обязательно стоит точка }

ЗАМЕЧАНИЕ

Очень важно понимать, когда нужно ставить точку с запяпюй, а кмЭа нет. Основное правипо — точка с запятой Эопжна ставиться при перечиспении инструкдий. атак, в рассмотренном топько что притере точка с запяпюй стоит тсжЭу разделом рродрат и разЭспот тепа програтты, а также в тепе протамты при перечиспеншм операторов. На поспеЭнем операторе wrjte1n перечиспение заканчивается, поэтому ты не поставипи точку с запятой.

Урок 1 .2. Как заложить эту программу в компьютер

В этом уроке мы рассмотрим и проделаем все действия, необходимые д.ля работы в среде Turbo Pascal. Мы условно разде.ли.ли их на 7 шагов. Может быть, такой длинный процесс покажется вам утомите.льным. Ну, что ж поделаешь — тяжело в учении „легко в бою.

Обнадежьте себя тем, что эти 7 шагов приведут вас к первому конкретному результату. Это начало большого пути!

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

Этапы создания компьютерной программы

+ 1 этап. Редактирование текста программы (команда Edit). На этом этапе пользователь набирает свою программу в символах выбранного им языка программирования.

+ 2 этап. Компиляция программы (команда Compi[e). В результате программа пользователя переводится из символов языка программирования в двоичный код компьютера. При обнаружении ошибок происходит возврат к 1 этапу.

+ З этап. Построение программы (команда Build). Подгружаются библиотечные модули: , и готовая программа в двоичном коде сохраняется на диске. При обнаружении ошибок происходит возврат к 1 этапу.

+ этап. Запуск программы на выполнение (команда Run). При обнаружении ошибок происходит возврат к 1 этапу.

Прохождение всех этих этапов заложено в интегрированной среде Turbo Pascal.

ЗАМЕЧАНИЕ

Точпш пюбая компьютерная программа состоит из бпоков (тоЭупей), каждый которых выпопняст какое-то оЭно Эействне. Программу сосптвпяют из этих бпоков, как из кирпичиков. 9-й теме ты НС7УЧИТСЯ создавать пткие бпокн самостоятельно. Однако гаже в CC7Wll,lX простых спучаях прихоЭится выпопнять Эсйствия, без которых не обходится почпш ни одна программа например выводить информадию на экран вводить что-то с кпавиатуры. атак как эти Эсйствия НУЖНЫ всели, Эпя них ввели спедиапьныс названия (например, wrjte и wrjte1 п) и запожипи непосредственно внутрь программы %rbo fasca/. Они-то и называются Обпиотечныти тоЭупяти. Их машинные коды хранятся в спајиапьных файпах (ббпиотеках и подсоединяются к вашим программам на этапе построения.

 О том, что такое оиолиотечные модули, см. замечание далее по тексту.

Л .

Рис. 1 . 1 . Схема прохождения этапов создания программы на компьютере

Вам пред.лагается выполнить последовате.льно с.ледующие действия.

1 . Запуск среды Turbo PascaI

 Щелкните на значке Turbo Pascal, если он существует на вашем Рабочем столе, или найдите на диске файл turbo.exe или bp.exe (исполняемые файлы с программой среды Turbo Pascal) с помощью функции поиска Windows (Пуск » Найти).

При успешном запуске интегрированной среды Turbo Pascal вы увидите на экране с.ледующие элементы: + г.лавное меню (строка сверху);

+ окно редактирования (синее поле посередине);

+ описания функциональных клавиш (строка внизу).

ЗАПОМНИТЕ!

Д<павиша Р 10 вызывает ыавнос меню (можно также [ДСЛКНУПIЬ

Д<давиша Esc осунјсствпяст возврат из тобою вызванного режима к предыЭунјему

СОВЕТ

Эти советы касаются пользователей c%rbo fasca/ Эля [Ui'1dows.

Скорее всего, вы запускаетс c%rbo fasca/ [l%1dows, создав Эпя этого ярпык (нап [ltue , на габочем стопе). Однако furbo fasca/ — програтта эм WS%O' в работы с ней вы спюпкнетесь с НЕСКОПЬКИТИ особенностями.

(Во-первых, программы Эпя [4'1S-OOS обычно запускаются в попноэкраннот текстовом режиме. Это кажется нам неудобным хопи бы потому, чию часпюта терзания монитора в этот режиме составляет всего 60 в то время как Эпя безопасности маз считается нсоблоЭитыт хотя бы 85 Обычно тафичсский режим Wi'1dows настроен на такситапьную частоту обновпсния. Шы рекомендуем ват перейти поспе запуска програтты fltrbo fasca/ в оконный режим работы (комбинадия кпав,'ни Alt+Enter) развернуть окно на весь экран.

ДЗо-вторыл, перекпючатспь кпавиатуры Эпя [И5-ОО5-програтт не итест никакою отношения к обычноту перекпючатспю клавиатуры [Uindows.

Л

Запомните: в furbo fasca/ псрскпючснис на русскую раскпаЭку кпавиатуры производится нажатием правою Ctr[+Shift, а обратно на патинскую — на жатием певою Ctr[+Shift      этом индикатор клавиатуры на панспи задач

— нс об андайтс на нао внимания. Обычно при перекпючении

эм WS%OS звуковой синап.

ДЗ-трстьих, окно протаммы Эпя J'1S-OOS крайне нс рекомендуется закрывать нјспчком на кнопке закрытия окна lVi'1dows. Это Действие привсЭст к аварийному принуЭитспьному закрытию программы furbo fasca[ и, скорее всего, к потере несох аненной программы. Закрывать furbo fasca[ нужно  либо — с помощью команды Exit в (исню File комбинадшм кпавнш Alt+X на кпавиатурс.

2. Работа в окне редактирования Edit

 Перейдите в главное меню (клавиша F10).

 Выберите пункт File с помощью клавиш перемещения курсора

 Нажмите клавишу Enter.

Появится раскрывающееся меню Fi[e:

Ореп save Save as

Save а] ]

Change djr prj nt

Prjnter setup

DOS

Exjt

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

• Выберите команду New с помощью клавиш перемещения курсора

ч)

 Нажмите клавишу Enter.

На экране откроется пустое окно, озаглавленное NONAM Е00. PAS. Это имя, данное средой по умолчанию вашей будущей программе (точнее, файлу, в котором будет храниться ваша будущая программа). Если вы повторите описанную выше операцию, то раскроется


программу

еще одно окно, но уже с именем NONAME01.PAS. Так можно раскрыть достаточное число окон редактирования. Для перек.лючения окон достаточно, удерживая нажатой клавишу A[t, нажать к.лавишу с цифрой, представляющей порядковый номер окна. Эту операцию также выпо.лняет клавиша F6.

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

Пример 1 .2. Первая программа без комментариев program Fjrst:

ЬедЈ п wrjte( ' Привет , wrjte1n( ' друзья!

wrjte1n( ' это вторая строка

 

 

9

СОВЕТ

Котпипятору языка furbo fasca/ безразлично, какие буквы вы испопьзуете при наборе протамты.• строчные ипи замавные. наших примерах ват преЭпатется станЭартный вариант использования реимстра ситвопов в протамте.

Три написании текста протамты вы можете свобоЭно во1твпять пробелы и перевоЭ строки тсэюЭу независимыми операторами. Но при этот позаботьтесь о тот, чтобы вашу программу быпо пако читать.

При работе в редакторе полезно знать основные комоинации

к.лавиш (табл. 1.1).

Таблица 1.1. Комбинации клавиш редактора Turbo Pascal

Действие

Клавиша

Образование новой строки

Enter

Переход в начало строки

Ноте

Переход в конец строки

End

1

Действие

Клавиша

Переход в начало файла

Ctrl+Home

Переход в конец файла

Ctrl+End

Включение режима замены символов

Insert (Ins)

Включение режима вставки символов

Insert (Ins)

Удаление текущего символа

Delete (

Удаление предыдущего символа

Backspace (Bs) — правая клавиша в ряду цифровых клавиш

Перемещение курсора по тексту

Страница вверх

Раде Up (PgUp)

Страница вниз

Раде Dn (PgDn)

Склеивание двух строк

1. Переход в конец 1-й строки

2. Клавиша De[ete

Удаление текущей строки

Ctr[+Y

ЗАМЕЧАНИЯ

Кпавишати управпения (PgUp, PgDn, Ins, и т. Э.) на вспототтельной дифровой кпавиатуре можно попьзовапњся при выкпюченнот режиме Num Lock.

ДЗ гпавнот теню есть пункт Edit. Таскрываюнјееся теню, которое появпяется при выборе этою пункта, позвопяет выпопняпњ различные операјии с 111€кстот.

ДЗыЭепипљ фраьиент текста можно с помощью кпавиш уравнения курсо рот, Держа нажатой кпавшиу Shift, ипи путем перетаскивания тьнии по выЭепяемоту фрапиенту.

С выделенным  можно работать почти так же, как в [Ипdows — уЭапять, вырезать, копировать, вставпяпњ.

ДЗ fltrbo fasca/, как и в [l%1dows, есть буфер обмена, куга можно скопировать выЭеленный фрапиент, а потом вставить из нао в Эругое тесто. Причем вставпять [5 буфера можно нескопько раз. Сочетания кпавши Эпя работы с буфером обмена испопьзуются не совсем привычные Эпя [Uindows (табп. .2).

Таблица 1 .2. Комбинации клавиш для работы с буфером обмена среды Turbo Pascal

Действие

Комбинация клавиш

Удалить выделенный фрагмент

Вырезать в буфер (Cut)

Shift+De[ete

Копировать в буфер (Сору)

Ctr[+Insert

Вставить из буфера (Paste)

Shift+Insert

Задание 1.1. Напишите (в редакторе Turbo Pascal) программу, которая выводит на экран фразу «Всем привет! » 80 раз — в таблице из 20 строк по 4 столбца.

Подсказка. Для задания расстояния между колонками используйте несколько пробелов. Напишите сначала только один оператор wrjte, который выведет одну фразу (не забудьте про пробелы). Затем скопируйте его еще З раза, чтобы получить целую строку. В конце не забудьте поставить переход на следующую строку (wrj te] п). После этого скопируйте получившийся текст еще 19 раз. Копировать begin и end не нужно!

З. Сохранение программы в файле на диске

 В главном меню выберите Fi[e (клавиши Alt+F).

 В раскрывающемся меню Fi[e выберите команду Save as (Turbo Pascal откроет окно Save Fi[e As.)

 Переключайтесь между элементами окна клавишей ТаЬ (или щелчком мыши).

 В поле ввода Save as введите имя. под которым собираетесь сохранить файл. например MY.PAS. и нажмите Enter (расширение .pas набирать не обязательно).

ЗАМЕЧАНИЯ

ДЗ попс Fi[es отображаются имена файпов тскунјао катапот в соответствии с таской, установпснной в попе Save as. Когда вы выберете НУЖНЫЙ файп в попе Fi[es и нажтстс Enter, то ао имя автоматически появится в попе ввоЭа Save as. Кнопка ОК спужшн Эпя подтверждения выбранных Действий. Кнопка Сапсе[ оттеняет все Эсйствия и закрывает Эиапмовое окно. Кнопка Неф выводит окно с поЭсказкой. Эапьнейшем

1

при сохранении файпа поэ текущим именем Эостаточно нажатия кпавиши F2 (save). атак как дтаскапь — при амма [1/1S-OOS, то и сохранять файпы рекотенЭуется поэ именами hs-oos.  см имя файла составпять не бопее чем 8-ми патинских букв и дифр, без прооепов и знаков препинания.

4. Запуск компилятора

 Выберите меню Compi[e (A[t+C).

 Выберите в окне режима Compi[e операцию Compi[e.

 Нажмите клавишу Enter.

Если компилятор не обнаружил ошибок, то на экране появляется окно с сообщением: Compi[ation successfu[: press апу Кеу («Компиляция прошла успешно: нажмите любую клавишу»).

Окно остается на экране до тех пор, пока вы не нажмете какуюлибо клавишу. Если обнаружена ошибка, курсор устанавливается на ошибку в окне редактирования и выдается сооощение оо ошиоке. Получить подробную информацию о найденной ошибке можно, нажав сразу клавишу F1. Esc — возврат в окно редактирования.

ЗАМЕЧАНИЕ

Справку по языку Таскапь тожно поп чипњ, спи установипљ курсор с потонјыо кпавиатуры на спужебное спово аскапя и нажали, котоинтјию кпавиш CtrL+F1.

СОВЕТ быстро запустить продесс компипяјшм можно нажатием кпавиш

5.   Выполнение программы

Объединяем работу построителя (команда Bui[d) и запуск нашей программы на выполнение.

: В главном меню выберите Run (A[t+R).

 В раскрывающемся меню Run выберите команду Run.  Нажмите клавишу Enter.


ЗАМЕЧАНИЯ быстро запустить программу можно нажатием котбннтуш кпаCtr[+F9

%РОДСССЫ котпипяјшм и запуска программы на выпопнснис можно объединить, вызвав котанЭу Run сразу поспс набора текста протатты.

6.   Просмотр результатов работы программы

 Одновременно нажмите клавиши A[t+F5 для перехода к экрану пользователя.

 Просмотрите результаты работы программы.

 Для возвращения в среду Turbo Pascal нажмите любую клавишу.

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

7.   Выход из среды Turbo Pascal

 Нажмите Alt+X.

Самый простой и быстрый способ выйти из среды Turbo Pascal — нажать комбинацию клавиш Att+X. Если ваша программа при этом не была сохранена, появится сообщение о том, что файл был изменен, и предложение его сохранить. Другой способ выйти из Turbo Pascal — выбрать в меню File команду Exit. Напоминаем, что щелчок мышью на «крестике» в правом верхнем углу окна Turbo Pascal в системе Windows является не выходом, а аварийным завершением работы. При этом набранная вами программа будет, скорее всего, потеряна. Задание 1.2. Напишите программу, которая выводит на экран текст:

Важно не путать \Arjte и 1Arjte1n!

Урок 1 .З. Оформление текста на экране

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

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

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

Рассмотрим принцип работы с модулем Crt и его основные

процеду ры.

Как мы уже говори.ли, Turbo Pascal работает в текстовом режиме. Это означает, что информация на экран выводится в виде симво.лов, каждый из которых отображается на экране в определенной позиции, как бы в клеточке. Экран при этом можно себе представить как таблицу из 25 строк и 80 сто.лбцов (рис. 1.2). Каждая ячейка этой таблицы имеет 2 координаты — х и у, где х — номер сто.лбца, у — номер строки. Строки нумеруются сверху вниз, начиная с единицы до 25, столбцы — слева направо, с 1-го до 80-го. То есть левый верхний угол экрана имеет координаты (1,1), правый верхний — (80,1), а левый нижний — (1,25). Символы можно выводить на экран 16 различными цветами, которые кодируются числами от 0 до 15. Каждому коду соответствует свой цвет. Полную таблицу кодов можно получить, набрав, например, слово red в окне редактора Turbo Pascal и нажр ав Ctrl+F1 на клавиатуре.

          1                                                                                   80

(1,1)

(1 ,25)

(80,1)

х

1

25

Рис. 1 .2. Схема нумерации позиций экрана для модуля CRT

зо

Внимате.льно разберите следующую программу:

Пример 1 . З. Использование модуля Crt program Second:

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

Она начинается словом uses.

Затем через запятую перечисляются подключаемые модули Заканчивается список символом uses Crt: { Crt - Имя подключаемого модуля

Блок заканчивается символом { Начало основной программы } begj п

              Text3ackGround (3) :            Вызов процекуры для выбора


фонового цвета. В скобках указан номер выбранного цвета В данном случае ” 3” означает светло-голубой цвет Вместо номера можно написать название цвета: ЬЛасК , red, green, Ь] ие, magenta , суап,

СЛ rScr;

{ Процекура очистки экрана Указав цвет фона до команды C]rScr, мы тем самым залили экран светло- голубым цветом }

TextC010r (14) :

{ Процекура выбора цвета

выдаваемых символов . 3 скобках указан номер выбранного цвета .

3 данном случае желтый цвет Обратите внимание:

команда TextC010r не меняет цвет символов , уже имеющихся на экране! она лишь устанавливает цвет, которым будут выведены следующие символы }

GoToXY (40,10): { Процедура установки курсора в точку экрана с координатами Х 40, У 10 }

Wrjte1n(' Все отлично! ! ! •

{ Вывод текста в 10 строку, начиная с позиции 40 }

(1000) { Процедура временной задержки на 1000 мкс }

{ На современных компьютерах De1 ау(1) обычно работает быстрее, чем 1/1000 секунды Поэтому задержка в данном случае будет меньше секунды }

{ Конец программы }

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

ЗАМЕЧАНИЕ

ПрежЭс чем писать протамму на языке программирования, стоит описать задачу сповати поштово — то есть приЭутапњ аторитт заЭачи. Иторитт тожно представить в виЭс бпок-аемы рис. З). Основные бпоки, чаще всею испопьзусмыс в таких схемах, ст. в припожею,ш

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

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

Сейчас ваша задача — представить каждый шаг (б.лок) а.лгоритма на языке Паскаль и оформить программу по образцу примера 1 .З.

Задание 1.4. Написать программу, которая очищает экран и выводит слова red, green, blue, каждое своим цветом в центр четвертей экрана (если экран условно разбить на 4 части, как показано на рисунке):

32

Задание 1.5. Левый столбец таблицы содержит действия, которые выполняет некоторый оператор. Правый столбец содержит операторы языка Паскаль. Поставьте в соответствие элементам из левого столбца таблицы элементы из правого столбца.

1. Очистка экрана

А. Crt ;

2. Позиционирование курсора в левый нижний угол экрана

В. TextBackGround( red) , СЛ rScr;

З. Заказ красного цвета фона экрана

С. wrjte( • Нарру New Year' ) ;

4.                   Заливка экрана красным цветом

5.                   Вывод в текущую позицию экрана ' Нарру New Year' с переходом курсора на новую строку

Е. TextCo] red) ;

6. Позиционирование курсора в правый верхний угол экрана

Е TextC010r(12) ; 1ArjteСНеЛЛо' ) ;

7. Установка красного цвета текста

С]. GoToXY(1.23) ;

8. Вывод в текущую позицию экрана ' Нарру New Year' без перехода курсора на новую строку

Н. lArj te1 n ( ' Ha ppy

Уеар' ) :

9. Библиотека среды ТигЬо Pascal для раооты в текстовом режиме

I. ВедЈп end

10. Начало и конец тела программы

ј. TextBackGround(red) :

11. Вывод в центр экрана 'Hello'

К. C Л rScr:

12. Вывод текста 'Hello' цветом N2 12

 Nrj te-

('НеЛЛо' ),

Ответ: 1-К, 2-G, з-ј, 4-В, 5-Н, 6-D, 7-Е, 8-С, 9-А, 10-1, 1 1-L, 12-F.

Задать цвет экрана

 

 

Очистить экран

 

 

Задать цвет символов 1-го сообщения

 

 

Позиционировать курсор в левый верхний угол

 

 

Вывод 1-го сообщения

 

 

 

Временная задержка

 

 

Задать цвет символов 2-го сообщения

 

 

Позиционировать дрсор в правый нижний угол

 

 

Вывод 2-го сообщения

 

 

 

Временная задержка

Рис. 1 .3. Алгоритм вывода цветных сообщений в левом верхнем и правом нижнем углах экран

34

ЗАМЕЧАНИЕ возможно, вы обратипи внимание, что ты иногЭа испопьзуем тертин «Таскапь», а в Эрушх местах «frubo fasca/». Этти ты обозначаем разнију тсэюЭу яЗыком программирования ДТаскапь и средой протаттирования c%rbo fasca/.

Выводы

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

2.     В структуре программы на языке программирования Паскаль обязательно присутствует тело программы. Его формируют операторы Begin и End. Между Begin и End с помощью другјлх операторов задаются определенные действия.

З. Вывод информации на экран осуществляют операторы write и writeln.

4.     При выпо.лнении некоторых действий в ТитЬо-среде используются биб.лиотечные модули языка Паскаль. Имена этих модулей объявляются в разделе uses.

5.     Д.ля красивого вывода на экран используется моду.ль Crt. Он позволяет очищать экран (C[rScr), менять позицию курсора (GotoXY), а также цвет символов (TextCo[or) и фона (TextBack

Ground).

Контрольные вопросы

1.     Какими      начинается и заканчивается те.ло любой программы на Паскале?

2.     Из каких этапов состоит процесс создания компьютерной программы?

З. Как запустить Turbo Pascal? Как выйти из среды Turbo Pascal? Как запустить программу в среде Turbo Pascal?

4.     Какие действия совершают операторы wrjte и wrjte1 п? В чем состоит разница между ними?


Контрольные вопросы

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

6.     Как просмотреть результат работы программы?

7.     Что такое текстовый режим работы? Опишите правило задаю,ш координат определенной точки экрана.

8.     Какой модуль позволяет выводить информацию на экран красиво и в цвете? Как установить курсор в нужную позицию экрана?

9.     Что такое линейный алгоритм?

ТЕМА 2

Как включить в работу числовые данные

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

Урок 2.1 . Начнем с простого: целые числа

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

Память

Значения

 

15

8

 

 

 

Ячейки

памяти

 00153 переменных

00154

00155

00156 00158

Имена переменных Адреса

Рис. 2.1 . Хранение переменных в памяти компьютера


11онятие переменной

Данные нашей программы принято называть величинами. Величины, которые меняются, называют переменными, а те, которые не меняются — постоянными.

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

ЗАПОМНИТЕ!

Идентификатор (имя) всаЭа Эопжсн начинаться с папшнской буквы, поспе которой тожст спсЭовать нскопюрос чиспо папшнских букв, дифр ситвопов подчеркивания (—). [Кисни нс Эопжно быть п обедов, Зш1яп1ых

Эругих непреЭусмотрснных знаков. furbo fasca/ учитываются пишь первые 63 симвопа.

Тип lnteger. Оператор присваивания.

Вывод на экран

Теперь рассмотрим работу с самыми простыми переменными. В них можно хранить только целые числа. Для хранения целых чисел в Паскале используется специальный тип данных — Integer.

Внимательно читайте комментарии к программе в следующем примере!

Пример 2.1. Работа с целочисленными переменными Program Product:

{ далее идет раздел описания переменных. он всегда начинается со слова var (от varjab1e переменная) var

А,В,С: jnteger; { Имена в списке через запятую, в конце списка через двоеточие

указывается тип данных:

                                                      Ј ntegep        целый }

      ведЈ п                                    Началось тело программы }

 Это оператор присваивания . В данном случае запись означает, что в переменную ( А записали число 5.

                                                      Не путайте с записью А=5 ! ! !    

Выводим на экран содержимое переменной А.

Имя А не заключено в апострофы !

Вывод на экран символа А }

 Запишем в переменную А число, которое до этого в ней было, но увеличенное на 1 }

 * — это операция умножения }

wrj                                               Product= 'Вывод содержимого ячейки C с пояснительным текстом }

{ Здесь кончается тело программы

При запуске программа выведет на экран следующее:

5

Product=42

ЗАПОМНИТЕ!

резупьтатс выпопнсния оператора присваивания в ячейку потешается новое чиспо. Старос соЭсржимос ячейки при этот пропадает.

Справа от оператора присваивания может стоять чис.ло и.ли

„любое выражение. Слева может стоять только имя переменной. Выражения слева быть не может — иначе Паскаль не будет знать, в какую ячейку памяти поместить результат.

Тип резу.льтата выражения справа от оператора присваивания до.лжен быть таким, чтобы помещаться в переменную с.лева

ОТ

Операции с типом lnteger

Рассмотрим операции, которые можно выполнять с це.лыми чис„лами и це.лочисленными переменными.

Пример 2.2. Операции с переменными целого типа Program Actjon:

var jntegep:

ЬедЈ п

я умножения: wpjte1n( ' 17

 Деление нацело: } с djv В: wpjte1n('17 djv З ' ,C) :

Вычисление остаткаДеГЕНИЯ :

с =А mod В: wpjte1n('17 mod З ' ,C) :

СЛОжение• wpjte1n( ' 17 + 3

Вычитание• wpjte1n( ' 17 - 3

При запуске программа выведет на экран следующее:

17 * 3=51

17 djv 3 5

17 mod 3 2

17 + 3=20

¯ 3=14

Рассмотрим еще несколько примеров операций div и mod. Д.ля успешного понимания результатов этих операций нужно вспомнить 2-й класс и деление столбиком (рис. 2.2).

5

4

    23                    23 div

 20                     23 mod

Рис. 2.2. Пример целочисленного деления столбиком

Частая ошибка: не забудьте, что все действия мы производим то.лько с це.лыми числами! Не нужно продолжать де.ление, когда де.лимое (это то, что мы делим) оказывается меньше де.лите.ля (это то, на что мы делим). То, что осталось от делимого, называется остатком. Это и есть результат операции mod. Целое которое по.лучи.лось в результате деления, называется целочисленным частным. Это результат операции div.

Проверим себя, вспомнив 2-й класс:

5      div 2=2; 5 mod 2-1;

6      div 2=3; 6 mod 2=0; 40 div 6=5; 40 mod 6=4; З div 5=0; З mod 5-3.

Резу.льтат вычисления операций div и mod для отрицате.льных чисе.л оказывается не совсем таким, как положено в математике (когда остаток всегда неотрицателен). Зато он бо.лее понятен. Другими словами, результат нужно посчитать отде.льно от знаков, а потом добавить знак в соответствии с прави.лами математики:

(-10) div 3=-3; (—10) mod 3=—1; div 5=0; —3) mod 5=—3.

Задание 2.1. Даны З целых числа А, В, С. Вычислить их сумму и произведение.

Продумаем а.лгоритм решения данной задачи (рис. 2.3). Представьте каждый шаг алгоритма на языке Паскаль.

Задание 2.2. Дана длина ребра куба (целое число). Найти объем куба и п.лощадь его боковой поверхности.

 

1 блок

Присваивание значений переменным

 

 

 

 

 

 

2 блок

Вычисление суммы чисел в переменной S

 

 

 

 

 

 

З блок

Вычисление произведения чисел в переменной Р

 

 

 

 

 

4 блок

Вывод на экран суммы чисел (переменная S)

 

 

5 блок

Вывод на экран произведения чисел

(переменная Р)

Рис. 2.3. Блок-схема алгоритма решения задания 2. Л

Стандартные функции типа Integer

Многие стандартные деиствия с числовыми данными выполняются путем вызова функций из библиотеки Паскаля. Такие функции называются стандартными функциями.

Пример 2.3. Демонстрация стандартных функций

Program Infunct: var jntegep:

ЬедЈ п

 Функция Abs (Х) вычисляет абсолютное значение аргумента Х, то есть модуль Х } wrjte1n( 'Abs(-2)

 Функция Sqr (Х) возводит в квадрат аргумент Х } 'Sqp(B)

При запуске программы вывод на экран:

Задание 2.3. Вычислите значение выражения 139 • 54 — 84 2

Задание 2.4. В переменные А и В записаны це.лые чис.ла (оператором присваивания, например, А:=20; В:=13). Поменяйте числа в этих переменных местами.

Будьте внимательны! Если записать А:=В, вы потеряете чис.ло 20 и получите в двух переменных число 13! Воспо.льзуйтесь третьей

Рис. 2.4. Схема обмена значений двух переменных через третью ячейку. В кружках указан порядок операторов присваивания

Задание 2.5. Выполните задание 4 без использования третьей переменной. Используйте действия сложения и вычитания.

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

Вся информация в компьютере хранится в виде последовательностей нулей и единиц. Информация, для записи которой используется всего два знака: 0 и 1, называется двоичной. Информацј,ш в компьютере хранится в виде двоичных кодов (комбинации из ну.лей и единиц). Память мы представляем, как пос.ледовате.льность ячеек, каждая из которых имеет свой адрес (см. рис. 2.1 ).

Стандартная д.лина ячейки — 8 бит, что равно 1 байт. В такую ячейку можно записать двоичный код длиной 8 бит.

Д.ля переменной типа jnteger выделяется ячейка д.линоЙ в 2 байта = 16 бит. Такая ячейка получает символьное имя — имя переменной, и вы обращаетесь к ней не по адресу, а по имени. Крайняя левая позиция выделяется для знака числа:

+ 0 — чис.ло положительно;

+ 1 — чис.ло отрущате.льно.

Остальные 15 позиций выделяются для записи самого чис.ла в двоичном виде (рис. 2.5).

                         Старший байт           Младший байт

Знаковый бит        15 бит

Рис. 2.5. Распределение двоичных разрядов (бит) при хранении числа типа integer

На 15 позициях можно получить 2 15 двоичных кодов. Самое маленькое число состоит из 15 нулей, самое большое — из 15 единиц. Поско.льку счет начинается с нуля, получаем всего (2 15 - 1) по.ложите.льных двоичных чисел. С учетом знака числа (+ —) по.лучаем, что числа типа Ј ntegep имеют диапазон представ.лею,ш 2 15 +2 15 - 1, или -32 768 ... +32 767.

Тип integer является основой для нескольких производных типов — со знаком и без знака (табл. 2.1).

Задание 2.6. Считая, что операция умножения и операция возведения в квадрат имеют одинаковую сложность, запишите оптимальным образом выражеения:

б) х6 ;

д) х10 .


Таблица 2.1. Целочисленные типы данных среды Turbo Pascal

Наличие нака

Тип переменной

Формат (длина) в байтах

 

Диапазон

запись с порядком

 

Обычная запись

Без знака

byte

1

о 2 8 - 1

 

о ... 255

word

2

о ... 2 16 - 1

 

о ... 65 535

Со знаком

shortjnt

1

¯ 2 7                         1

 

-128 ... 127

Ј nteger

2

—215              215 - 1

 

-32 768 ... 32 767

]ongjnt

4

—231              231 - 1

 

-2 147 483 648 .

2 147 483 647

Урок 2.2. Включаем в работу вещественные числа

Что де.лать, ес.ли число не целое, то есть имеет десятичную точку?

Описание вещественного типа данных (reaI)

Опишем в программе переменные для хранения не целых чисел. Такие переменные имеют тип rea[ — вещественный тип.

Пример 2.4. Работа с типом real Program 0pte] :

var

begj п

При запуске программы вывод на экран:

сумма 1.1100000000301

Форматы заг•иси вещественных переменных

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

1.     Обычная форма.

Примеры:

+ 0,7 может быть записано как 0.7 или . 7

+ —2,1 может быть записано как —2.1

2.     Запись с экспонентой: число представляется в виде мантиссы, то есть дробной части числа, умноженной на 10 в некоторой степени.

Примеры:

+ 2700 = 2,7 • 103 . Чис.ло 10 записывается в виде буквы Е, а за ней идет ве.личина степени: 2.7Е3; + 0,002 = 2 • 10 3 соответствует запись 2Е—3.

Вещественные операции

Пример 2.5. Операции с переменными вещественного типа program 0peratjon; var

begj п

А =17.З;

— это операция деления }

При запуске программа выведет на экран следующее:

А/В- 5.0882352941300

А+В= 2.0700000000301

ЗАМАЧАНИЕ

Опя вещественных чиссп нст таких пробки с операдией Эспения, как Эпя вспых чиссп. Опсрадия «/» — это обычное Эспсние.

Задание 2.7. Вычислите выражение:

7.478937 - 89.2456 х 76.2833.

883.5995 + 618.332

Стандартные функции типа real

Пример 2.6. Стандартные функции с вещественными переменными Program var

А, 3: rea] : begj п

Вычисление квадратного корня }

                               { Вычисление синуса }

{ Зададим вывод вещественного числа не в экспоненциальной, а в обычной форме. На экране под значение переменной закажем 6 позиций . Из них 3 позиции выделим для цифр справа от десятичной точки } wrjte1nCSjn(2)

Вычисление косинуса } wrjte1n( 'Cos(2)-

Вычисление арктангенса } Вычисление логарифма }

wrjte1n( 'Ln(2)-

 { Возведение числа е в степень А } wrjte1n( 'Ехр(2)

{ Вычисление числа Пи } wrjte1nCPj=

При запуске программы выведет на экран следующее:

Sqrt(2)= 1.4142135624000

0.909

1.1071487178000

6.9314718056Е-01

7.3890560989000 З. 1415926536000

ЗАМЕЧАНИЕ

ДЗы, вероятно, заметим, что ДТаскапь содержит шапо ФУНК1јИЙ. Он нс ушест вычислять Эажс тс ФУНКДШМ, копюрыс вычис(1ясп1 Обычный ИНЖСНСРНЫЙ капькупятор! Что же это за язык программирования, скажете вы! Ответ на это прост: (Таскапь разрабатывался не Эля вычиспсннй как, например, фортран), а Эпя обучения.

Запись математических выражений

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

sinx cosx lnx tg х ctgx logy х

                  cosx               sm х                 lny

Например, чтобы вычислить 1 + cosx мы напишем на Паскале:

ехр( (1+cos(x)) * 1п(2*х+З) )

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

Например, выражение , записанное в виде Х+1/2х, содержит 2х сразу три ошибки. Во-первых, приоритет операции деления выше, чем у сложения, поэтому, для правильного вычисления числителя его надо взять в скобки: (х+1). Во-вторых, Паскаль не понимает, что означает 2х. Это мы привыкли, что в математике операцию умножения в случаях опускают. Паскалю требуется, чтобы она была указана явно:  Но даже это выражение все еще содержит ошибку. Дело в том, что умножение и деление имеют одинаковый приоритет и выполняются слева направо. Значит, при такой записи сначала выполнится деление, а потом результат будет умножен на х. Нужно либо поставить знаменатель в скобки и написать (><+1)/(2*х), либо, для ленивых, поставить вместо умножения деление: Порядок вычисления в этом случае будет не такой, как требует условие. Однако результат будет таким же:

ведь поделить на 2х — это все равно, что поделить на 2, а потом результат — на х!

ЗАПОМНИТЕ!

Иргутенты функдни всаЭа пишутся в скобках. [То есть у функдии нет аргументов (как у fi, например), пю скобки поспе ее не нужны. 8спн же аргументы есть, пю поспе имени функдшм вы Эопжны обязатепьно открыть скобку, перечнспшпь аргументы и не забыть закрыть скобку. Например, sin 2х в Таскапе нужно записывать как sin(2*x)

Задание 2.8. Напишите программу для вычисления дискриминанты квадратного уравнения. Коэффициенты задайте в программе через оператор присваивания.

Продумаем алгоритм решения данной задачи (рис. 2.6.). Запишите каждый шаг алгоритма на языке Паскаль.

Задание 2.9. Вычислите выражение:

Рис. 2.6. Алгоритм выполнения задания 2.8

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

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

8 бит

Знаковый Порядок              Мантисса бит

Рис. 2.7. Распределение двоичных разрядов (бит) при хранении числа типа real

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


Как

Таблица 2.2. Вещественные типы данных среды Turbo Pascai

Тип переменной

Формат (длина) в байтах

Примерный диапазон абсолютных значений

Количество значащих десятичных цифр

Одинарный (sj пд]е)

4

10-45    1038

7 или 8

Вещественный (реа Л)

6

10-39    1038

11 и.ли 12

Двойной (doub] е)

8

10¯324          10308

15 или 16

Расширенный (extended)

10

10-4932           104932

19 или 20

Урок 2.3. Как совместить переменные целого и вещественного типа

В программе могут одновременно встречаться переменные разных типов. Как их совместить?

Преобразование типов

Пример 2.7. Одновременное использование вещественных и целых

типов Program МЈх; var

Ј nteger; реал : begj п

{ 3 переменную типа реал можно записать целое число }

wrjte1n( '3

{ 3 переменную типа jnteger нельзя записать вещественное число! Чтобы все-таки поместить число типа реал в переменную типа jnteger, нужно явно указать, что делать с дробной частью числа. Есть два варианта: }

{ Функция Трипс(Х) возвращает целую часть числа Х, то есть, отбрасывает дробную часть }

{ Функция Round(X) — округляет до ближайшего целого }

При запуске программа выведет на экран следующее: в— 4. 0000000000000

Еще раз уточним правила преобразования типов: д.ля хранешля данных типа jnteger используется 2 байта, а для реал необходимо 6 байт. Это значит, что число типа jnteger можно поместить в ячейку типа rea] (це.лая часть будет равна этому числу, а дробная будет равна ну.лю). А вот число типа реал в ячейку типа jnteger никак не поместится. Чтобы все-таки поместить его туда, нужно явно указать, что делать с дробной частью числа. Д.ля этого предусмотрены 2 функции: trunc и round. Обе они возвращают результат типа Ј nteger.

Что делать, если в программе нужно записать сложное математическое выражение? В каком порядке будут выполняться действия?

Правила приоритета в выполняемых действиях

1.     Действия над переменными, стоящими в скобках, выпо.лняются в первую очередь.

2.     Пос.ле вычисления значений всех скобок вычис.ляются все функции.

З. Пос.ле функций выполняются умножение и деление. Они имеют одинаковый приоритет.

Как

4.     С.ледующие по приоритету — сложение и вычитание.

5.     Операции одинакового приоритета выполняются с.лева направо.

Действия над данными разных типов

Сведем воедино операции и функции по работе с вещественными и целыми величинами.

Таблица 2.3. Операции и функции для типов integer и real

Операция/функция

 

Тип данных

1 -го аргумента

Тип данных 2-го аргумента

Тип результата

 

Integer

Integer

Integer

Integer

 

 

Integer

 

 

 

 

 

Не важен

 

Djv, mod

 

Только Integer

Integer

AbS , Sqr

 

Integer

 

Integer

 

 

Sqrt , Sjn, Cos, [п, Ехр, РЈ

Arctan ,

Не важен

 

 

Трипс, Round

 

Не важен

 

Integer

Поясним написанное в таблице. Мы разделили все функции операции на 6 категорий.

1.     Резу.льтат операций +, — и * зависит от типа аргументов. Ес.ли хоть один из них имеет тип реал , то и результат будет иметь тип real. Это объясняется тем, что у данных типа реал есть дрооная часть, а у Ј nteger — нет. Даже если в вещественной переменной хранится целое число, оно все равно имеет дрооную часть, то.лько она равна нулю. То есть, если хотя бы у одного из аргументов есть дробная часть, то в результате выполнения операции она никуда не исчезает. Поэтому результат тоже имеет дрооную часть (реал

2.     Резу.льтат операции вещественного деления по опреде.лению всегда имеет дробную часть.

З. Операции целочисленного деления определены то.лько д.ля це.лых чисел. Поэтому результат тоже всегда це.лый.

4.     Функции Abs и Sqr определены для обоих типов данных. Поэтому тип их результата зависит от типа аргумента. Д.ля це.лого аргумента результат имеет целый тип, д.ля вещественного — вещественный.

5.     Функции Sqrt, Sin, Cos, Arctan, Ln, Ехр, Pi по определению яв.ляются вещественными. (На самом деле это связано с осооенностями вычис.ления Паскалем этих функций. Он вычис.ляет их приб.лиженно путем разложения в ряд. Такой метод не предпо.лагает це.лого результата. Более того, значения этих функций всегда вычис.ляются приближенно.)

6.     Функции Trunc и Round предназначены для преобразования типов. Они явно указывают на то, что сделать с дрооной частью числа. Поэтому это единственный способ получить на Паскале из дробного числа целое.

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

Задание 2.10. Вычислите выражение и укажите тип результата:

      Abs(12          1*4/2-350 djv 15)+2

Сначала расставим операции в соответствии с их приоритетами (таб.л. 2.4).

Таблица 2.4. Расстановка операций в примере 2.8

N2 пп

Операция

Пояснение

1

12          7

Сначала выполняются действия в скооках. Скобки в данном случае ограничивают аргумент функции Abs. В скооках сначала выполняются операции типа «умножение-де.ление», а потом — «сложение-вычитание». Операции mod и div осуществляют целочисленное деление. Их приоритет такой же, как у операций «*» и «/». В первой группе таких операций три. Выполняем их слева направо. Поэтому сначала выполняем mod, затем оудет «*», а потом «/»


Урок 2.3. Как

N2 пп

Операция

Пояснение

2

(12 mod 7) * 4

 

з

(12 mod 7*4)

 

4

350 djv 15

Теперь выполняем вторую группу операций типа «умножение-деление». В данном случае это одинокая операция div

5

(12          7*4/2)

- (350 djv 15)

Теперь пришла пора объединить результаты первых двух групп операцией «—». Она выполняется последней в скобках, так как имеет наименьший приоритет

6

Abs ( )

Теперь пора посчитать результат функции

7

Abs(...) + 2

Последний оператор — сложение

Нам представляется удобным расставлять приоритеты, обводя операции (рис. 2.8). Так оказывается легче понять, что уже вычис„лено и что еще предстоит вычислить.

Рис. 2.8. Порядок выполнения операций в задании 2. Л О

Теперь определим тип и результаты каждого действия (таб.л. 2.5).

Таблица 2.5. Вычисление результатов каждого действия из задания 2.10

 

Операция

Результат и его тип

Пояснение

1

12          7

5

Integer

Остаток от деления 12 на 7 равен 5. Результат операции mod всегда целый

2

 

20

Integer

Результат умножения двух целых — целое число

продолжение Ф

Таблица 2.5 (продолжение)

 

Операция

Результат и его тип

Пояснение

з

20 / 2

10.0

Результат вещественного деления всегда вещественный. Хотя 20 делится нацело на 2, мы специально приписали «.0» к результату, чтооы подчеркнуть и не заоыть, что результат вещественный и имеет дрооную часть

4

350 djv 15

23

Integer

При целочисленном делении нас интересует только целая часть частного. Результат операции div всегда целый

5

10.0 - 23

-13.о

При вычитании одно из чисел имеет дрооную часть. Команды от нее изоавиться не оыло, поэтому результат тоже имеет дрооную часть. Значит, результат имеет тип Rea

6

Abs(-13.O)

13.0

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

7

13 . 0 + 2

15.0

Те же соооражения, что и в 5-м пункте. Результат имеет дрооную часть

Ответ: 15.0. ReaL

Задание 2.11. Дано действительное число Х. Напишите программу для вычисления:

+ целой части числа Х;

+ чис.ла Х, округленного до ближайшего целого; + чис.ла Х без дробных цифр.

Урок 2.4. Ввод и вывод данных

Заносить данные в ячейки памяти можно не только оператором присваиваю,ж, но и путем непосредственного ввода с к.лавиату-

данных

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

Вводим переменные с клавиатуры

Пример 2.8. Ввод с клавиатуры значения переменной N program Г пр: uses Crt; var jnteger•, begj п СЛ rScr:

wrjte( 'Введите число с клавиатуры:

Здесь программа приостановится и будет ожидать ввод с клавиатуры Наберите на клавиатуре число, например 153, и нажмите клавишу Enter}

                           ввели число             

read]n Это оператор пустого ввода. Здесь программа опять приостановится и будет ожидать нажатия клавиши Enter За это время вы успеете просмотреть вывод на экране. этот прием мы рекомендуем использовать, чтобы не нажимать A]t+F5 после окончания работы программы }

Красивый вывод на экран

Рассмотрим еще одну задачу: задать с клавиатуры цвет фона (экрана), симво.лов и координат для вывода текста, а затем вывести текст в окно с заданными координатами.

Продумаем алгоритм решения данной задачи (рис. 2.9).

Блок 1

Очистка экрана.

Установка текстового окна

 

 

 

Блок 2

Ввод цвета экрана

 

 

 

 

 

 

Блок З

Ввод цвета выдаваемых символов

 

 

 

 

 

 

Блок 4

Установка цвета экрана

 

 

 

 

 

 

Блок 5

Установка цвета выдаваемых символов

 

 

 

 

 

 

Блок 6

Ввод координат Х,У дпя выводимого текста

 

 

 

 

 

 

Блок 7

Очистка экрана

 

 

 

 

 

 

Блок 8 Позиционирование курсора в точку Х,У

 

 

 

 

 

Блок 9

Вывод текста на экран

Рис. 2.9. Алгоритм решения задачи примера 2.9

данных

Пример 2.9. Красивый вывод текста program 1 пр со1ор:

uses

Crt , { Подключение библиотечного модуля Crt } var

{ ОП ишем переменные, где будут храниться

цвет экрана

и цвет выдаваемых символов - СЛ 2 }

сл 1 , 0 2 : о . . 15:

0. . 15 — этот тип для переменных

 

называется интервальным . В данном случае значения переменных МОГУТ меняться в интервале от 0 до 15. Здесь мы имеем отрезок (интервал) базового типа jnteger. Палитра

цветов лежит именно в этом интервале }

    Ј nteger:

Для хранения координат }

begj п

{ Блок 1: } СЛ rScr:

{ Ниже следует вызов процедуры У2) из модуля Crt, которая определяет на экране текстовое окно. (X1,Y1) — координаты верхнего левого угла окна, (Х2,У2) — координаты нижнего правого угла }

{ Блок 2: } wrjte( Введите цвет для экрана:

            read1n(C11) :          { Здесь работа программы

приостанавливается и ожидается ввод номера цвета экрана в переменную C 1 1. во время работы

программы следует ввести число и нажать Entep }

{ Блок З: } wrjte( Введите цвет для символов: ' ) :


read1n(C12) ,

{ Блок 4: }

{ Ожидается ввод номера цвета символов в переменную СЛ 2. во время работы программы следует ввести число и нажать Enter }


TextBackGround(C] 1) ;              { Выбор фонового цвета }

{ Блок 5: }

 { Выбор цвета выводимых символов }

{ Блок 6: } координаты Х и У ' ) ;

{ Ожидается ввод координат для выводимого текста . Необходимо ввести два числа (координаты) через пробел и нажать Enter. Помните, что координаты внутри текстового окна отсчитываются от его левого верхнего угла . В нашем окне 16 строк и 41 столбец}

{ Блок 7:

 

С] rScr;

{ Блок 8:

 

{ Функция очистки экрана в данном случае очистит не весь экран, а только заданное текстовое окно }

 Позиционирование курсора в точку с координатами Х, У }

{ Блок 9:

wrjte]nC Мы отлично вводим с клавиатуры! ' ) :

read]n { этот ”пустой” оператор read1n задерживает нас в экране пользователя Возврат в среду ТирЬо Pasca1 происходит после нажатия Enter }


Задание значений переменных датчиком случайных чисел

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

Зададим этим способом цвет экрана. Вместо второго б.лока в программе поставим вызов датчика случайных чисе.л Random(X). Но перед этим обязательно нужно выполнить процедуру инициа.лизации датчика, иначе он будет выдавать нам нес.лучайные чис.ла:

Randomjze; Иницидлиздция датчика случайных чисел проводится один раз в программе }

 В результате переменной СЛ 1 присваивается целое случайное число из диапазона 0 . . 15 .

Результат функции Random(N) случайное число из диапазона 0. .N-1 }

ЗАМЕЧАНИЕ

На самот Эслс настоящий программный Эатчик СП!јчаЙНЫХ чиссл созЭС7ПIЬ невозможно. функдия Random выЭает псевдослучайные чисТ. Это что чист, выдаваемые функднсй, порожЭаются по ОПРСЭСЛСННОЙ закономерности. Эта закономерность придумана так, чтобы казадось, что чиспа подучаются случайными. ОЭнако если не использовапљ продеЭуру randomize, кажЭый раз при запуске программы поспеЭоватепьность чисел будет одинаковой. ТродсЭура randomize «встряхивает» начапьное значение поспеЭовательноспш. Поспе этого порождаемая поспеЭоватепьность чисел становится почти уникапьноп.

Рассмотрим еще несколько примеров использования функции Random.

Пусть нам необходимо получить случайное двузначное число, то есть число от 10 до 99. Логика проста: нам нужно одно случайное 90 (именно столько всего двузначных чисел). Значит, будем использовать функцию Random, которая выдаст одно число из 90, то есть Random(90). Но результат тогда будет лежать в диапазоне 0..89, а нам требуется 10..99! Значит, нужно сдвинуть полученный диапазон вправо на 10. Это делает операция +10. В итоге получаем следующее: Random(90)+10.

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

Задание 2.13. Напишите программу, которая выдает сообщение на по.лныЙ экран (без текстового окна). Координаты сообщения, цвет экрана и цвет символов задайте с помощью датчика с.лучайных чисе.л.

ЗАПОМНИТЕ!

Запивка экрана ипп текстовою окна выполняется выЗовом Эвух продеЭур: установка уста экрана продеЭурой TextBackGround(...) а очистка экрана продсЭурой C[rScr.

Урок 2.5. Зачем нужны константы в программе?

Бывают случаи, когда некоторые величины не меняются по ходу выполнения программы. Для удобства работы с такими величинами в Паскале предусмотрена отдельная категория — константы.

Для начала простой пример:

Пример 2.1 О. Расчет скорости тела при падении с башни

Program Pjza: var реал :

ЬедЈ п

{ эта переменная всегда имеет одно значение и не ИЗ Меняет е по по ходу выполнения программы }

wrjte( Введите высоту башни: '): read1n(H):

wrjte1n( [Скорость падения ' :20,V:7:3):

{ На выводимый текст выделяется 20 позиций } read1n

Зачем нужны константы в программе?

Д.ля наг.лядности в формуле вычисления скорости падения мы испо.льзовали переменную G, которая, однако, в действите.льности не менялась. И это неудивительно: она представляет ускорение свободного падения, которое, как известно, есть величина постоянная.

Д.ля того чтобы явно указать в программе, что ве.личина G не может измеш,ггься, перенесем ее описание в отдельный разде.л — разде.л описания постоянных величин, или констант (см. с.ледующий пример). Тем самым мы покажем Паскалю и человеку, который будет читать нашу программу, что эту величину не.льзя менять по ходу выпо.лнения программы. Она получает свое значение один раз и не может быть изменена.

Пример 2.11. Программа при использовании констант — более логична и читабельна Program Pjza2:

        const                  Это раздел описания констант.

Он находится перед разделом var }

G=9 .8:  Тип константы определяется автоматически по форме здписи числа. В данном случае из-за наличия десятичной точки это тип реал }

var V,H: rea] : begj п wrjte( Введите высоту башни: read1n(H) : V:=Sqrt(2*G*H) :

' Скорость падения

Чтобы текст и число не ”слиплись' после текста внутри апострофов добавлен пробел }

read1n

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

Во-вторых, константы оказываются нужны при объяв.лении новых типов данных — массивов. Об этом мы поговорим в теме 8.

Задание 2.14. Вычислите длину окружности и площадь круга. Радиус до.лжен вводиться с клавиатуры.

Выводы

1.     Данные, с которыми работает программа, хранятся в ячейках. Каждая ячеика имеет имя и тип данных. Изменяемые ячейки называются переменными, неизменяемые — постоянными.

2.     Ячейки, используемые в программе, описываются в разделах const и var.

З. Для хранения целых чисел используется тип данных Ј nteger, а для хранения вещественных — реал .

4.     Каждый тип данных имеет свои операции и функции. Особенно это важно для типа Ј ntegep, который имеет две операции деления — dj v и mod.

5.     Для преооразования значений типа rea1 к типу Ј nteger используются специальные функции — tpunc и round.

6.     При записи арифметических выражений нужно помнить о приоритете операций и о типе данных, который получается в резу.льтате.

7.     Нача.льные значения переменных можно задавать путем ввода с к.лавиатуры или с помощью датчика случайных чисе.л.

Контрольные вопросы

1.     Где хранятся все данные, с которыми работает программа?

2.     Чем раз.личаются между собой понятия имя ячейки, а ес ячейки и значение ячейки?

З. Какой тип данных используется для хранения целых чисел? А для дробных?

4.        Что следует сделать, если в программе используется величина, не изменяющаяся за все время работы программы?

5.        В чем различие между операциями mod, djv и / ?


Контрольные вопросы

6.        Зачем нужны функции trunc и pound? В чем между ними разница?

7.        Какое максимальное значение может принимать переменная типа jntegep? Что делать, если необходимо сохранить це.лое чис.ло, в 10 раз большее этого значения?

8.        Как записать на Паскале «2,5 в степени 16,7»?

9.        Что означает запись «1ЕБ», «ЗЕ-4», « .2Е7»?

10.     Что нужно использовать, чтобы изменить приоритет выполнения математических операций?

11.     Чему равно и какой тип данных имеет вырюкение  mod 4/3)?

ТЕМА З Учимся работать с символами

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

Урок 3.1 . Как компьютер понимает символы

Под символами мы понимаем буквы и все те значки, которые вы видите на клавиатуре. В Паскале переменные для хранения символов имеют тип Char.

За каждым символом закреплен свой числовой код. Все коды сведены в таблицу

Кодовая таблица ASCIl

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

Как видите, цифры здесь — не числовые данные, а тоже симво.лы, каждый из которых имеет свой код. В компьютере коды записаны в двоичном виде. На каждый код выделено 8 бит, то есть 1 байт. По.лучаем 2 8 — 256 двоичных кодов. Всего в таб.лице ASCII 256 кодов: наименьшее значение кода 00000000, наибо.льшее 1 1 1 1 1 1 1 1 (это 255 в двоичном виде).


Таблица 3.1 . Фрагмент таблицы ASCll (таблица кодов символов)

Код

двоичный код

Символ

Код

двоичный код

Символ

48

00110000

0

65

01000001

 

49

00110001

1

66

01000010

в

50

00110010

2

67

01000011

с

57

00111001

9

89

01011001

 

 

 

 

90

01011010

 

Описание типа Char и стандартные функции

Пример 3. 1 . Демонстрация стандартных функций для работы с типом

Char Program Letter1:

var

N : lnteger:


Х : Char; begj п

 { 3 символьную переменную Х записали символ ' L ' }

wrjte1n(X) ,

 { Функция 0rd возвращает код символа, занесенного в переменную Х, то есть код буквы ' L ' }

wrjte1n(N) ,

wrjte1n(X) ,

 { Функция Chr возвращает символ по заданному коду. Сейчас в переменной Х оказался символ ' L '        имен НО е ГО код мы только что записали в переменную N }

wrjte1n(X) , read1n

                                       Урок 3.1 . Как компьютер понимает символы          69

При выпо.лнении программа выведет на экран следующее:

76

Пример 3.2. Ввод символов с клавиатуры Program Letter2: var

begj п wrjte1n( Введите несколько символов: read1n(X) , wrjte1n(X) , wrjte1n( Введите еще несколько символов:

read1n

Запустив программу на выполнение, введите с к.лавиатуры пос.ледовательность символов (например, АВС) и нажмите Enter. Программа выведет единственный символ:

В ответ на второе предложение введите с клавиатуры САГ. На экране получим следующее:

СА

ЗАМЕЧАНИЕ

Переменная типа Char принимает топько оЭин симвоп из введенной строки. Три ввоЭе симвопы не закпючаются в апострофы. Иаким образом, в первом спучае из введенных симвопов запомнился топько оЭин, во вт•орот — Эва.

Можно определять и символьные константы:

const Leto ' Х ' ,

Урок 3.2. Тип Char — порядковый тип!

В таб.лице кодов вы могли заметить такую закономерность:

Таким образом, для каждого элемента типа Char всегда есть предшествующий и последующий элементы. Такой тип данных называется порядковым. Тип Chap — порядковый тип. Тип Integer также яв.ляется порядковым.

Пример 3.3. Стандартные функции, применяемые к порядковому типу

Program Letter3: var

Char: begj п

Функция Pred возвращает предшествующий элемент относительно значения переменной х 1 }

             wrj           'Pred=

относительно значения переменной Х 1 }

wrj te1n( Лисс read]n

При выполнении программа выведет на экран следующее:

Succ=M

Задание З. 1. Напишите программу расшифровки 4-буквенного однос.ловного сообщения. Для получения 4 букв нужно ввести З строки:

                                                                                                        Выводы        71

+ из первой строки прочесть только первую букву;

+ из второй строки прочесть только первую букву;

+ из третьей строки прочесть первую и вторую буквы.

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

Д.ля проверки возьмите пример, приведенный ниже. Ввод:

FINISHED

PENDING?

На выводе до.лжно быть слово «DONE» .

Задание 3.2. Известно, что коды прописных (заг.лавных) букв .латгшского алфавита следуют в таблице непрерывно друг за друтом. Коды строчных букв латиницы также следуют непрерывно друг за другом на расстоянии 32 символов от прописных (ниже по таб.лице). Если ord( = 65, то ord( 'А' )+32 97, и это код строчной буквы «а», то есть ' А ' )+32) - ' а ' . Напишите программу, в которой вы вводите прописную букву (то.лько .латиницу!), а получаете ее строчный эквивалент, и наоборот, по строчной букве по.лучаете соответствующую прописную.

ЗАМЕЧАНИЕ

С русскими симвопалш такого порядка нет из-за особенностей ортНИЗТЈ[Ш кодовой табпшјы.            частности, строчные буквы в табптје спеЭуют не поЭряЭ, а с разрывом в середине алфавита.

Выводы

1.     Все симво.лы хранятся в компьютере в виде кодов.

2.     Обычно д.ля кодирования символов применяется таб.лица ASCII.

З. Каждому символу соответствует свой код.

4.     Для преобразования символов в коды и обратно применяют функции ord и ст.

5.     Тип Char является порядковым типом.

6.     Коды буквы латинского алфавита идут последовате.льно.

7.     Русские буквы хранятся в таблице символов ASCII с разрывом в пос.ледовательности кодов.

8.     Д.ля по.лучения следующего и предыдущего симво.ла испо.льзуют соответственно функции succ и pred.

Контрольные вопросы

1.     Сколько всего различных символов кодируется таблицей ASCII?

2.     Какой объем памяти требуется для кодирования одного символа? А для 15 символов?

З. Какой тип данных в Паскале предназначен для хранен»ш символьной информации? Сколько символов можно поместить в одну переменную этого типа?

4.     Какой код у буквы «F»? Какой символ кодируется кодом 87?

5.     В программе определены З переменные (а , Ь, с :char; ). В ответ на инструкцию pead1 , с); пользователь ввел текст Л еша . В каком месте памяти оказалась каждая из введенных букв?

6.     Каков будет результат выполнения инструкции c:=succ(pred(succ('D')))?

7.     Какое значение получит переменная i в операторе pred(ord( 'F' )-2)?

ТЕМА 4

Джордж Буль и его логика


В этой теме мы оосудим вопросы, связанные с Решениями. Именно так, с большой буквы. Мы разберем, как же компьютер принимает решения. Конечно, компьютеру не приходится принимать такие сложные решения, как человеку. Однако логики и определенности в поведении компьютера куда больше. Собственно, никаких колебаний у него и не бывает. Каждый раз, когда компьютер принимает решение, оно четко и окончательно — или да, или нет! Согласитесь, людям подобной решимости зачастую не хватает.

Урок 4.1 . Необходим еще один тип — логический!

Поговорим о философии, а именно — о логике.

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

+ если условие выполняется, то результатом будет «истина»;

+ если условие не выполняется, то результатом будет «ложь».

Например, утверждение «4 > З» является истинным, а утверждение «2 > З» — ложным.

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

и.ли алгеорой логики.

                            Урок 4.1 . Необходим еще один тип        логический!

Логический тип данных (Boolean)

Для хранения результата проверки условия введен логический тип — Воо] еап. Переменные такого типа называются булевыми переменными.

Пример 4.1. Булевы переменные в программе

Program 30011; var Х : jnteger; Во] Воо]еап; begj п

 { Это утверждение истинно }

 { Это утверждение ложно }

read]n

При выпо.лнении программы на экране мы получим следующее:

ПИЕ

FALSE

Что такое ПИЕ и FALSE? Это значения логического выраже ения: TRUE означает «истина», а FALSE — «ложь».

Операции отношения

В логических условиях используются операции отношения. Вот как они записываются на языке Паскаль (табл. 4.1 ). Таблица 4.1. Запись операций отношения на языке Паскаль

Операция отношения

Запись на язьже Turbo PascaI

Меньше

 

Меньше или равно

 

Бо.льше

 

Больше или равно

 

Равно

 

Не равно

 

Ввод-вывод булевских переменных

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

Пример 4.2. Как ввести с клавиатуры переменную булевского типа program 300]eanInput; var ch:char; begj п wrjte( г Ты хочешь есть Гу/п1? •

[Твой ответ: ' ,eat) ; read]n

Урок 4.2. Логические (булевы) операции

Часто принимаемое решение зависит от результата не одного, а нескольких утверждений. Например, «Вася получит сегодня пятерку, если придет на урок И правильно выполнит задашле». Значит, нужно научиться объединять результаты нескольких утверждений и принимать общее решение. В приведенном примере этим объединением служит союз «И».

Логическое умножение (конъюнкция)

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

Рассмотрим утверждение:

Два отношения связаны союзом «и» (and).

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

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

Урок 4.2. Логические (булевы) операции

Таблица 4.2. Таблица истинности для операции логического

умножения

Операнд 1

Операция

Операнд 2

Результат

трие

and

Трие

трие

Трие

and

Fa]se

Fa]se

Fa1se

and

Трие

Fa1 se

Fa]se

and

Fa]se

Ра Л сое

Логическое сложение (дизъюнкция)

Рассмотрим утверждение: х> 100 и.ли х< 10

Выражения можно связывать союзом «или» (or).

Логическое выражение, связанное союзом «и.ли», всегда ложно (fa] se), ес.ли .ложны обе его части. Во всех других случаях результатом будет «истина» (true).

Эта операция называется логическим сложением, или Дизъюнкцией. На Паскале она обозначается как or.

Таблица 4.3. Таблица истинности для операции логического сложения

Операнд 1

Операция

Операнд 2

Результат

трие

 

Трие

Трие

Трие

 

 

Трие

Fa1se

 

Трие

Трие

Fa]se

 

 

Fa]se

Исключающее ИЛИ (сложение по модулю 2)

Рассмотрим утверждение: либо х> 5, либох< 0

Выражения связаны парой «либо-либо».

Такое утверждение истинно, когда истинно только одно из двух составляющих его утверждений.

Можно сформулировать это иначе: логическое выражение истинно (true), если его операнды различны.

Данная операция называется исключающим ИЛИ. На Паскале она обозначается как xor.

Таблица 4.4. Таблица истинности для операции исключающего ИЛИ

Операнд 1

Операция

Операнд 2

Результат

трие

Хор

Трие

 

трие

Хор

Г-аКе

трие

Г-аКе

Хор

Трие

трие

Г-аКе

Хор

Г-аКе

 

Как еще можно получить эти результаты? Представим значение true как 1 (логическую единицу), а fa1 se — как 0 (логический ноль).

Теперь сложим эти значения и возьмем остаток от деления полученного результата нацело на 2 (mod 2). Эта операция называется также сложением по модулю 2. Ясно, что результат будет всегда меньше двух.

Логическое отрицание (инверсия)

Рассмотрим утверждение: не (х> 100)

Выражение отрицается частицей «не».

Результат операции противоположен отрицаемому утверждению. Если утверждение было истинным (true), результатом будет «ложь» (fa1 se). И наоборот, если утверждение было ложным (false), то получится «истина» (true).

Эта операция называется логическим отрицанием, логическим НЕ или инверсией. На Паскале она обозначается как not.

Таблица 4.5. Таблица истинности для операции логического НЕ

Операнд

Результат операции not

трие

Г-аКе

Г-аКе

Трие

Применение логических операций в программе

Пример 4.3. Логические операции в программе

Program 3001 1:

var Х : Integer:

во] , Оп3о1 , Rez: Зоо1еап: ведЈ п

Урок 4.2. Логические (булевы) операции

опво1 wrj te1n( ' во] wrjte1n(    ' ,ОпВо1 ) : and ОпВо1 :

wrjte1n( 'B01 and ОпВо1-' ,Rez) : ор ОпВо1 :

       wrjte1n( 'ВОЛ ОПВО1- ' ,Rez):

             Rez            вол :

wrjte1n( 'not 301- ' ,Rez) : read1n

При выполнении программы имеем на экране следующее:

ВоЛ=ТИЕ

0nB01=FALSE

      ВОЛ         0nB01=FALSE

ВОЛ not 301=FALSE

Пример 4.4. Составление логических выражений

Program во] 2:

{ Введем логические переменные, которые будут определять характеристики студента.

Составим выражения , определяющие, является ли студент первокурсником и получающим стипендию }

var

 

       Prj се еап:

{ Определяет наличие стипендии у студента }

{ Определяет, является ли студент первокурсником }

       Rezu1t еап:

ЬедЈ п

{ Определяет результат }

{ Пусть наш студент получает стипендию }

    <urs1

{ Пусть студент           первокурсник }

and kups1:

wrj te1n( ' Студент  первокурсник со стипендией? Rezu1t) :

{ Пусть наш студент не получает стипендию } and <urs1:

             wrj te1n( ' Студент         первокурсник со стипендией?

Rezu1t), read1n

При выполнении программы имеем на экране следующее:

      Студент        первокурсник со стипендией? - TRUE

      Студент        первокурсник со стипендией? - FALSE

Задание 4.1. Определите в программе 4 логических переменных, которые содержат следующую информацию о людях: Marrjed «истина», если человек женат (замужем); в 1 ond «истина», если у человека светлые волосы; «истина», если человек — мужчина;

     Етр1 oyed     «истина», если человек работает.

Составьте логические выражения, с помощью которых можно определить, является ли человек:

1)    замужней женщиной;

2)    неженатым мужчиной;

З) незамужней блондинкой;

4)    безработной незамужней женщиной;

5)    „либо неженатым, либо безработным, либо и тем и другим.

Приоритет логических операций

При объединении нескольких логических операций нужно помнить, что они, подобно математическим операциям, подчинены прави.лам приоритета (табл. 4.6). Для изменения приоритета выпо.лняемых действий необходимо использовать скооки.

Таблица 4.6. Приоритет логических операций

Приоритет

Логическая операция

1 (самый высокий)

not

2

and

З (самый низкий)

ор, хор

Контрольные вопросы

Операции xor и or имеют одинаковый приоритет, и значит, при отсутствии скобок, выполняются слева направо.

ЗАПОМНИТЕ!

Это очень важно! ДТриоритст пюбой опертјшм сравнения ниже, чем у шобой пошческой операдии. Это значит, что при объединении сравнений при потонји пошчсских операдип каждое сравнснис необходимо взять в скобки. Например, утвержЭснис 2 Х 4 Эопжно быть записано как

2<=х) and

Выводы

1.     Существуют утверждения, относительно которых можно однозначно сказать, истинны они или ложны.

2.     Д.ля записи результатов таких утверждений, а также д.ля анализа ус.ловий необходим еще один тип данных — ВооЛ еап.

З. Данные этого типа могут принимать одно из двух значений: true («истина») или fa] se («ложь»).

4.     При составлении выражения для анализа условий используются операции

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

6.     Порядок выполнения действий определяется скобками и приоритетами операций. Контрольные вопросы

1.     Что такое логическое утверждение?

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

З. Как записать на Паскале утверждение «икс не равен          ?

4.     Как записать на Паскале утверждение «игрек не прушад.лежит отрезку [3,5]»?

5.     Чему равен результат операции true ор fa1se and fa1 se?

6.     Правильно ли записано выражение (х<0) ор (><+2)>3?

7.     Чему будет равен результат операции true хор fa1se хор true?


ТЕМА 5 Анализ ситуации и последовательность выполнения команд

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

Урок 5. 1 . Проверка условия и ветвление в алгоритме

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

Пусть стоит такая задача:

Ес.ли Х > З, то выводим на экран Х.

Б.лок-схема алгоритма решения этой задачи выг.лядит так (рис. 5.1):

Рис. 5.1 . Блок-схема алгоритма. выводящего число. если оно больше трех

На языке Паскаль такую схему обрабатывает ус.ловный оператор if.

Полная и неполная форма оператора if

Формат          оператора if следующий:

jf <условие> then <оператор> Пример:

jf Х > З then wpjte1n(X):

Под ус.ловием здесь понимается любое выражение, резу.льтат которого имеет тип Ьоо[еап.

Это неполная форма алгоритма с ветвлением.

Изменим немного нашу задачу:

Ес.ли Х > З, то вывести на экран Х, иначе вывести текст ' Х < 3 В том и другом случае Х необходимо увеличить на 1 .

Здесь испо.льзуется расширенный условный оператор — полная форма ветвления:         then

Формат записи оператора: jf <условие> then <оператор-да> ел se <опера тор- нет>. (См. блок-схему на рис. 5.2).

Рис. 5.2. Блок-схема алгоритма. выводящего число. если оно больше трех. или сообщение «Х<З» в противном случае

В общем случае структурная схема условного оператора выглядит так (рис. 5.3).


Урок 5Л . Проверка условия и ветвление в алгоритме

false       true (ложь)             (истина)

Рис. 5.3. Структурная схема условного оператора l f

( S 1, S2, S3 условные обозначения операторов.)

ЗАПОМНИТЕ!

Переэ е] se нельзя ставить точку с запятой.

Независимо от формы записи ус.ловия, после окончанј,ш оператора Ј f программа снова «соединяется» и продолжает выпо.лнять операторы, стоящие после структуры Ј f. Это наглядно демонстрирует рис. 5.3. Оператор S3 выполняется независимо от того, каким будет резу.льтат проверки условия.

Рассмотрим задачу определения количества корней квадратного уравнения по дискриминанту (рис. 5.4).

Пример 5.1 . Анализ дискриминанта квадратного уравнения program Djskp: var

ЬедЈ п wrjte( Введите коэффициенты ' )

jf D >- 0 then jf D > D then wrjte1n( ' два вещественных корня ' )

e1se wrjte1n( ' один вещественный корень ' )

[Нет вещественных корней ' ) : read]n

В этой задаче используется вложенный оператор Ј f.

Рис. 5.4. Блок-схема алгоритма. определяющего количество корней квадратного уравнения по дискриминанту

ЗАПОМНИТЕ!

Три испопьзованшм впожснных операторов jf слово е Л зе опшосится к поспеЭнему Ј f, у которого аде нет е] se.

Оформление программ

Возможно, вы уже обратили внимание, что во всех приводимых нами примерах мы слегка (на 2—3 позиции) сдвигаем операторы вправо. Таким способом мы выделяем, например, б.лок описаю,ш переменных var, а также операторы основной программы относи-

Урок 5Л . Проверка условия и ветвление в алгоритме

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

Мы рекомендуем всегда сдвигать вправо вложенные фрагменты программы относительно точки вложения. Так, например, список переменных, определяемых в разделе var, следует сдвигать относите.льно слова vap, список операторов основной программы — относите.льно begjn и end, а операторы, вложенные в структуру jf, относительно jf и e1se.

При этом соответствующие пары операторов begj п и end рекомендуется располагать друг под другом, на одинаковом расстоянии от „левого края. Это позволяет в сложной программе отс.ледить, какому оператору ЬедЈп какой оператор end соответствует, и, например, найти пропущенный оператор.

 

 

9]

ЗАМЕЧАНИЕ

СекотенЭуется при наборе протатты сразу же поспе оператора begjn писать оператор end, и потом уже межау нити всптвпять впоженные операторы. Это позвопяст избежать сипуадий с появпением непарных begj n/end.

и скобок: набрав певую скобку, сразу поставьте правую и поспе вписывайте ,111Ck011 теэюЭу нити.

Задание 5.1. Нарисуйте блок-схему алгоритма и напишите программу, которая анализирует введенное с клавиатуры число и выдает на экран:

+ удвоенное значение числа, если число положите.льное;

+ абсо.лютное значение числа, если число отрицательное.

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

Урок 5.2. Блоки операторов

Управ.ляющая структура Ј f then может показаться негиокой, так как пос.ле служебного слова then должен стоять то.лько один оператор. Ес.ли вы напишете два оператора подряд (например, Ј f then х то второй оператор выполнится в .любом с.лучае, независимо от проверяемого условия.

Ес.ли требуется выполнить последовательность действий (неско.лько операторов подряд), то ее заключают в б.лок, образуемый операторами begjn и end. Пример:

jf Х > 3 then begjn S1: S2: S3; S4 end;

Здесь S1-S4 символически обозначают операторы.

Эта группа (Ьедјп S1: S2: S3: S4 end) называется составным оператором, и.ли операторной. скобкой. Она как бы говорит компи.лятору, что данный б.лок операторов нужно рассматривать как единое целое.

Пример 5.2. Вычисление корней квадратного уравнения Program Quad: var

            А, 3, С:                 Переменные для хранения коэффициентов }

             D : rea] :              Переменная для дискриминанта }

Х1,Х2: rea] Переменные для получения корней } begj п коэффициенты А, В, С:

wrjte1n( ' Уравнение не имеет вещественных корней ' )

jf   then wrjte1n('Y уравнения один корень '

{ Ниже идет составной оператор }

ЬедЈп

wrjte1n('Y уравнения два корня:

Урок 5.2. Блоки операторов

end; read]n

Задание 5.3. Нарисовать блок-схему алгоритма и написать программу, в которой с клавиатуры вводится код режима работы и цвет СЛ . На экран выводится сообщение. При этом, если то сообщение выводится символами цвета СЛ , а если kd 1, то сообщение выводится символами цвета СЛ +16.

На экране во втором случае вы получите мигающие символы, так как код их цвета больше 15. В этом случае в качестве номера мигающего цвета Turbo Pascal использует остаток от деления кода цвета нацело на 16.

ЗАМЕЧАНИЕ

ДЗ качестве проверяемых усповш7 можно использовать поимческие

Рассмотрим задачу: ввести три числа А, В, C и определить, равны ли введенные числа. Блок-схема алгоритма показана на рис. 5.5.

Рис. 5.5. Блок-схема алгоритма. определяющего. равны ли друг другу три введенные с клавиатуры числа

Пример 5.3. Проверка равенства трех чисел. введенных с клавиатуры

Program [Ух: var jntegep: begj п wrjte1n(' Введите З числа:

jf (А В) and (А С) then wrjte1n( ' Числа равны ' )

wrjte1n( ' Числа не равны ' ) : read1n

Задание 5.4. Ввести три числа А, В, C и определить максимальное из них. Нарисуйте блок-схему алгоритма и напишите программу.

Задание 5.5. Ввести четырехзначное целое число и опреде.лить, яв.ляется .ли оно палиндромом, или «перевертышем» (такими, например, яв.ляются числа 6666 и 3223). Нарисуйте б.лок-схему а.лгоритма.

Подсказка: Для выделения отдельных разрядов числа испо.льзуются операции djv и mod.

Задание 5.6. Ввести три числа А, В, С. Если ни одно из чисе.л не равно ну.лю, то в переменную К записать среднее арифметическое трех чисе.л.

Задание 5.7. Введите значение Х и, используя график функции (рис. 5.6), определите значение У. Заполните блок-схему алгоритма (рис. 5.7).

Задание 5.8. Положение фигуры на шахматной доске (8х8) описывается двумя числами — номером  и номером вертикали. Ввести с клавиатуры координаты ферзя (Х, У) и координаты любой фигуры (М, М). Проверить, находится ли фигура под ударом. Ферзь бьет по вертикали, горизонтали и диагонали.

Задание 5.9. Введите число с клавиатуры. Если это число четное и кратно 7, то выведите свое имя на экран красным (red) цветом, иначе выведите его зеленым (дрееп) цветом. Предварительно заполните блок-схему алгоритма (рис. 5.8).

Задание 5.10. Ввести три числа А, В, C и среднее из них (то, которое больше одного, но меньше другого). Нарисуйте блок-схему алгоритма и напишите программу.

Урок 5.2. Блоки операторов

 

 

_1

1 2

-2

х

 

Рис. 5.6. График кусочно-заданной функции для задания 5.7

Рис. 5.7. «Слепая» блок-схема алгоритма вычисления кусочно-заданной функции. изображенной на рис. 5.6

Рис. 5.8. «Слепая» блок-схема алгоритма. выводящего слово «Вася» красным цветом на экран, если введенное число четное и кратно 7. и зеленым цветом в противном случае

Урок 5.3. Ветвление по ряду условий (оператор case)

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

Пример 5.4. Преобразование введенного целого числа из диапазона (0..4) в его словесное представление

Program Djgjt1: var

Num: jntegep:

ЬедЈ п wrjte( Введите число: read1n(Num) jf Num 0 then wrjte1n( 'Ноль' ) :

Урок 5.3. Ветвление по ряду условий (оператор case)

            jf Num      1 then

'0дин'):

jf Num = З then

jf Num = 4 then wrj te1 ' Четыре ' ) : read]n

Выпо.лним ту же задачу, используя другую управ.ляющую структуру — оператор выбора case of. Формат записи оператора таков:

сазе <выражение порядкового тип» of

Пример 5.5. Использование структуры case ... of для перевода целого числа в его словесное представление Program Djgjt2: var Мит Ј nteger : begj п wrjte( ' Введите число: read1n(Num) : case [\lum of

Введено другое число' ) end, read1n

Да.лее приведена блок-схема алгоритма решения этой задачи (рис. 5.9).

Переменная Num является селектором в операторе case. По значению се.лектора происходит переход на соответствующую метку.

Се.лектор должен принадлежать к порядковому типу (то есть

Рис. 5.9. Блок-схема алгоритма. выводящая словесное представление введенного с клавиатуры числа (не большего 4)


Урок 5.3. Ветвление по ряду условий (оператор case)

Пример 5.6. Определение номера квартала по введенному номеру

месяца Program Djgjt3:

var

Num : Ј nteger:

begj п wrjte( Введите номер месяца: read1n(Num) :

сазе Мит of

                    1,2,3                          'Первый квартал

4' Второй квартал

                   7. 9                                  ' Третий кварталметка 7. .9 -

интервал чисел от 7 до 9; тоже самое, что и        } 10. .12:              'Четвертый квартал ел se     'Некорректный ввод

read]n

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

Задание 5.11. Написать программу, в которой в переменную типа Char вводится символ с клавиатуры. Программа выдает сообщение о том, какой символ был введен: + цифра от 0 до 9;

+ „латинская строчная буква; + „латинская срочная буква.

При записи меток в операторе case можно использовать интервальный тип. Например, интервал для латинских заглавных символов записывается: 'А '

Если нужно учесть строчную латиницу, то интервал для меток будет выглядеть так:

Выводы

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

2.     При ветв.лении анализируется логическое выражение и, в зависимости от его результата, выполняется та и.ли иная ветвь алгоритма.

З. На Паскале оператор ветвления называется jf. Он имеет две формы зашмси — полную и неполную.

4.        При полной форме записи jf в случае истинности логгщеского условия выполняется один блок программы (после слова then), а в случае ложности другой (после слова е] se).

5.        При неполной записи оператора Ј f блок ел se опускается.

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

7.        Точка с запятой слева и справа от then и от ел se не ставится.

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

9.        Оператор case должен заканчиваться ключевым с.ловом end. Это один из тех редких случаев, когда ко.ш,щество операторов begj п в программе не будет совпадать с количеством операторов end.

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

Контрольные вопросы

1.                  Чем от.личается линейный алгоритм от ветвления?

2.                  Какие ключевые слова используются в Паскале для организации ветвления? Что находится между ними? З. Чем полное ветвление отличается от неполного?

                                                                              Контрольные вопросы         97

4.     Как оформ.лять текст программы, чтобы он бы.л понятнее?

5.     Что необходимо использовать, если в случае истинности некоторого условия нужно выполнить несколько операторов?

6.     Как быть, если в случае истинности некоторого ус.ловия никаких действий выполнять не требуется, а в случае „ложности нужно выполнить несколько действий?

7.     Какую управляющую структуру Паскаля нужно испо.льзовать, ес.ли проверяемое выражеение может принимать неско.лько возможных значений, и в каждом случае необходимо выпо.лнить разные действия?

8.     В каком случае количество операторов begj п в программе не должно соответствовать количеству операторов end?


ТЕМА 6

Многокра.тно повторяющиеся действия

Иногда неооходимо повторить определенные действия в программе. Повторение некоторой последовательности действий называется циклом. Саму последовательность повторяющихся действий называют телом цикла.

Если число повторений известно заранее, то используется структура, которая называется циклом с заданным (известным) числом повторений, или циклом со счетчиком. Этот цикла является частным случаем цикла с условием. Мы начинаем с этого вида цикла в силу его простоты и наглядности.

Урок 6.1 . Оператор цикла for

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

for <переменная порядкового типа>:=<начальное значение> to «онечное значение> do <оператор>

Вторая форма последовательно уменьшает счетчик:

for <переменная порядкового типа>:=<начальное значение> downto «онечное значение> do <оператор>

1 00

Оператор for с последовательным увеличением счетчика

Пример 6.1 . Вывод на экран квадратов чисел от 1 до Л О Program Test1: var N : jntegep:

ЬедЈ п for N:=1 to 10 do  переменная будет от 1 до 10 с шаг ОМ 1 }

Эта строка — тело цикла. Оно выполняется 10 раз }

read]n

Поясним пример 6.1. Переменная является счетчиком цикла. Счетчик цикла всегда должен иметь порядковый тип (то есть он не может иметь тип реал В операторе for указаны его начальное и конечное значения. Начальное значение не обязательно равно 1 ! При первом выполнении тела цикла = 1, при втором — N 2 и т. д. При последнем выполнении тела цикла = 10. Каждый раз перед выполнением тела цикла текущее значение сравнивается с конечным. После каждого выполнения тела цикла переменная N увеличивается на 1. Блок-схема алгоритма представлена на рис. 6.1.

Рис. 6.1. Блок-схема организации цикла в примере 6. Л

Как только N превысит конечное значение, выполнение цикла прекращается. Считается, что после окончания цикла переменная цикла не определена (то есть в разных реализациях языка Паскаль она может принимать разные значения). Иными словами, непра-


ви.льно считать, что после окончания цикла переменная-счетчик цик.ла имеет какое-то определенное значение.

Крайне не рекомендуется внутри цикла самостояте.льно менять счетчик цикла, особенно в сторону уменьшения. Это может привести к «зацик.ливанию» программы (бесконечному повторению те.ла цик.ла).

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

Счетчик может изменяться с шагом —1. Это вторая форма оператора for (for ... downto ... do)

Пример 6.2. Вывод на экран кубов чисел от Л 1 до 5

Program Test2: var

М : jnteger: begj п for               downto 5 do           Счетчик изменяется с ШДГОМ -1 }

wrj :                   Эта строка — тело цикла ; оно выполняется 8 раз, так как изменяется от 11 до 5 с шаг ОМ -1 }

wrjte]n; { Этот оператор нужен , чтобы закончить вывод чисел в одну строку }

read]n

Урок 6.2. Применение циклов со счетчиком

Можно организовать выполнение одного цикла внутри другого. В этом с.лучае раз.личают внешний и внутренний цик.лы — например, когда при каждом значении счетчика внешнего цик.ла нужно неско.лько раз выполнить какое-то действие (внутренний цик.л). Счетчик внешнего цикла изменяется медленнее, чем счетчик внутреннего.

102

цикл в цикле

Рассмотрим задачу вывода последовательности пар чисел:

       1       1

       1       2

       1      

1            4

2            1

       2        2

       2       

2       4 З          1 З          2

       З       З

       З       4

Блок-схема алгоритма решения задачи показана на рус. 6.2.

Рис. 6.2. Блок-схема алгоритма с вложенными циклами. выводящего последовательность из примера 6.2

Пример 6.3. Использование цикла в цикле

Program Test3 var

<, М: jnteger:

ЬедЈ п foc to 3 do for to 4 do

wrjte1n(k, '{ Пробел в апострофах между К и М нужен для того, чтобы эти числа не сливались друг с другом }

read]n

Для каждого значения переменной К переменная М меняется от 1 до 4. Нетрудно подсчитать, что в этом случае оператор wrj te] п выполнится 12 раз.

Задание 6.1. Вывести на экран 6 раз свое имя.

Задание 6.2. Вывести на экран таблицу умножения д.ля 5 чисел от 9 до 4.

Задание 6.3. Вывести на экран коды таблицы ASCII от 0 до 255 и их симво.лы. Выводить парами код и символ.

Трассировка

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

Пример 6.4. Вычисление суммы чисел от 6 до Л О

Program Test4; var

N Ј nteger:

{ Это будет счетчик цикла for }

      Ј nteger:

begj п

{ В этой переменной будем накапливать сумму }

{ Первоначально обнулим сумматор }

             for N          to 10 do

{ эта строка — тело цикла . При его выполнении каждый раз к S прибавляется очередное N. Переменную S можно сравнить с аккумулятором, в котором накапливается сумма }

1 04

wrjte1n( ' Сумма чисел read1n

Блок-схема алгоритма решения задачи показана на рус. 6.3.

Рис. 6.3. Блок-схема алгоритма вычисления суммы чисел от 6 до 1 0

Д.ля проверки правильности работы программы рекомендуется пошагово отслеживать изменение всех переменных пос.ле выпо.лнения каждого оператора программы. Такой процесс называется трассировкой. Продемонстрируем этот прием (таб.л. 6.1 ).

Таблица 6.1. Трассировка программы из примера 6.4

Оператор

Условие

N

 

Примечание

 

 

 

 

 

for        6 to 10 do

да

6

 

 

 

 

6

For      6 to 10 do

да

7

 

 

 

 

13

6+7 = 13

For      6 to 10 do

да

8

 

 

 

 

21

13+ 8 - 21

For      6 to 10 do

да

9

 

 

Оператор

Условие

N

 

Примечание

 

 

 

зо

21+9-30

For N:= 6 to 10 do

да

10

 

 

 

 

 

40

30+ 10-40

For      6 to 10 do

нет

11

 

 

writeln('CYMMa чисел ', 5:3)

 

 

 

На экране: Сумма чисел = 40

В резу.льтате работы программы на экране получим число 40.

Д.ля операторов, выпо.лняющих проверку условий (Ј f, for и т. п.) в сто.лбце «Ус.ловие» принято указывать результат проверки. В данном с.лучае в цик.ле for проверяется условие продолжения цик.ла. Симво.лы «???» подчеркивают, что значение счетчика цик.ла по выходе из цик.ла считается неопределенным.

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

Вычисление суммы ряда

Рассмотрим задачу вычисления суммы ряда:

                         1          1          1          1          1

1 х 1 2х2 3х3 4х4 5х5

Здесь мы имеем ряд дробей, у которых в знаменате.лях зашлсаны квадраты чисел от 1 до 5.

Рассмотрим каждую дробь как произведение двух дробей, например:

                                          1       1    1

В общем виде это можно записать так:

                                         1        1     1

106

Б.лок-схема алгоритма решения задачи представ.лена на рис. 6.4.

Рис. 6.4. Блок-схема алгоритма вычисления суммы ряда

Пример 6.5. Вычисление суммы ряда

Program Test5: var


     N   Ј ntegep:

S реал

А реал

begj п

for N           5 do begjn

это будет счетчик цикла for }

3 этой переменной будем накапливать сумму. Выбрали для нее тип реал , так как искомая сумма будет нецелым числом }

для записи очередной дроби 1/N (тоже число не целое) }

Первоначально обнулим сумматор }

эти строки             тело цикла . 3 цикле необходимо выполнить два оператора, поэтому объединяем их в блок ЬедЈп


S:=S + А*А; end, wrjte1n( 'CYMMa чисел S : 6:4), read1n

Задание 6.4. Написать программу вычисления п! (факториал числа п), где п — целое неотрицательное число.

Определение факториала:

0! 1!

= 1 - 2

= 1 - 2 . з

п! = 1 - 2 - 3 . .

Другими словами, п! — это произведение первых п натуральных чисел.

Каждый следующий результат (обозначим его Р) получается путем умножения предыдущего результата (предыдущего Р) на счетчик, который пробегает значения от 1 до п. Обозначим значение счетчика буквой К. Получаем общий вид выражения: Р = Р • К (то есть воспользуемся рекуррентной формулой вычисления факториала: п! (п 1)! • п).

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

Задание 6.5. Написать программу вычислен»ш суммы ряда S = 1 + 2 + З +4 + 5 + 6. Нарисовать блок-схему и запо.лнить таб„лицу трассировки. Убедиться при трассировке, что сумма равна 21.

Таблица 6.2. Заготовка для таблицы трассировки алгоритма из задания 6.5

Оператор

Условие

 

К (счетчик)

Примечание

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 08

Задание 6.6. Написать программу вычисления суммы ряда д.ля п с.лагаемых (п вводится с клавиатуры):

                     1              1              1              1

1х2х3 +2х3х4 3х4х5 4х5х6

Задание 6.7. Используя возможности модуля Crt д.ля работы с экраном в текстовом режиме, написать программу, которая 16 раз меняет цвет экрана и выводит любой текст на новом фоне в центр экрана.

Пояснение: разумно, если цвет фона и параметр цик.ла будут одной переменной (палитра цветов изменяется в диапазоне 0— 15).

Задание 6.8. Используя возможности модуля Crt, напишите программу, в которой символ «звездочка» ( * ) пробегает по всему периметру экрана из верхнего левого угла.

в программе организуйте 4 цикла. В качестве счетчика испо.льзуйте координаты Х и У. Нарисуйте б.лок-схему алгоритма.

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

Задание 6.9. По экрану разбросайте 1000 звездочек в с.лучайном месте случайным цветом с небольшой задержкой. Не забудьте инициализировать датчик случайных чисел в начале программы — один раз! Нарисуйте блок-схему алгоритма.

Выводы

1.     Для организации многократно повторяющихся действий с заранее известным числом повторений используется оператор цикла for.

2.     Счетчик цикла всегда имеет порядковый тип.

З. Счетчик цикла изменяется с шагом +1, если оператор 1,ьмеет форму for  do


Контрольные вопросы

4.     Счетчик цикла изменяется с шагом —1, если оператор 1,ьмеет форму for  downto          do

5.     Чтобы узнать, сколько раз выполнится тело цикла for, нужно найти разность между крайними значениями счетчика (по модулю) и прибавить к результату 1.

6.     Не рекомендуется изменять счетчик цикла в теле

7.     Ес.ли внутри цикла fop поставить еще один цик.л for, то ко.личество раз, которое выполнится тело внутреннего цик.ла, равно произведению числа повторений внешнего цикла на чис.ло повторений внутреннего.

8.     Д.ля проверки правильности работы алгоритма его выпо.лняют вручную, шаг за шагом, отслеживая изменения всех переменных. Это называется трассировкой.

Контрольные вопросы

1.     Какой оператор нужно использовать, чтобы вывести в каждой строке экрана слово «Привет»?

2.     Чем отличаются формы to и downto оператора for?

З. Переменные какого типа должны использоваться в качестве счетчика цикла fop? 4. Сколько раз выполнится тело внутреннего цикла: for   to 6 do for        downto З do wrjte1n( ) •

5.     Предположим, некоторая написанная программа выдает странный результат. Вероятно, программа написана с ошибкой. Как понять, где содержится ошибка?

6.     Требуется последовательно присвоить переменной N значенј,ш всех трехзначных чисел. Напишите оператор, присваивающий переменной нужные значения.

ТЕМА 7 циклы с условием

Цикл со счетчиком for, рассмотренный в предыдущей теме, отлично выполняет свои функции, когда число повторений тела цикла известно к моменту его начала (или известны начальное и конечное значения счетчика, что, впрочем, то же самое). Однако такая «радужная» картина встречается в программировании далеко не всегда. Часто приходится решать задачи, когда число повторений цикла неизвестно и определяется лишь постепенно, после некоторого количества повторений тела цикла. В этом случае применяют другую разновидность цикла — цикл с условием. В языке Паскаль циклов с условием предусмотрено два: условие цикла может проверяться перед телом цикла или после него.

Урок 7. 1 . цикл с предусловием

В первой разновидности цикла условие проверяется перед выполнением тела цикла. Поэтому данное условие правильно будет назвать условием продолжения щкла. Цикл такого вида называется циклом с предусловием.

Цикл будет повторяться до тех пор, пока проверка этого условия будет давать результат «истина» (tpue), то есть пока условие выполняется. Если условие сразу оказывается ложным, цикл не будет выполнен ни разу.

Описание цикла с предусловием

Запишем цикл с предусловием на языке блок-схем (рис. 7.1 ).


Тема Циклы с условием

Рис. 7.1. Блок-схема цикла с предусловием

Вот как этот тип цикла реализуется на языке Паскаль:

Nhj1e <лопическое условие> do <оператор-тело цикла>

Так же как при использовании цикла for и оператора Ј f после служебного слова do предполагается только один оператор.

Если в теле цикла нужно выполнить несколько операторов, оно оформляется как блок ЬедЈ п end.

 

9]

ЗАПОМНИТЕ!

Поспе спужсбных снов then, e1se, do (в операторах jf, for, whj 1 е) Эопжен стоять топько оЭин оператор! 8спи неоохоЭито выпопнить нескопько операторов, они Эопжны быть взяты в операторные скооки (переэ операторами нужно поставить ЬедЈ п, поспе — end).

[Точка с запятой не ставится ни переэ спужебныгш словами then, е 1 se, do, ни поспе ню.

Приближенное вычисление суммы бесконечного ряда

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

                          2            З

С ТОЧНОСТЬЮ Е.

Считается, что нужное приближение получено с заданной точностью Е (эпсилон), если вычјмс.лена сумма неско.льких первых с.лагаемых, и очередное слагаемое оказалось по моду.лю меньше, чем данное малое положительное число Е. Тогда считается, что это и все пос.ледующие слагаемые можно уже не учитывать.

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

Ес.ли уже получено           )!, то для вычисления xi/i! достаточно умножить предыдущий результат на x/i.

Б.лок-схема алгоритма решения задачи приведена на рис. 7.2.

Рис. 7.2. Блок-схема алгоритма приближенного вычисления суммы ряда (пример 7.1)

                 TeMa    I-lhKJ1bl c ycnower.n

npmmep 7.1 . rlpm6nmxeHHoe Bblqmcnel-we CYMMbl

6eCKOHeHHO y6blBa}01-uero pua

Program Summer2; var

Eps : real ,

  3anaHHoe 9VlCJIO  

X  real ,

OCHOBaHVle c TeneHb,l B 9b,lCJUdTene npoöh }

S  real ,

B 3ToVl nepeMeHHoVl 6yneM Ha«annnaTb cyMMY }

Y  real ,

XPaHeHb•19 ogepenHoro cnaraetqoro }

i nteger ;  gmcna waroB } begi n write( ' BBenb•lTe X VI Epsi lon : ' ) ; readln(X, EPS) ,

 nepwe cnaraeme }

 

IIOIOHVIM

B CYMMaTOP nepBoe cnaraeme

06HYTWlM

 war 0B }

whi le

Eps do {

Ilo«a n06aBneHHoe cnaraeme

He Mel-I b l_ne

C9VlTaeM cyMMY. cpaay

                                                                                                      He Bb1110JIHVlTCf               pagy!

             begin                Haganocb Teno Ub,lKJ1a }

                    inc(i ) ,          Bblgmcrwlfih HOMep  war a }

[locgmanvl HOBOe cnaraeme } cyMMY Ha cna raetqoe }

                    end;                                      KOHeu onmcaHb,19 Tena UVlKJ1a .

Ilocne 3ToVl c TPOKb,l KOM11blOTep nepeVlneT

Ha onepaTop whi le cpaBl-lel-lVlf 3 IICL'ITIOH" C T OJI b IKO n06aBneHHblM ClaraeMblM }

{ Теперь выведем результат на экран } wrjte1n( 'Cyyrqa чисел S : 6:4) : read1n

Задание 7.1. Вычислить сумму ряда:

      1        1

        12      32       52          72

Вычис.ления прекращаются по тому же условию, что и в примере 7.1.

По.лученная сумма должна быть близка к числу л 2/8.

Возведение числа в указанную целую степень

Рассмотрим задачу: возвести число а, введенное с клавиатуры, в степень п.

Задачу будем выполнять за п + 1 шаг. Например, возведем число 2 в степень З (2 3 ): 0 шаг: 2 0

1 шаг: 2 1 = 2 0 • 2 (1 • 2) 2 шаг: 2 2 = 2 1 • 2 (2 • 2)

З шаг: 2 3 = 2 2 2 (4 • 2)

Пример 7.2. Возведение числа а. введенного с клавиатуры. в степень п


Program Stp:

var Р : реалПеременная, которая хранит результат очередного шага }

N jntegep:  Показатель степени }

        Ј ntegep:         Счетчик числа шагов }

А       реалОснование степени } begj п wrjte( Введите основание степени: ' ) : read1n(A) : wrjte( Введите показатель степени: ' ) : read1n(N) :

 0-й шаг }


 20=1 }

whj 1e

begjn

do { Показатель может быть отрицательным, поэтому используем для анализа его абсолютную величину . Если показатель N-0, то в тело цикла не попадаем ни разу, так как 0-й шаг уже сделан }

Јпс(Ј);

 

Увеличиваем Ј на 1, то есть Ј теперь равно номеру текущего шага }

 Получаем результат Ј-го шага , то есть АА Ј }


{ переменной Р на данный момент получен результат для положительного N } jf then Если показатель N — отрица тельный ,

то результат должен иметь обратную величину }

wrj              ' Результат= read]n

Задание 7.2. Используя цикл с предусловием, написать программу вычисления N!.

Задание 7.3. Выполните задачу из предыдущей темы (задание 6.8), но используйте для этого цикл с предусловием. Блок-схема алгоритма вывода звездочек в верхней (2-й) строке с 3-го столбца (координата х) до 75-го столбца приведена на рис. 7.3. Продолжите блок-схему. Будьте внимательны с условиями!

Обратите внимание: нам понадобилось самим устанавливать значение х до входа в цикл и увеличивать х на 1 в теле цикла! В со счетчиком это все делалось за нас в самой конструкции цикла.

Задание 7.4. Измените в задании 7 .З в теле цик.ла шаг счетчика, сде.лав его равным З.

Задание 7.5. Проведите звездочки по диагонали из нижнего .левого уг.ла в верхний правый угол. Сначала запо.лните б.лок-схему а.лгоритма (рис. 7.4).

Пояснение: координата х изменяется быстрее, чем у, поско.льку


экран прямоугольный.

Рис. 7.3. Блок-схема алгоритма вывода звездочек во 2-й строке экрана с 3-й по 75-ю позицию

Рис. 7.4. «Слепая» блоксхема алгоритма вывода звездочек по диагонали из левого нижнего в правый верхний угол


Задание 7.6. Выведите в центр экрана с задержкой друг относите.льно друга следующие числа: 1, 2, 4, 8, 16, 32, 64, 128, 256. Вероятно, вы поняли, что это степени числа 2.

Запо.лните блок-схему алгоритма (рис. 7.5), затем напишите программу.

Задание 7.7. Введите два числа (например, А 5 и В = 8) и найдите их произведение, используя только операцию с.ложения. Запо.лните б.лок-схему алгоритма (рис. 7.6).

Рис. 7.5. «Слепая» блок-схема

Рис. 7.6. «Слепая» блок-

алгоритма вывода в центр экрана

схема алгоритма вычисления

степеней числа 2 с временной

произведения двух чисел

задержкой

с использованием только

операции сложения

Задание 7.8. Введите два числа (например, А = 45 и В = 8) и найдите частное от деления нацело первого чис.ла на второе (А на В) (в переменной К) и остаток от деления наце.ло (в переменной А), используя только операцию вычитания. Запо.лните

б.лок-схему а.лгоритма (рис. 7.7).


Пояснение: в переменной К подсчитывайте, сколько раз сде.лана операция вычитания, то есть сколько раз число В содержится в чис.ле А.

Рис. 7.7. «Слепая» блок-схема алгоритма вычисления целого частного и остатка от деления одного числа на другое с использованием только операции вычитания

Урок 7.2. цикл с постусловием

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

Цикл будет повторяться до тех пор, пока проверка этого условия будет давать результат «ложь» (fa1 se), то есть пока условие не выполнено. Даже если условие сразу окажется истинным, цикл выполнится хотя бы один раз.

Ог•исание цикла с постусловием

Блок-схема в общем виде выглядит так (рис. 7.8).

Рис. 7.8. Блок-схема цикла с постусловием

цикла продолжается, если проверка логического условия дает результат «ложь». Если логическое условие выполняется, то происходит выход из цикла. Иными словами, если в цикле whj 1 е проверялось условие продолжения цикла, то в цикле repeat untj — условие окончания.

На языке Паскаль этот тип цикла реализуется так: repeat

<тело цикла>

{ операторы 5едЈп end не требуются ! untj1 <лопическое условие>

Использование циклов repeat и while

Рассмотрим задачу, в которой требуется вводить с клавиатуры чис„ла и подсчитывать их сумму. Сумму необходимо подсчитывать до первого введенного отрицательного числа. Блок-схема а.лгоритма приведена на рис. 7.9.

Пример 7.3. Использование цикла repeat для подсчета суммы вводимых чисел до первого отрицательного числа


Program Summer1: var

sum, а : реал ,

ЬедЈ п

sum   для накопления суммы, а         для очередного числа }

sum -0,

Обнуляем сумму }

{ это тактическая хитрость

(см. замечание к примеру) } repeat

{ Добавляем введенное число к сумме } wrjte( ' Введите число: { Ввод очередного числа }

untj] а<0;                    { Проверяем введенное число на отрицательность }

{ При выходе из цикла выполняется этот оператор: 'Сунма чисел=' , sum:5:3): read]n

Рис. 7.9. Блок-схема алгоритма подсчета суммы вводимых элементов до первого отрицательного числа на базе цикла с постусловием

Испо.льзование оператора pepeat untj1 оправдано тогда, когда нужны повторяющиеся действия, от выполнения которых зависит дальнейшее продолжение цикла. Так, в приведенном примере продо.лжение цикла зависит от введенного числа. Если сразу введено отрицательное число, его не нужно добавлять к сумме. чис.ло неотрицате.льное, то нужно добавить его к сумме и продо.лжить выпо.лнение цикла. Если перевести вышесказанное буква.льно на язык б.лок-схем, алгоритм должен выглядеть так (рис. 7.10):

Рис. 7.10. Блок-схема алгоритма подсчета суммы вводимых элементов до первого отрицательного числа с выходом из цикла в середине тела цикла

Однако этот пример цикла нельзя отнести к цик.лам с пред- или постусловием. Здесь условие окончания цикла находится в середине. Хитрость примера 7.3 состоит в том, чтобы выполнить оператор S : =S+A до проверки условия окончания цикла и не нарушить прави.льности а.лгоритма. Ведь если поставить S : =S+A после ввода переменной А, то введенное отрицательное число добавится к сумме, чего быть не должно. А если поставить его перед вводом переменной А, то что же тогда прибавится к сумме во время первого шага цик.ла? Ведь переменная А еще не введена! Изначально припеременной А нулевое значение, мы решаем эту проб.лему. Вы скажете: если pepeat здесь использовать неудобно, не „лучше „ли испо.льзовать whj Ле?

Давайте посмотрим, какую программу придется написать в этом с.лучае, и сравним получившиеся два варианта:

Пример 7.4. Использование цикла whj Ле для подсчета суммы вводимых

чисел до первого отрицательного числа

var sum, а : реал : begj п

wrjte( ' Введите ЧИСЛО :

Ввод первого числа }

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

 

 

whj]e

begjn

do

 

Проверяем введенное число на отрицательность }

 Добавляем

введенное ЧИСЛО к сумме }

        wrjte( ' Введите число:      

 Ввод очередного числа }

 Обнуляем сумму }

{ При выходе из цикла выполняется этот оператор:  чисел=' , read]n

Необходимость задать начальное значение переменной А вынуждает нас повторить операторы ввода переменной А дважды — до цик•.ла и внутри него. С этой точки зрения использование whj Л е оказывается менее удобным.

Относительность выбора операторов whiIe и repeat

Со временем вы поймете, что проверка условия окончания цик.ла до и.ли пос.ле тела цикла — это вопрос исключите.льно „личных предпочтений. В данном случае, например, можно написать эту программу через whj 1 е и при этом не повторять оператор ввода:

Пример 7.5. Использование цикла whj Ле для подсчета суммы вводимых чисел до первого отрицательного числа без дублирования оператора

ввода var sum а : реал :

ЬедЈ п

sum -0,                       

Обнуляем сумму }

Используем ту же хитрость, что и в примере 7.3 это нужно, чтобы значение А удовлетворяло условию whj1e }

whj 1e а>-о do          

begjn

Проверяем введенное число на отрицательность }

        wrjte( ' Введите число:              { Ввод очередного числа }

read1n(a):

 Добавляем введенное число к сумме }

wrjte1n('CYMMa чисел ' sum:5:3): read1n

В приведенных примерах должно быть хорошо видно, наско.лько важен порядок выполнения действий внутри цик.ла. Достаточно переставить местами операторы — и программа начинает раоотать совершенно иначе. Для начинающих это самое трудное. Даже если понятно, какие операторы должны выпо.лняться в те.ле цик.ла, — то как определить, в правильном ли порядке мы их расстави.ли?

Мы рекомендуем не лениться и всегда использовать один во.лшебный метод — трассировку! Честно и аккуратно вручную несколько шагов цикла, можно понять, прави.льно „ли написана программа.

Задание 7.9. Написать программу, которая подсчитывает произведение целых чисел, введенных с клавиатуры. Произведение подсчитывается до тех пор, пока вводятся числа в интервале от — 10 до +10. Используйте цикл с постусловием.

Подсказка: в записи условия используйте логическую операцию .

Задание 7.10. Выполните ту же задачу, но с испо.льзованием цик.ла с предусловием.

Задание 7.11. Написать программу «Угадай-ка»:

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

Подсказки:

1.     Каждый раз в цикле наращивается переменная К, содержащая счетчик попыток.

2.     Для отс.леживания правильного ответа введите .логическую переменную f]ag, которой первоначально с.ледует присвоить значение Ра] se. Если ответ верен, присвойте этой .логической переменной значение True.

З. Цик•.л завершается, если значение счетчика попыток К равно 10 или если логическая переменная имеет значение Трие (использовать операцию логическое «или» — ор).

4. При выходе из цикла надо сообщить, угадано ли число или же выход 1,13 цикла произошел по совершении 10 попыток. Для этого надо проверить значение логической переменной f] ад («истина» или «ложь»).

Снача.ла запо.лните блок-схему алгоритма (рис. 7.11 ).

Задание 7.12. Введите два числа (например, А = 5 и В = 8) и найдите их произведение, используя только операцию сложения. Нарисуйте блок-схему алгоритма, используя цикл с постусловием.

Задание 7.13. Введите два числа (например, А 45 и В = 8) и найдите частное от деления нацело (в переменной К) и остаток от де.леш,ш нацело (в переменной А), используя то.лько операцию вычитания. Нарисуйте блок-схему алгоритма, испо.льзуя цик.л с постус.ловием.

Рис. 7.1 1 . «Слепая» блок-схема алгоритма «Угадай-ка» (см. задание 7 Л 1)

Задание 7.14. Перед вами блок-схема алгоритма подсчета количества десятичных разрядов в заданном положительном числе N (рис. 7.12). Заполните таблицу трассировки и докажите, что в переменной К мы действительно получаем количество разрядов (в нашем случае их 4).

Таблица 7.1. Таблица для трассировки алгоритма. приведенного на рис. 7.12 (задание 7.14)

Оператор

Условие

 

 

Примечание

Ввод N

 

1024

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 7.12. Блок-схема алгоритма подсчета количества десятичных разрядов в заданном положительном числе N

Напишите программу и проверьте алгоритм д.ля других значений N

Задание 7.15. Перед вами блок-схема алгоритма подсчета суммы десятичных разрядов в заданном положите.льном чис.ле N (рис. 7.1 З). Заполните таблицу трассировки и докажите, что в переменной S мы действительно получаем сумму разрядов (в нашем

с.лучае сумма равна 14).

Таблица 7.1. Таблица для трассировки алгоритма. приведенного на рис. 7.13 (задание 7.1 5)

Оператор

Условие

 

 

Примечание

Ввод N

 

4235

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 7.13. Блок-схема алгоритма подсчета суммы десятичных разрядов в заданном положительном числе N

Напишите программу и проверьте алгоритм для других значений N.

По.лезным примером цикла repeat       untj яв.ляется испо.льзование биб.лиотечной функции <eyPressed.

<eyPressed — библиотечная функция (из моду.ля Crt), которая, отработав, возвращает в программу результат в виде „логиче-


Контрольные вопросы

ского значения. Первоначально результат равен Ра Л se. Как то.лько будет нажата любая клавиша на клавиатуре, возвращаемый резу.льтат станет равным Трие. Таким образом, цикл вида repeat untj <eyPressed будет выполняться, пока не будет нажата какая-.либо к.лавиша.

Обычно это используется для получения задержки в программе — например, для просмотра результата на экране. Те.ло цик•.ла пусто, цик.л сводится только к проверке условия keyPressed.

Выводы

1.     Д.ля организации многократно повторяющихся действий с неизвестным числом повторений используется оператор цик.ла с предусловием:

whj]e <логическое условие> do <опердтор>

2.     Выпо.лнение цикла whj ] е прекращается, как то.лько .логическое ус.ловие примет значение fa se.

З. Цик.л с предусловием (whj ] е) может не выпо.лниться ни разу.

4.     Для выпо.лнения той же задачи служит цикл с постус.ловием: repeat

<опин или несколько операторов> untj] <логическое условие>

5.     Выполнение цикла repeat прекращается, как только логическое условие примет значение true.

6.     Цик•.л с постусловием (repeat) будет выполнен хотя бы один раз. 7. При испо.льзовании нескольких операторов в теле цик.ла реpeat untj] операторная скобка (begjn ... end) не нужна, так как пара repeat untj Л сама является операторной скобкой.

Контрольные вопросы

1.     В каких случаях предпочтительнее использовать оператор цик.ла for, а в каких — операторы цикла с условием?

2.     Чем проверка условия выполнения цикла whj Ле отличается от проверки в цикле pepeat untj1?

З. Что будет на экране в результате выполнения с.ледующих фрагментов программ? (Переменные описаны д.ля всех фрагментов программ.) var

Ј ntegep : begj п

{ Фрагмент 1 }

whj]e К<>1 do begjn

К :=К djv 2 end:

{ Фрагмент 2 } repeat

untj] К>30:

{ Фрагмент 3 } for Ј :=1 to 5 do

{ Фрагмент 4 } for downto 4 do wrjte(j :З) :

{ Фрагмент 5 } for to 79 do

ЬедЈп

gotoxy(x, 15) : wrjte( ' * ' ) end,

ТЕМА 8

Массивы — структурированный тип данных


В предыдущих уроках мы с вами рассматривали задачи, в которых использовалось небольшое количество данных. Для каждого мы создавали отдельную ячейку памяти с уникальным именем. Однако зачастую приходится работать с большим количеством однотипных данных. Для каждого из них требуется отдельная ячейка памяти — это понятно и естественно (два литра воды не поместятся в один стакан). А вот указывать для каждой ячейки отдельное имя — неудобно. Как быть?

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

Урок 8. 1 . Хранение однотипных данных в виде таблицы

Массив — совокупность однотипных данных, хранящихся в последовательных ячейках памяти и имеющих общее имя. Ячейки называются элементами массива. Все э.лементы пронумерованы по порядку, и этот номер называется инДексом элемента массива.

Все э.лементы массива имеют один и тот же тип. Сам массив при этом имеет имя — одно для всех элементов. Д.ля обращения к конкретному элементу массива необходимо указать имя массива и (в квадратных скобках) индекс элемента.

Простейший вид массива — одномерный массив (рис. 8.1 ).

1 2 3 4 5 6 7 8

Рис. 8.1 . Изображение одномерного массива в виде строки

А — имя массива, числа в клетках таблицы — элементы массива. Рассмотрим запись АГЗ] -8. В этой записи:

*                     А — имя массива;

*                     3 — номер э.лемента массива (индекс);  АГЗ1 — обозначение 3-го элемента массива;  -8 — значение 3-го элемента массива.

Основные действия по работе с массивами

Нам предстоит научиться выполнять ряд наиболее распространенных действий с массивами:

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

Описание массива на языке Паскаль

<Имя массива>: array Г<тип индекса>] of <тип компонентов>:

Здесь <тип компонентов> — это тип данных, который 1,ьмеет кткдый э.лемент массива, а <тип индекса> — границы изменения индекса.

Например:

var А: array [1. .101 of jnteger:

Здесь тип индекса — интервальный, изменяется в интервале от 1 до 10, тип данных (элементов массива) — целый.

8.

Заполнение массива случайными числами и вывод массива на экран

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

Б.лок-схема алгоритма показана на рис. 8.2.

Рис. 8.2. Блок-схема алгоритма заполнения одномерного массива случайными числами и вывода массива на экран

Пример 8.1 . Основные действия по работе с массивами Program Massjv1: uses Crt:

const { Раздел описания констант, то есть постоянных величин, определяемых в программе заранее и не ИЗМеНЯЮЩИХСЯ по ходу выполнения программы }

           { Имена констант не используются для имен переменных величин (из раздела var) }

var

А: array [1. .N1 of jntegep: { 1. .N тип индекса . для индекса выбран интервальный тип , то есть интервал целых

чисел от 1 до N, где N определено в разделе const }

j : j nteger; { Переменная , хранящая индекс элемента массива, к которому идет обращение } begj п

{ I I . Задание значений элементов массива как случайных чисел }

Randomjze; { Инициализация датчика случайных чисел }

{ Задание элементов массива : }

for Ј =1 to N do

{ переменная             изменяется в цикле от 1 до N , то есть мы по очереди перебираем все элементы массива }

А(Ј 1 •

{ 3 очередной элемент массива

АГЈ1 записываем случайное число от 0 до 99 обратите внимание: Ј номер элемента массива (принято

говорить ” индекс" ) , АГЈ1 значение элемента массива }

{ I I I . Вывод элементов массива на экран в одну строку } СЛ rScr,

Введенный массив •               for Ј    to do

{ На каждый элемент массива выделяется по 4 позиции строки , чтобы они не склеивались при выводе! }

wr1te]n: { этот ” пустой” оператор вывода отработает только один раз и переведет КУРСОР на новую строку для дальнейшей работы } read1 n


В данном примере мы заполнили массив случайными чис.лами от 0 до 99. Это обеспечила нам функция random( 100). А ес.ли нам нужно по.лучить случайные числа в другом диапазоне — например, не от нуля? Расчет нужно сделать такой: функция random(N) выдаст N раз.личных чисел от 0 до 1. Если нам нужно, чтобы наименьшим числом моего диапазона было К, значит, необходимо прибавить это К к pandom(N). Наибольшее число, которое будет выдавать в этом случае формула pandom(N) + К, будет наибо.льшим чис.лом диапазона.

Пусть, например, нам требуются случайные чис.ла в диапазоне —100..+100. Считаем, сколько различных чисел в этом диапазоне: 100 положительных, 100 отрицательных но.ль. Итого 201. Форму.ла тут проста: вычесть из большего меньшее и прибавить 1. Значит, м = 201, а К —100. То есть по.лучаем форму.лу random(201) - 100.

ЗАМЕЧАНИЕ

К сожапению, в таком виЭс формупа работать не будет — при запуске протамта выпстит с сообдснисм об ошибке. Это от «изпшинао ума», который проявпяст здесь срсЭа furbo fasca/. Осло в тот, что fltrbo fasca/ считает тип этою выражения по функдии random. она итеет тип word. Иными сповами, бонаковый. Три попытке вычесть ОО  чиспа, теньшао 100, попучасм отршјатспьный резупьтат, что fltrbo fasca/ не устраивает. Самый простой способ обойти эпу напасть — по тенять тестами уменьшаемое (pandom) и вычитаемое, то есть написать

выражения как jntegep по первому чиспу ( 100), и ошибки не возникнет.

Задание 8.1. Оформите эту программу так, чтобы задание массива и вывод его элементов на экран выполнялись в одном цикле. Вам понадобится составной оператор для тела цикла begj п end.

Задание 8.2. Добавьте в программу задания 8.1 новый цикл вывода элементов массива в обратном порядке (начиная с последнего). Попробуйте выполнить то же задание без введения нового цикла.

ЗАПОМНИТЕ!

(Иассив — это множество ячеек памяти. Лэтому пюбое Эействие с тассивот состоит в том, чтобы перебрать все эти ячейки иди, по крайней

мере, какую-то их часть. Это значит, что пюбое Эсйствие с массивами Эопжно соЭержать в себе дика, в котором перебираются эпеиенты массива. 8спи вы пишете протамму с массивом и нс написапи 1јикпа (for, whj Ле repeat) — значит, вы ошибись.

Создание пользовательского типа данных

В следующем примере массив описывается в новом разделе — разделе описания типов пользователя (type). Вы можете по-прежнему пользоваться описанием массива в разделе описания переменных (как в примере 8.1). Вариант описания массива, приведенный в этом примере, в большей степени соответствует грамотному стилю оформлен»ш программы.

Пример 8.2. Ввод с клавиатуры одномерного массива целых чисел и вывод его элементов на экран с противоположным знаком

Program Massjv2: const

N-10,

type                          Раздел описания типов переменных. эти типы определяет сам пользователь, то есть мы определяем тип одномерного массива из п целых чисел }

Mas=array (1.тип индекса ; для индекса выбран интервальный тип, то есть интервал целых чисел от 1 до N , где N определено в разделе const }

var

 

       Ljne:Mas;       

Ljne         одномерный массив, его тип определен нами как Mas }

j : jnteger;

Переменная , хранящая индекс элемента массива, к которому идет обращение }

ЬедЈ п

{ IV . ввод массива с клавиатуры }

for Ј :=1 to do                   { Обращение к элементам массива происходит в цикле, по очереди }

begjn { Начало цикла ввода элементов массива } wrjte( Введите элемент с индексом

{ Обращаемся к 1 -му элементу массива

{ Конец цикла ввода элементов массива } { Вывод элементов происходит также в цикле: }

for Ј :=1 to N do { Перебираем все элементов массива }

wrjte( -ЕЈпеГЈ1:5): { 10 элементов выводятся в строку. Выводим все элементы массива с противоположным знаком }

wrjte]n:                      После вывода массива элементов переход на новую строку }

read]n

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

             For             to N do

= -ЕЈпеГЈ1:

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

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

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

реши.л, что проще всего будет просто вывести на экран все э.лементы, начиная с последнего. Такое решение не засчитывается!

Задание 8.3. Выполнить следующие действия:

1)    создать одномерный массив А из 10 целых чисел (с помощью датчика случайных чисел);

2)    вывести массив на экран в виде строки чисел;

З) подсчитать сумму элементов массива (блок-схема а.лгоритма показана на рис. 8.3);

4) вывести сумму на экран.

Рис. 8.3. Блок-схема алгоритма вычисления суммы элементов одномерного массива

Задание 8.4. Выполнить следующие действия: 1) создать одномерный массив А из 10 целых чисел;

2) вывести массив на экран в виде строки чисел;

З) поменять местами элементы массива (блок-схема а.лгоритма показана на рис. 8.4) следующим образом:

1-й э.лемент — со 2-м;

3-й — с 4-м; 5-й — с 6-м; с 8-м;

9-й — с 10-м

(надо вспомнить, как идет обмен значениями двух переменных (рис. 2.4));

4) вывести измененный массив на экран.

Рис. 8.4. Блок-схема алгоритма обмена соседних элементов одномерного массива

Поиск максимального элемента массива

Дово.льно-таки типичная задача для большого количества данных — поиск максимума. Например, в списке успеваемости учеников к.ласса найти самого прилежного. Иначе говоря, требуется выбрать наиоольшее значение среднего балла и указать фами.лию ученика.

Пример 8.3. Программа поиска максимального элемента в массиве и его индекса (см. блок-схему алгоритма на рис. 8.5)

Program МахЈтит: const

N-10, type

Mas=array [1. .N1 of jntegep:


var

A:Mas ,

:jntegep:

{ Счетчик цикла }

Мах

:jntegep:

{ Переменная для хранения величины максимального элемента }

Imax: jnteger; { Переменная для хранения индекса максимального элемента }

begj п                                   { Тело программы }

{ Заполним элементы массива значениями датчика случайных чисел и выведем весь полученный массив на экран в одном цикле }

Randomj де ; for Ј - 1 to N do begjn

           АГЈ 1   -50+Random(101) ,

end ; wrjte1n;

{ V . поиск максимального элемента и его индекса в массиве }

Imax: { Сначала считаем , что первый элемент массива и есть максимальный }

     Мах     Аб 11 , { Его индекс и величину

записываем соответственно в переменные Imax и Мах }

           for Ј - 2 to N do             { Сравним нашего кандидата

в максимумы со всеми остальными элементами массива (со второго до последнего) } j f Мах < АГЈ 1 then { Если наш кандидат


в максимумы оказался

МеНЬИје текущего элемента .

begj n

 

 

     Мах:         

   . то будем считать теперь кандидатом в максимумы текущий элемент }

Imax :

{ Запомним его значение и индекс

в переменных Мах и Imax }


end, wrjte1n( [Максимальный элемент в массиве , мах :5) ; индекс read1n

Заметим, что в процессе поиска максимума не обязательно хранить обе величины — номер максимума и его значение. Достаточно хранить одну, в зависимости от поставленной задачи.

Рис. 8.5. Блок-схема алгоритма поиска максимального элемента массива и его индекса

Ес.ли индекс максимума не нужно знать, достаточно будет переменной Мах. Если, наоборот, нужен только номер — достаточно Imax. Тонкость состоит в том, что если нужно найти и то и другое, все равно достаточно найти только Imax, ведь значение максимума „легко может быть получено по его индексу (АГ Imax1).

Иными словами, нашу программу можно упростить с.ледующим образом:

Пример 8.4. Программа поиска максимума. не хранящая значение максимума. а запоминающая только его номер

Program МахЈтит2: const

N-10, type

Mas=array [1. .N1 of jntegep: var

: Mas :

нгах : jntegep: begj п

Randomj де: for           1 to do begjn

101) :

end • wrjte1n:

{ V . поиск индекса максимального элемента в массиве }

 2 to do jf АГЈтах1 < АГЈ 1 then

              wrjte1n( [Максимальный элемент в массиве      ,АГЈтах1 : 5) ,

read1n

Задание 8.5. Выполните поиск максимального и минимального элементов в массиве за один цикл (блок-схема алгоритма показана на рис. 8.6).

Задание 8.6. В одномерном массиве из 10 элементов опреде„лить местопо.ложение минимального элемента. Обну.лить э.лементы, стоящие до него, но не сам этот элемент. (Обну.лить — значит, записать 0 на место элемента, то есть выполнить АГЈ 1 Измененный массив вывести на экран.

Рис. 8.6. Блок-схема алгоритма поиска индексов максимального и минимального элементов массива за один цикл

Задание 8.7. В одномерном массиве из 10 э.лементов определить местопо.ложение минимального и максима.льного э.лементов. Обну.лить элементы, стоящие между ними, а также сами эти

э.лементы.

Вычисление суммы и количества элементов массива с заданными свойствами

Еще одна задача — посчитать сумму элементов, которые удовлетворяют какому-то условию. Наверное, вы уже поняли общий принцип: перебираем все элементы массива (цикл for) и про-


YPOK 8.1 XpaHeue OAHOTHIIHBIX AaHHblX B BHAe Ta6flhUbl

BepneM                                    KWK110ro 3J1eMe1--1Ta  YC.JIOBHSI (onepaTop i f).

Ec.lll,l ycJ10B1,re BblHOJIHeHO, 1106aBHM 3.J1eMeHT K CYMMe (S

npmmep 8.5. Bblqmcne}-wte CYMMbl nonoxmefibHblX 3nepneHTOB Maca,1Ba program PositivSumm; const N=IO; type Mas=array Cl . var a :Mas;

i : integer;

 

UVlKna }

S : integer;

Konun«a

CYMMVlPOBaHV19 nono)KVlTeDbHblX aneyeHTOB } begi n

{ 3a(10fiHVlM MdCCVlB cnygaVlHblMVl

B nylana30He -100. .+100 } randomi ze; for to N do begin at 1 1• :=-100+random(201) ;

end' wri tel n ;

{ llPVlCBOhM nepeEHHblM HaganbHble 31-1ageHl,lf }

aKKYMYflfTOP.

OHa 6yneT Ha«anJIVlBaTb CYMMY

Bcex nonO)KVlTeJ1bHblX areyeHTOB

HYh{H0 nPVlCBOVlTb eVl Taoe

HaganbHOe 3HageHue, 9T06bl OHO

He  Ha peaYJ1bTaT

CYMMVIPOBaHV19 . TaKVlM I-MCDOM HUI) } for i to N do { nepe6mpaeM BCe aneMeHTbl maccna }

                   if At i          then           Ilpoæpfetq                aneM+lT

Ha nonOHVITe1bHOCTb }

Ecu anemeHT n06aB19et*1 3HageH"e anemeHTa  aKKYMY"Topy }

{ выводим результат на экран: } wrjte1n( [Сумма положительных элементов read1n

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

Пример 8.6. Вычисление количества четных элементов массива (блоксхема алгоритма показана на рис. 8.7) program EvenCount: const type [Mas=appay Г 1. .N1 of jntegep: var а.•Mas:

                1 •jnteger:           { Счетчик цикла }

 { Копилка переменная для подсчета количества четных элементов } begj п

{ Заполним массив случайными числами в диапазоне +10. .+100 } randomj де: for Ј    to do begjn

end • wrjte1n:

{ Присвоим переменным начальные значения }

                                             переменная К       счетчик .

она будет выполнять ту же функцию, что и пальцы на руке при счете• каждый раз, когда будет встречаться четное число, будем загибать один палец, то есть увеличивать переменную К на е ДИНИЦУ .

Урок 8Л Хранение однотипных данных в виде таблицы

3 начале нужно присвоить ей такое значение, чтобы оно не повлияло на результат суммирования . Таким числом является ноль } for Ј to do { Перебираем все элементы массива }

jf аГЈ1 mod 2 0 then { Проверяем каждый элемент на четность, то есть проверяем, что остаток от деления этого элемента массива на 2 равен нулю }

{ Если элемент четный, увеличиваем счетчик на единицу }

{ выводим результат на экран: } wrjte1n( [Количество четных элементов read1n

Рис. 8.7. Блок-схема алгоритма вычисления количества четных элементов массива

Урок 8.2. Поиск в массиве

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

Определение наличия в массиве отрицательного элемента с использованием флажка

Рассмотрим задачу: определить, есть ли в массиве хотя бы один отрицательный элемент.

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

Данная задача может быть решена несколькими способами. Первый — самый простой — использовать флажок.

Пример 8.7. Определение наличия в массиве отрицательного элемента с использованием флажка program Search1: const N=10; type mas=array Г 1. .N] of jnteger; var a :mas; j :jnteger:

 { Флажок указывает на успешность поиска } begj п

{ Заполним массив случайными числами } randomj ze: for to N do begjn

.--2+random( 20) :

end • wrjte1n:

{ Начало блока поиска }

{ Первый вариант поиска отрицательного элемента использование флажка }


{ Изначально флажок fa1se, так как мы еще ничего не нашли }

for Ј :=1 to N do jf аГЈ then { Проверяем каждый элемент на отрицательность }

{ Если нашли отрицательный, устанавливаем флажок в состояние ”истина” } jf f] then { в конце проверяем флажок } wrjte1n( 'B массиве есть отрицательный элемент ' ) wrjte1n( 'B массиве нет отрицательных элементов ' ) ;

{ Конец блока поиска } read]n

Определение наличия в массиве отрицательных элементов путем вычисления их количества

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

Пример 8.8. Определение наличия в массиве отрицательных элементов путем подсчета количества таких элементов program Search2: const N-10:

type mas=array Г 1. .N1 of jntegep: var a :mas: j : j nteger:

f1 : jnteger: { Это уже не флажок, а счетчик количества найденных отрицательных элементов }

ЬедЈ п

{ Заполним массив случайными числами } randomj де: for Ј            to do begjn

аГЈ1:--2+random(20) :

end, wrjte]n:

{ Начало блока поиска }

{ Второй вариант поиска отрицательного элемента количество отрицательных элементов }

{ Изначально счетчик так как мы еще ничего не ндшли }

for Ј :=1 to N do jf аГЈ then Проверяем каждый элемент на отрицательность }

f]  Если ндшли отрицательный увеличиваем счетчик на единицу }

[Количество отрицательных элементов= массиве есть отрицательный элемент ' )

В массиве нет отрицательных элементов ' ) ;

{ Конец блока поиска } read]n

Нахождение номера отрицательного элемента массива

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

Пример 8.9. Определение наличия в массиве отрицательного элемента путем вычисления его номера program Search3: const ГМ-10 :

       type         mas=appay Г 1. .N1 of jntegep:

var a :mas ; j : jnteger;

{ Индекс найденного отрицательного элемента }

ЬедЈ п

{ Заполним массив случайными числами } randomj де ; for Ј           to do begjn аГЈ 1    -2+random(20) ,

end ; wrjte1n;

{ Начало блока поиска }

{ Третий вариант поиска отрицательного элемента нахождение индекса отрицательного элемента }

-0 , { Изначально флажок (индекс) - 0 , так как мы еще ничего не нашли }

          for Ј        to do

j f                                 then  { Проверяем каждый элемент на отрицательность }

{ Если нашли отрицательный, запоминаем его номер }

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

j f f1>0 then wrjte1n( ' Индекс отрицательного элемента wrjte1n( ' 3 массиве нет отрицательных элементов ' ) ,

{ Конец блока поиска } read1n

Возможно, вы уже задаетесь вполне резонным вопросом: а ес.ли в массиве несколько отрицательных элементов, то какой из мы наш.ли?

Так как при нахождении отрицательного элемента цик.л не заканчивается, ответ очевиден: последний. Этот метод находит номер пос.леднего в массиве отрицательного э.лемента.

А как нам найти первый отрицательный элемент? Один из методов (на данный момент самый легкий) — исправить цик.л так, чтобы он перебирал элементы с конца:

       for К         N downto 1 do...

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

Эти рассуждения подталкивают нас заменить цикл for, который перебирает все элементы, циклом whj 1 е, который остановится в нужный нам момент:

whj]e а(К1 >= 0 do Перебираем все не ПОДХОДЯЩИе нам

(то есть неотрицательные) элементы jnc(k): { Берем номер слепующего элемента }

Лирическое отступление. В только что приведенном примере мы испо.льзова.ли оператор inc, который раньше не упоминали. Он увеличивает значение указанной переменной на единицу. То есть оператор Ј пс (к ) аналогичен оператору К К + 1. По аналогии с -тс, в Turbo Pascal имеется еще оператор dec. Он уменьшает значение указанной переменной на единицу. Мы не приве.ли эти операторы ранее, чтобы преждевременно не забивать ваши го.ловы излишней информацией. Вы можете ими не пользоваться и продолжать писать К  К - 1, как и ранее, вместо jnc(k) и dec(k).

Рассмотренный метод поиска первого отрицате.льного э.лемента об.ладает одним серьезным недостатком: он не останав.ливается, в массиве вообще нет отрицательных элементов. Перебрав все э.лементы массива, цикл продолжит искать отрицате.льные э.лементы дальше. Это приведет или к зависанию компьютера, и.ли к аварийному завершению программы в зависимости от настроек. Исправим ошибку. Для этого добавим еще одно условие окончанј,ш поиска — «если мы перебрали все элементы».

Пример 8. 1 О. Определение наличия в массиве отрицательного элемента и вычисление его номера (если такой есть) program Search4: const N=10:

type mas=array [1. .N1 of jntegep: var a :mas; j : j nteger : { Здесь нам не нужен флажок — счетчик цикла будет нашим флажком } begj п

{ Заполним массив случайными числами } randomj ze: for Ј :=1 to N do begjn

20) :

wrj te] n :

{ Начало блока поиска

{ Четвертый вариант поиска номера первого отрицательного элемента }

 Так как используем цикл whj ]e вместо for , приходится самим заботиться о счетчике цикла }

whj ]e (аГЈ and (j<N) do { Перебираем все не ПОДХОДЯЩИе нам

(неотрицательные) элементы Кроме того, проверяем выход за границы массива }

                  Ј пс(Ј ) :        { Берем номер следующего элемента }

{ цикл закончен. Проверим, по какому из двух условий это произошло }

j f аГЈ1<0 then

{ Если элемент, на которым мы остановились , отрицательный значит, мы его нашли . нет отрицательных элементов } wrjte1n( ' Индекс отрицательного элемента wrjte1n( В массиве нет отрицательных элементов ' )

{ Конец блока поиска } read1n

Задание 8.8. Определить, есть ли в массиве по.ложите.льные четные э.лементы, и, если есть, вывести номер пос.леднего из них.

Урок 8.3. Двумерные массивы

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

Двумерный массив — это таблица из однотипных э.лементов, организованная по строкам и столбцам. Местопо.ложение каждого э.лемента двумерного массива (матрицы) определяется индексом (номером) строки и индексом (номером) столбца (рис. 8.8).

                          з                      т

Table[n,m]

Рис. 8.8. Изображение двумерного массива в виде таблицы


Двумерные массивы

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

Пример 8.1 1. Создание матрицы 5х4, вывод ее на экран по строкам program Massjv_2: const

{ Число строк }

Число столбцов } var

ТаЬЛе             array Г 1. .N, 1. .М3 of jnteger; Заказываем область пдмяти для хранения двумерного массива из строк и М столбцов }

{ Вообще говоря, нигде не определено, что первый индекс — это номер строки, а второй — это номер столбца.

Так как выводом на экран занимается программист, он сам решает, как ему удобнее.

Нам удобнее считдть, что номер строки — первый индекс, а номер столбца — второй }

jnteger:            переменные для индексов строки и столбца } begj п

{ Заполнение массива датчиком случайных чисел: } randomj ze:

for          to do for ј:=1 to М do

ТаЬ1еГЈ 100) : { Запись случайного числа в массив на место с номером строки Ј и номером столбца д }

{ вывод матрицы на экран по строкам: } for to N do begjn fop to М do

wrjte1n { Переход на новую строку после вывода всех элементов строки Ј }

end• read1n

Рис. 8.9. Блок-схема алгоритма вывода двумерного массива 5х4 на экран по строкам:

Задание 8.9. Написать программу, в которой:

+ опреде.лить матрицу З х 5;

+ вывести ее на экран;

+ определить величину максимального элемента данной матрицы и вывести на экран его значение и его позицию в матрице.

Выводы

1.     Для хранения однотипных данных используется структурированный тип данных — массив (одномерный и многомерный).

Контрольные вопросы

2.     Д.ля описания массива необходимо указать его имя, тип данных (а rray), диапазон изменения индексов его э.лементов (в квадратных скобках) и тип элементов, из которых он состоит:

mas: array Г 1. .201 of jnteger:

З. Обращение к кажедому элементу массива идет по имени массива и по индексу (номеру) элемента в массиве.

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

5.     При выполнении операций поиска в массиве нужно перебирать по очереди его элементы и проверять для каждого искомое условие.

6.     Примером многомерного массива является двумерный массив (матрица). Обращение к элементам матрицы идет по ее имени, индексу строки и индексу столбца.

Контрольные вопросы

1.     Какое условие должно выполняться, чтобы некоторое ко.личество отде.льных данных можно было объединить в один массив ?

2.     Что в записи АГ41 -12 является именем массива, что — индексом, а что — значением элемента?

З. Чем одномерный массив отличается от двумерного?

4.     Какие необходимы действия, чтобы вывести на экран все отрицательные элементы массива?

5.     Почему при поиске какого-либо элемента в массиве нельзя ооойтись без цикла?


ТЕМА 9

Вспомогательные алгоритмы. Процедуры и функции. Структурное программирование

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

Урок 9.1 . Конструирование алгоритма «сверху вниз»

При конструировании достаточно сложного алгоритма „логично разбивать его на ряд более простых задач. Построение а.лгоритма идет «сверху вниз». Сначала строится основной а.лгоритм. В нем записываются обращения к вспомогательным а.лгоритмам, которые позволят решить отдельные, более простые подзадачи (подзадачи 1-го уровня). Если есть необходимость, осуществ.ляют да.льнейшую детализацию, и подзадача разбивается на еще бо.лее простые задачи (подзадачи 2-го уровня). Вспомогате.льные а.лгоритмы для решения подзадач последнего уровня не содержат обращений к другим вспомогательным алгоритмам (рис. 9.1 ).

Рис. 9.1 . Схема алгоритма. построенного по принципу «сверху вниз» 160               Тема 9. Вспомогательные   Процедуры и функции

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

Сог.ласно концепции структурного вспомогате.льный а.лгоритм должен:

+ иметь имя, по которому его можно вызвать из других а.лгоритмов;

+ возвращать управление тому алгоритму, из которого он бы.л вызван. После того как завершится выполнение вспомогате.льного алгоритма, вызвавший его алгоритм должен продо.лжить раооту с той точки, в которой он был прерван;

+ иметь возможность вызывать другие алгоритмы; + иметь достаточно малые размеры.

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

Задача: выпо.лнить с массивом действия, которые бы.ли пред.ложены в заданиях 8.3, 8.4 (см. урок 8.1):

а) запо.лнить одномерный целочисленный массив из 10 э.лементов с.лучайными числами от —20 до +20;

б) вывести массив на экран в виде строки чисел;

в) подсчитать сумму элементов массива;

г) поменять местами элементы массива следующим образом:

1-й э.лемент — со 2-м, с 4-м; 5-й — с 6-м; с 8-м;

9-й — с 10-м;

д) вывести измененный массив на экран.

Разобьем основной алгоритм на подзадачи в порядке их перечис.ления в задании (рис. 9.2).

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


9Л .

Заполнить массив случайными числами от —20 до +20

Вывести массив на экран в виде строки чисел

Подсчитать сумму элементов массива

Поменять местами элементы массива

Вывести массив на экран в виде строки чисел

Рис. 9.2. Блок-схема алгоритма решения задачи из примера 9. Л

Такой фрагмент называется процеДурой.

Процедура оформляется по тем же правилам, что и программа. Пункты а) и г) оформим также в виде процедур.

Пункт в) — вычисление суммы элементов массива — оформим отдельным фрагментом, но с возвращением результата вычисления. Такой фрагмент называется функцией. В отличие от процедуры, из функции всегда возвращается результат в виде переменной, тип которой указывается в заголовке функции (см. пример 9.1).

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

Процедуры и функции в программе оформляют в виде отдельных блоков, кткдый из которых начинается специальным словом (procedure или functj оп соответственно). Эти блоки должны располататься в тексте программы перед оператором begj п, начинающим основную программу. В ка,ждом блоке процедуры или функции мо-

1 62            Тема 9. Вспомогательные                            Процедуры и функции

жет находиться свой раздел описания переменных (va р) до.лжна быть пара операторов 5едЈп end, между которыми пишется текст процедуры (или функции).

Внимате.льно читайте комментарии к программе!

Пример 9.1. Демонстрация процедур и функций на примере работы с одномерным массивом

Program [Massjv 1: const

type

Mas=array [1. .N1 of jntegep:

var { Раздел описания переменных.

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

            Ljne: [Mas :         { Переменная Ljne — одномерный массив

Его тип определен нами как Mas }

Sm :jntegep: { Переменная для вычисления суммы элементов }

{ Процедура ввода одномерного массива.

Внимание! После запуска программы управление передается первому исполняемому оператору из тела программы! Процедура Inp будет выполняться только после вызова ее из тела программы!

       Procedure [пр:      { Заголовок процедуры [пр }

var             { Раздел описания локальных переменных, то есть переменных процедуры Inp, действующих только в пределах этой процедуры }

j :jntegep: переменная , хранящая индекс очередного элемента массива , к которому идет обращение.

                             9.1 .                                 алгоритма

Эта переменная ј не И меет никакого отношения к глобальной переменной Ј

Хотя они имеют одинаковое название, это совершенно разные ячейки памяти 3 данной процедуре (Inp) обращение к переменной Ј будет означать локальную переменную.

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

3 данном случае это и не нужно. Если вы хотите иметь возможность обращаться к обеим переменным , нужно давать им разные имена }

ЬедЈ п { Начало тела процедуры Inp } for Ј  to  do begjn wrjte( Введите элемент с индексом

end;                            

Конец тела процедуры Inp }

{ Процедура вывода

одномерного массива на экран }

Procedure 0ut;     var

Заголовок процедуры 0ut }

   j : j nteger;      

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

begj п                          

Начало тела процедуры 0ut }

for Ј to N do wrjte(Lj wrjte1 n end; { Конец тела процедуры 0ut }

{ Процедура обмена местами соседних элементов } Procedure Change:


1 64           TeMa 9. Bcn0MoraTeJ1bHble anr0PhTMbl. npoueaypbl

var

1 : integer;

X : integer;

{ [lepeMeHHa9              BPeM+lHOro

XPaHeHV19 aneMeHTOB Maccvna

begi n

{ Hagan Tena npouenypbl Change }

{ HagarIbHoe 31-lagewe b,lHneca }

            {         06Mel-le nap anetqeHTOB Ml-IneKC V13MeHÆTCf

TaoVl war He nonneph{VlBaeT while do }

              whi le 1<10 do { Ha                  ware

6yneM MeH9Tb i -VI i+l-Vl anemeHTbI begin

IlepBblVl 3J1etqeHT     napbl coxpaHÆM B X }

Ha ero MeCTO ganVlCblBaeM BTOPOVI 3J1etqeHT napbl }

Ml-Ine«ca Ha 2 nrlf nepexona  cnenyueVl nape aneMel-lTOB } end end • { KOHeu Tena npouenypbl Change } { CYMMbl aneEl-lTOB Maccvna }

           Function Sum: integer;            { 3ar0T10BOK ØYHKUW .

Hepea LBoeT09b,le y«a3blBaeTc9

B03Bpaluaetqoro pe3YJ1bTaTa . Pe3YfibTaT B03BpaluaeTCf gepeg    ØYHKUW .

B HaweM cnygae B03BpaluaeMbIVl pe3Y1bTaT  CYMMa anemeHTOB Maccna . Tun pe3YJ1bTaTa

COOTBeTCTByeT Tb,lny

Maccna }

                             9.1 .                                 алгоритма

var j : j nteger:

S : j nteger: { Переменная для накопления суммы } ЬедЈ п { Начало тела функции Sum }

          for Ј    = 1 to N do

S + Ljne(j 1 ;

           Sum                     { Это обязательная строка !

Результат вычислений возвращается в вызывающую программу через имя функции Sum }

{ Конец тела функции Sum }

{ Начинается тело программы }

begj п               { Именно это и есть главный оператор begjn — начало всей программы } Т пр;       Вызов процещуры ввода элементов массива } 0ut ;       Вызов процекуры вывода массива на экран }

Вызов функции вычисления суммы }

 Г Сумма элементов , Sm) ; { Вывод результата на экран }

{ Результат вычисления функции необходимо присвоить переменной того же типа (как в выражении или вывести сразу на экран :

элементов: , Sum) :

            Change;         { вызов процедуры обмена элементов }

             0ut ,                     { вызов процедуры вывода массива на экран }

read1 n end . { Конец тела программы. Только здесь ставим точку } 1 66       Тема 9. Вспомогательные алгоритмы. Процедуры и функции

Задание 9.1. Написать программу, в которой выпо.лняется:

а) ввод одномерного массива А из 14 чисел (по.ложите.льных и отрицате.льных);

б) вывод массива на экран;

в) сдвиг всех элементов массива на одну поз»щию в.лево (рис. 9.3); первый элемент встает на место последнего (см. б.локсхему а.лгоритма на рис. 9.4);

х

Рис. 9.3. Схема ЦИКЛИЧеСКОГО сдвига одномерного массива влево

Рис. 9.4. Блок-схема алгоритма циклического сдвига массива влево

г) вывод массива на экран;

д) подсчет количества положительных элементов. Все пункты оформить как процедуры или функции.


Урок 9.2. Пример работы с функцией

Урок 9.2. Пример работы с функцией:

Поиск максимального элемента

Пример 9.2. Программа поиска максимального элемента в массиве

Program МахЈтит: const

ГЫ-10 , type

Mas=array Г 1. .N] of jntegep:

       var                              Раздел описания глобальных переменных }

ljne: Маз: Переменная Ljne — одномерный массив Его тип определен нами, как Mas } гг, Ј Ј nteger :

{ Функция определения индекса максимального элемента массива } functj on Maxjm:jnteger:

var                          

Раздел описания локальных переменных }

1 max: jnteger:

 Переменная для хранения индекса мдксимдльного элемента }

            Ј nteger:

 Эта переменная Ј не совпадает с глобальной переменной Ј            

begj п                      

Начало тела процегуры }

             За максимальный элемент принимаем первый элемент массива }

for Ј 2 to do { Сравним текущего кандидата в максимумы со всеми остальными элементами массива (то есть , начиная со 2-го и до последнего) }

jf Ljne[jmax1 < ЕЈпеГЈ1 then { Если кандидат в максимумы оказался меньше 1 -го

1 68                  Тема 9. Вспомогательные алгоритмы. Процедуры и функции

л тах:то считаем 1-й элемент новым кандидатом в максимумы

{ Возвращаем найденное значение, присваивая имени функции найденный индекс }

       end;                           { Конец тела функции }

      begj п                      { Начало тела программы }

{ Заполнение элементов массива с использованием датчика случайных чисел }

Randomj де: for ljne100) :

{ вызов функции поиска индекса максимального элемента массива }

wrjte1n( [Максимальный элемент в массиве индекс ' , т : 5) : read1n

Задание 9.2. Написать программу, в которой для массива из 20 элементов, заполненного случайными числами от —20 до +20, 20 раз выполняется следующая процедура: слева направо по порядку сравниваются все соседние элементы и, если первый элемент в паре оказался больше второго, элементы меняются местами. В конце процедуры массив выводится на экран в строку. Таким образом, результатом программы должны быть 20 строк, в каждой из которых большие элементы массива постепенно «сдвигаются» вправо, а меньшие — влево.

Выводы

1.     При решении сложной задачи разумно ее разбивать на подзадачи. При реализации на языке Паскаль кајждая такая подзадача (блок) оформляется в виде функции или процедуры.

Контрольные вопросы

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

З. Резу.льтат работы функции возвращается через ее имя.

4.     Раз.личают г.лобальные и локальные переменные.

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

6.     Локальные переменные действуют только внутри блока, в котором они описаны.

Контрольные вопросы

1.                  Зачем нужно создавать процедуры и функции?

2.                  Чем отличаются функции от процедур? З. Зачем нужны локальные переменные?

4. Ес.ли „локальная и глобальная переменные имеют одинаковые имена, то к какой переменной будет идти обращение?

ТЕМА 10 Как работать с символьными строками


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

Урок 1 0.1 . Работаем с цепочками символов: тип String

Д.ля работы с цепочками символов (словами и пред.ложениями) в Turbo Pascal введен специальный тип данных — Strj пд. Он чем-то похож на массив символов. Однако, в отличие от массива, со строками можно делать больше действий. Например, строки можно ск.ладывать.

Описание строковой переменной

Для работы с переменной типа String она должна быть описана в разделе var:

Strjng:

В этом с.лучае под строку S выделяется 255 симво.лов, а в памяти, соответственно, она будет представлена 255 байтами. (На самом де.ле в памяти выделяется 256 байт, но это нам сейчас не важно.

Ес.ли мы не планируем использовать такие бо.льшие строки, можно явно указать максимальный размер нужной вам строки. Например, запись S 1 Stpj пдГ401; говорит о том, что строка S 1 может содержать от 0 до 40 символов.

Основные действия со строками

Рассмотрим операции, которые можно осуществлять с данными строкового типа (ввод/вывод, присваивание, сравнение).

Пример 1 О. 1 . Основные действия с символьными строками

Program Ljne 1 :

var

Name1

Stpj пдГ201 :

{ Под строку выделено 20 символов }

Name2

Stpj пдГ201 :

 

Tjt1e

Stpj      :

{ Под строку выделено 40 символов }

Rez

ЬедЈ п

Stpj пдГ701 :

 

Name1

 ' Д. Прайс            

{ Фактическое число символов

в строке должно быть < числу символов, объявленному в разделе var для этой переменной }

Tjt1e Т1рограммирование на языке Turbo Pasca1 '

             Rez =Name1+Tjt1e:         { Строки можно складывать .

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

            wrjte1n(Rez) :               { На экране имеем :

Д.Прайс Программирование на языке Turbo Pasca1

{ К строковым переменным одинаковой длины можно применять операции ОТНОШеНИЯ (сравнения) •             первое имя : read1n(Name1) : второе имя : read1n(Name2) :

j f Name1 Name2 then wrjte1n( [Имена одинаковы ' ) : j f Name1 < Name2 then

             Урок 10.2. Функции и процедуры для работы со строками           1 73

wrjte1n( [Первое имя в алфавитном списке раньше' ) , read1n

ЗАМЕЧАНИЕ

Шакситапьная Эпина строки — 255 символов (255 байт). фактическая Эпина строки хранится в нупсвом байте этой строки (пшенно поэтому поэ строку реапьно выЭспястся на байт больше)

Задание 10.1. Написать программу, которая выводит в алфавитном порядке три введенных пользователем имени.

Урок 1 0.2. Некоторые функции и процедуры Паскаля для работы со строками

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

Использование библиотечных подпрограмм работы со строками

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

Пример 10.2. Поиск и замена Program Ljne 2: var

                Str:Strjng:               { По умолчанию для строки

 

выделяется 255 байт }

hbrd1 , Word2   

 переменные для хранения

слова -образца и слова-заменителя }

[еп : byte,

{ переменная для хранения

длины слова-образца. Тип byte для хранения целых чисел в диапазоне 0 . .255 }

Posjtjon:byte;

ЬедЈ п

Переменная для хранения позиции, с которой начинается в строке слово-образец

(его первое вхождение) }

wrjte1n( Введите

слово-образец: ' ) :

read1 n(Word1) ,

'

wrjte1n( ' Введите read1 n(Word2) ,

слово-заменитель :

wrjte1n( Введите

исходную строку: ' ) :


read1n(Str) ,

{ Функция Length возвращает длину строки hbrd1 }  возвращает номер позиции в строке Str, с которой начинается первое вхождение слова-образца Nord1. Если слово в строке отсутствует, то Pos возвращает 0 }

whj1e  do { Пока в строке Str есть слово-образец }

ЬедЈп

De1 ete(Str, ,Len) , { Процедура De1ete удаляет [еп символов из строки Str, начиная с позиции Posjtjon, то есть слово-образец }

{ Процедура Insert вставляет строку lAord2 в строку Str, начиная с позиции Posjtjon . Таким образом, она раздвигает строку }

Posjtjon { Вычисляем номер новой позиции слова-образца для следующего шага цикла whj1e } end,•

{ Функция сору выдает в качестве результата кусок строки такой длины, чему равен последний параметр, начиная с символа, номер которого указан

             Урок 10.2. Функции и процедуры для работы со строками           1 75

вторым параметром. В данном случае, результатом функции сору будет строка Str, кроме первого и последнего символов } wrjte1n(Stp): read1n

Задание 10.2. Написать программу, в которой вводится строка из слов с некоторым количеством пробелов между ними. Уда.лить лишние пробелы, оставив по одному между словами.

Работа с символами строки как с элементами массива символов

В некотором смысле можно считать строковые переменные массивами симво.лов. То есть, тип данных Strjng можно рассматривать как array [1 . . 255] 0f char. Это значит, что к э.лементам строки можно обращаться так же, как к элементам массива — по имени массива (строки) и, в квадратных скобках — номер э.лемента (номер симво.ла от начала строки). При этом у строк, как вы уже поня.ли, есть дополнительные операции по сравнению с массивами (их можно складывать, искать в них подстроки, удалять симво.лы и вставлять в них символы). Каким именно способом обращаться к символам строки — строковыми подпрограммами или как к элементам массива — выбирает программист. Например, если в программе требуется добавлять/удалять э.лементы строки, удобнее пользоваться процедурами Insert и [)еЛ ete. А ес.ли нужно просмотреть все символы строки — удобнее вспомнить, что строка также яв.ляется и массивом.

Пример 10.3. Подсчет количества скобок в строке Program Strjng_3: var

             Str : Strjng :               Возьмем строку максимальной длины

 переменные сече ТЧ ИК а ци И для подсчета числа скобок }

ЬедЈ п wrj te1n( ' Введите строку: read1 n(Stp) :

            к            0: { Изначально число скобок равно нулю }

for Ј                                                   to Length(Stp) do { Перебираем по порядку все символы строки Str }

{ если текущий символ скобка } ор (Str[j 1-' ) ' ) then

{ Увеличиваем число скобок на единицу }

read]n

Выводы

1.     Д.ля работы с массивом символов разумнее испо.льзовать тип данных Strjng.

2.     Со строками можно выполнять операции присваивания, сложения и сравнения.

З. Максимальное количество символов, которое можно хранить в строковой переменной, равно 255.

4.     Д.ля удобства работы с типом данных Strj пд рекомендуется испо.льзовать библиотечные функции и процедуры языка Паскаль.

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

Контрольные вопросы

1.     Если в строковой переменной не планируется хранить более 50 символов, как ее разумнее описать в программе?

2.     Если к строковой переменной длиной 200 символов, описанной как Strj пд, «добавить» (+) строковую переменную длиной 100 символов, какова будет длина получившейся строковой переменной?

З. Как определить длину введенной с клавиатуры строки?

4.     Как определить количество точек во введенной с к.лавиатуры строке?

5.     Как увеличить строку вдвое, дописав рядом с каждым сј,ьмволом строки такой же (например, из строки «Вася» получить «В Ваассяя»)?

ТЕМА 11

Процедуры и функции с параметрами


Все вспомогательные алгоритмы, рассмотренные нами в теме 9, были «слепыми». То есть они осуществляли свои действия независимо от каких-либо значений. Однако нетрудно было заметить, что мы пользуемся массой встроенных в Паскаль вспомогательных алгоритмов, действия которых зависят от значений, указываемых нами в скобках. Эти значения называются параметрами.

Урок 1 1 . 1 . Простые примеры использования подпрограмм с параметрами

Уже самый первый оператор, с которым мы познакоми.лись, является процедурой с параметром. Процедура в данном случае занимается выводом на экран, а параметр указывает, что именно должно появиться на экране.

Наша текущая задача — научиться самим создавать процедуры с параметрами.

Простейшие процедуры с параметрами

Пример 1 1.1. Использование процедур с параметрами для рисования на экране перекрестий и последовательностей звездочек program Рарат1:

uses Crt: { Мы будем активно использовать функции рисования на экране }

{ Процедура Staps выводит на экран указанное количество звездочек }

Л . Простые примеры использования подпрограмм

procedure : { После имени процедуры в скобках указываются ее параметры и их типы. В данном случае процедура имеет один параметр N (типа jnteger) , который указывает количество звездочек, выводимых на экран } var j : jnteger; { Раздел описания локальных переменных }

begj п for Ј =1 to do { Мы используем параметр для указания того, сколько раз нужно выводить на экран символ wrjteC*D

{ Процедура Cross выводит на экран перекрестье с центром в координатах (Х, У) }

procedure  ;  Если параметры однотипные, их имена можно перечислить через запятую } begj п

end,

{ Начало основной программы }

сл rscr:

{ Очистим экран, чтобы лучше было видно результаты работы }

Stars(10) ,

{ Вывели 10 звездочек

в начало первой строки }

gotoxy(6,20) ,

Stars(70):                 Заполнили звездочками почти всю 20-ю строку }

Вывели перекрестье в центр экрана }

Вывели перекрестье в правый верхний угол экрана } read]n

Обратите внимание: в процедурах мы используем параметры так, как будто у нас есть переменные с соответствующими именами, и они имеют определенные значения. Это приблизительно так и есть. Эти «переменные» даже можно менять. То есть вполне можно написать N N + 2, и параметр в этом месте действительно увеличится на 2. Нужно только понимать, что «время этих «переменных» такое же, как у локальных переменных — по окончании работы процедуры они уничтожаются.

Формальные и фактические параметры

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

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

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

Л Простые примеры использования подпрограмм

11ростейшие функции с параметрами

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

Пример 1 1 .2. Применение пользовательских функций с параметрами для расширения набора математических функций языка Паскаль program Param2:

{ Функция вычисления тангенса } functjon  :              Необходимо описать не только тип параметра, но и тип самой функции В данном случае они совпадают, но, ВООбше говоря, это не обязательно } begj п tg:=sjn(x)/cos(x) В дднном случае функция очень простая. Однако ее использование делает программу намного более удобной для чтения}

{ Функция вычисления числа х, возведенного в целую положительную степень п } functjon  n:jnteger) :rea] ;  Если параметры разных типов , они перечисляются через точку с запятой }

var j : j nteger :

{ Аккумулятор для накапливания произведения }

{ Начальное значение произведения-аккумулятора всегда равно единице } to п do

end;

{ Начало основной программы }

ЬедЈ п

' Тангенс числа Пи/б-' : 7:3) ;

{ Результат функции вещественный, поэтому для красивого вывода на экран применяем формат }

{ Заметьте, использование функций делает программу более наглядной }

Тангенс Пи/3 в степени 5 равен / 3) , 5) : 7 : 3) : read]n

Урок 1 1 .2. Способы передачи параметров

Среди стандартных процедур Паскаля можно найти и такие, которые изменяют само значение параметра — например Ј пс( ). Как самому создать такую процедуру?

Пример 1 1 . З. Процедура, изменяющая значения параметров program Param3: var

{ Процедура обмена двух переменных местами } procedure Change(var для указания того, что значения передаваемых переменных будут изменены в процедуре, используют служебное слово vap. Это называется передачей параметров по адресу. Параметр, имя которого используется для передаваемой переменной , называется формальным }

var 7 • jnteger: { Временная переменная для обмена } begj п

у end;


Л .2. Способы передачи параметров

{ Начало основной программы } ЬедЈ п

                        обмена           , а, '

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

[После обмена         , а, ' В read]n

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

Заметим, что при вызове процедуры, изменяющей значения параметров, в качестве фактического параметра нельзя использовать выражения. Фактическим параметром может быть только имя переменной! Это связано с тем, что в такую процедуру передается не значение, а адрес ячейки памяти, в которой хранится переменная-параметр. Если подставить выражение, то Паска.ль сообщит об ошибке — ведь выражение не имеет адреса!

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

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

Л Л .

СОВЕТ

Испопьзованис продсЭур с параметрами, переЭаюнјитися по аэресу, с одной стороны, Эобавпяст Эопопнитсльныс возможности Эпя програттирования, а с Эруюй — часто приводит к ошибкам, которые очень труЭно выповить. Изменение значений переменных при вызове продеЭур и функдий не всегда бросается в таза, особенно, сспи программа большая и ее написание занимает тнмо Эней. ЪуЭьтс вниматспьны.

Задание 11.1. Написать процедуру, которая получает целочисленную переменную и «разворачивает» ее цифры в обратном порядке — например число 1234 преобразует в число 4321.

Задание 11.2. Написать процедуру, которая получает две вещественные переменные и возвращает вместо каждой из них отк.лонение от среднего арифметического. Например, чис.ла 5 и 8 до.лжны превратиться в —1,5 и 1,5, а числа 3,2 и 0,8 — в 1,2 и -1,2.

Выводы

1.     Существует возможность создавать свои собственные процедуры и функции с параметрами.

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

З. Передаваемые параметры перечисляются в скобках пос.ле имени подпрограммы с указанием типа данных.

4.     Д.ля изменения значений передаваемых параметров испо.льзуется передача по адресу.

5.     Д.ля указания того, что передача параметров происходит по адресу, используется служебное слово var.

6.     Используя передачу параметров по адресу, будьте особенно внимательны!

Контрольные вопросы

1.     Приведите пример, когда использование процедуры с параметрами сокращает текст программы втрое.

2.     Какой из способов передачи параметров расходует больше памяти?

Контрольные вопросы

З. Можно .ли передавать в качестве параметра произведение двух переменных передаче параметров по значению? Где окажется значение произведения? Почему этого не.льзя сде.лать при передаче по адресу?

4.     Рассмотрим следующую процедуру:

procedure Maxjm(var begj п jf        then у:=х

end;

Тело программы выглядит так:

Каковы в результате будут значения переменных х у?

5.     Рассмотрим следующую процедуру: procedure  : begjn

end:

Те.ло программы выглядит так:

Каково в результате будет значение переменной х?


ТЕМА 12

Файлы: сохраняем результаты работы до следующего раза

Как вам должно быть известно из общего курса информатики, память, с которой работает Паскаль и в которой он хранит все свои данные (как и любая другая программа), называется оперативной. Она ооладает одним неприятным свойством: ее содержимое стирается при выключении питания компьютера. Чтобы информацј,ш сохранялась при выключенном питании (принято говорить «для долговременного хранения информации»), используется внешняя память. Это разного рода диски, флешки и другие виды носителей. Работе с внешней памятью из программы на Паскале и посвящена наша тема.

Урок 12.1. Как работать с текстовым файлом

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

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

Открытие файла для чтения

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

Мы начнем с самого простого — попытаемся открыть текстовый файл д.ля чтения и выведем его содержимое на экран. Д.ля того чтобы программе было что открывать, создайте в Б.локноте и.ли прямо в среде Turbo Pascal текстовый фай.л и назовите его work txt. Этот фай.л должен быть сохранен в той же папке, что и рабочие ( . раз) фай.лы с программой на Паскале. Содержимое файла нам не важно. Мы рекомендуем набрать несколько строчек текста, же.лате.льно латинскими символами.

Пример 12.1. Вывод на экран содержимого текстового файла work.txt Program Fj]e 1: var

Fj ] : text: { Описание файловой переменной для работы с текстовым файлом }

{ Паскаль умеет работать с разными типами файлов . Мы ограничимся изучением самого распространенного и понятного типа — текстового. Такой тип данных в Паскале называется text. Удобство работы с текстовым файлом состоит в том, что его можно открыть в обычном текстовом редакторе

(например, в Блокноте) и просмотреть или исправить }

Str strjng: { Переменная для чтения строк из файла } begj п

{ Перед началом работы с файлом необходимо выполнить несколько предварительных действий: }

Assjgn(Fj1 , 'work.txt' ) : { Файловой переменной Fj1 назначается конкретное имя файла (в данном случае wopk .txt) }


Урок 12.1 . Как работать с текстовым файлом

Reset(Fj1 ) : { Файл открывается с признаком ” для чтения ' Файловый указатель при этом

устанавливается

в начало файла }

{ Читаем все строки по одной из файла work .txt и выводим их на экран. При чтении из файла work.txt для определения того, что файл прочитан целиком используется признак ” конец файла“ . Этот признак проверяется библиотечной функцией Eof (end of fj]e). Пока файловый указатель не достигнет конца файла, этот признак, а также результат, возвращаемый функцией Eof, имеют значение Ра Л сое ( ”ложь") . Когда конец файла достигнут (то есть файл прочитан весь) , функция Eof возвращает результат Трие истина”) }

whj ]e not Eof(Fj] )

begjn

do Чтение файла происходит до тех пор, пока функция Eof возвращает Fa1se. Чтобы условие продолжения цикла выполнялось пока не достигнут конец файла , используется логическое отрицание not }

          ,Str) :

{ Чтение строки из файла work.txt }

wrjte1n(Str)  Вывод прочитанной строки

на экран }

end,

После окончания работы открытый файл нужно закрыть }

Открытие файла для записи

Из предыдущего примера вы, вероятно, поняли, что открытие файла для чтения пројмсходит в результате процедуры reset. При этом файловый указатель устанавливается в начало файла, и процедурами чтею,ж из файла pead1n и read можно построчно и.ли посимво.льно прочитать содержимое всего фай.ла. Ес.ли же мы хотим не читать уже имеющийся файл, а записывать в фай.л свою информацию, мы должны открыть файл для записи. Это де.лает процедура rewrjte. Если в текущей папке нет фай.ла с именем, указанным в процедуре assjgn, то создается новый пустой фай.л. Ес.ли фай.л с таким именем есть, его содержимое очищается. Так и.ли иначе, файловый указатель устанавливается в начало пустого фай.ла, пос.ле чего в файл можно записывать информацию построчно (wrj te1 п) и.ли посимвольно (wpjte).

В с.ледующем примере программа создает новый текстовый фай.л work . txt и копирует его содержимое в файл user . txt.

Пример 12.2. Создание и копирование файла

Program Fj1e 2: var

Fj 1 1, Fj1 2: text: { Описание файловых переменных для работы с текстовыми файлами }

Str                               stpj ПД: переменная для Ч Т ения строк из файла и записи в него }

{ Процедура создания нового файла }

var

ЬедЈ п

Assjgn(Fj1 1, 'wopk . txt ' )

Rewrjte(Fj1 1) : Создается новый пустой файл и открывается с признаком “ для записи” . Файловый указатель устанавливается в начало файла (которое в данном случае совпадает с концом) Файл пуст }

{ Введем с клавиатуры три любых символьных строки и запишем их в файл work.txt } for to З do begjn wrjte1n( [Введите строку и нажмите Enter: ' ) ;

read1n(Stp) :                     { Введенная строка помещается в переменную }

Урок 1 2.1 . Как работать с текстовым файлом

wrjte1n(Fj 1 1, Str) { Строка Str записывается в файл . Файловый указатель перемещается в начало следующей строки, то есть в то место, куда будет внесена следующая запись } end ;

                                { Файл закрывается .

это обязательно нужно делать после окончания работы с файлом или перед его новым открытием } end;

{ Чтение всех строк по одной из файла work .txt и запись их в файл user .txt }

Procedure сору fj 1 e;

ЬедЈ п

Assjgn(Fj 1 1, 'work .txt ' ) ,

Reset(Fj 1 1) ,                         { Файл открывается с признаком ” для чтения ' Файловый указатель при этом

устанавливается

в начало файла }

Assjgn(Fj 1 2, ' user .txt ' ) , { Файловой переменной Fj1 2 назначается имя файла, в который будет идти копирование }

Rewrjte(Fj 1 2) ,                      { Создается новый файл и открывается ” для записи'

whj 1e not Eof(Fj1 1) do { Чтение файла происходит до тех пор, пока функция Eof возвращает Fa1 se. Чтобы условие продолжения цикла выполнялось пока не достигнут конец файла, используется логическое отрицание not } begjn read1n(Fj1 1 ,Str) , { Чтение строки из файла work .txt }

wrjte1n(Fj1 2,Str) Запись прочитанной строки в файл usep.txt }

end•

C10se(Fj1

       C10se(Fj1 2)                            После окончания работы

end;

все открытые файлы положено закрывать }

ЬедЈ п

Начало основной программы

Ne\N

Вызов процедуры создания нового файла }

сору fj1e

Вызов процедуры копирования в файл }

Пос.ле работы программы откройте оба файла (это можно сде.лать в режиме Ореп в среде Turbo Pascal или в Блокноте) и убедитесь, что все получилось верно.

ЗАМЕЧАНИЕ

8спи вы ввоЭипи эти три строки по-русски, то в Тпокноте вы, виЭитс странную путаншју из русских букв. Это оттого, что fltrbo ;asca/ программа Эпя J4S-OOS, и символы, которые вы ввоЭипи, тожс быпи записаны в кодировке [['1S-OOS Чтобы [L\' но тапьно прочитать, нужно открывать эти фаты из  эм OOS (например, Norf0'1 Commandcr), ипи испопьзовапњ преобразование формата (это умеет Делать, например [4'1S Word).

Задание 12.1. Напишите программу, которая:

а) создает текстовый файл из четырех строк строчных латинских букв;

б) читает строки из созданного файла и преобразовывает в строки заглавных латинских букв;

в) после преобразования каждую строку записывает в другой созданный текстовый файл.

Проверьте результаты работы путем чтения обоих файлов!


Урок 12.2. Сохранение двумерного массива чисел в текстовом файле

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

Сохранение числовых данных

в текстовом файле

Пример 12.3. Сохранение чисел в текстовом файле

Program Fj1e 3:

var

Fj 1 :text,• begj п

Assjgn(Fj1 , 'ppjm.txt' ) : Rewrjte(Fj1):

wrjte(Fj1 „А, '         

Файловый указатель в файле ppjm.txt после этой операции будет стоять за пробелом после числа З, без перехода на следуюшую строку }

wrjte(Fj1 , В):            

При записи числа в текстовый файл записывается

его символьный код }

C10se(Fj1 )

Задание 12.2. Просмотреть полученный файл prjm. txt в текстовом редакторе.

Сохранение массива чисел в текстовом файле

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

Пример 12.4. Запись матрицы вещественных чисел 5 х 4 в текстовый файл и чтение данных из файла

program F11e 4: const

var

A:rea] :

S:char,

1 ,0 : jnteger: begj п assjgn(Fj] , 'matrjx.txt' ) : rewrjte(Fj ] ) : randomj де:


{ Запись матрицы в текстовый for I :=1 to М do begjn for to N do begjn

'

         wrjte(Fj1              

end,

wrjte1n(Fj1 )

файл }

Запишем в элемент матрицы случайное число }

Число А записывается в файл в указанном формате, за ним стоит пробел }

1-я строка массива кончилась .

Файловый указатель переводится на следующую строку в файле }

{ Чтение файла и вывод матрицы на экран по строкам Повторное использование процедуры assjgn( Fj ] )

не требуется }

whj ]e not eof( Fj ] ) do begjn whj ] e not еоЛп(РЈ Л ) do       

begj п read( Fj ] , А) ,

wrj te(A : 5 : 3) read( Fj ] , S) :  

wrj te(S ) end ; wrj te] n ;

еоЛп(РЈ Л ) возвращает статус конца строки Возвращаемый результат имеет тип ЬооЛ еап Пока не будет постигнут конец строки , цикл выполняется Функция еоЛп работает только для текстового фа Й ла ( для него имеет смысл понятие строки )

Чтение числа из текущей строки файла }

Вывод числа на экран }

Чтение пробела за числом Пробел - это символ он не может быть

считан как число в переменную А

вывод пробела на экран }

Перевод курсора на экране на следующую строку }


 { Перевод файлового указателя на следуюшую строку } end, c1 0se( Fj ] ) : read]n end

Пример 1 2.5. Чтение данных из текстового файла в двумерный массив program ReadMatrjx: const

var

F-i 1

А:аррауГ1. . М, 1  N1 of реал S : char:

1 ,0 : j nteger: begj п assjgn(Fj 1 , 'matrj x . txt ' ) ,

reset(Fj 1 ) :                { Открываем для чтения тот же файл, в который записали матрицу }

{ Чтение матрицы из текстового файла } for to М do

ЬедЈп for    to N do ЬедЈп

Читаем из файла элем еж ТЫ ПОСТРОЧНО . В переменную S считываем пробел между числами }

'

, 01

end :

выводим матрицу на экран в том же порядке }

wrj te] n ;

Переходим на следуцую

строку на экране }

read1n(Fj1 ) { Переходим на следуцую строку файла } end, с osefj1e(Fj1):

read1n

Задание 12.3. Написать программу чтения фай.ла prjm . txt и вывода чисе.л из файла на экран монитора.

Задание 12.4. Используя файл matrjx.txt, подсчитать сумму э.лементов в каждой строке и записать полученные резу.льтаты в новый фай.л rezu]t.txt.

Дописывание информации в конец файла

Процедура rewrj te, как уже было сказано, очищает все содержимое фай.ла, ес.ли он уже существует. Если же нужно дописать что-то к уже существующему файлу, используется процедура append. Она тоже открывает файл для чтения, но не очищает старое содержимое фай.ла, а устанавливает файловый указатель в конец.

Пример 12.6. Дописывание информации в конец текстового файла program Fj]e 6:

const

var

append(Fj]):                                      Открытие файла для добавления текста в конец }

wrjte1n(Fj1 , ' Конец матрицы '): { Допишем этот текст в конец записанной матрицы }

c10se(Fj1):

read1n

ЗАМЕЧАНИЕ

ДЗы, конечно, обратипи внимание, что продеЭуры вывода на экран (wrj te и wrjte1 п) имеют тс же названия, что и продеЭуры записи в файп.

[То же касается и продеЭур read и read1n. Это оттого, что экран и кпавиатура с точки зрения таскапя также явпяются файлами. Эти файпы называются соп (консопь). сто есть, кмЭа ты попьзуеися продеЭурой вывода на экран, fllrbo fasca/ на самом Эспе вывоЭит информадию в файп соп, поэ которым система понимает монитор. [74 кмЭа ты читаем информа[јию с кпавиатуры, furbo fasca/ читает информадию из файпа соп, поэ которым система понимает кпавиатуру. (Вы можете уоеЭиться в этот сати: присвойте файповой переменной или файпа соп (assjgn(Fj Л , соп ' ) ) и попробуйте открыть этот файп Эпя записи (rewrjte(Fj Л )) и записать в нао какие-нибуЭь Эанные (например, wrjte1n( ' Не] 10, wor1d! ' )). Э111[ПИ же тетоЭот можно вывоЭить из furbo fasca/ информадию на принтер. Нужно топько знать, что или файпа-принтера — 1 pt1.

Выводы

1.     Д.ля до.лговременного хранения полученной информации испо.льзуются файлы.

2.     Нами рассмотрена работа с текстовыми фай.лами.

З. Основным элементом файла типа text является строка символов ASCII.

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

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

6.     Для работы с файлом необходимо назначить файловой переменной имя файла с помощью команды assjgn и открыть его для чтения (reset) или для записи (rewrjte).

7.     Запись в файл и чтение из файла производятся процедурами wrj te/wrj te1n и read/read1n с обязательным указанием в качестве первого параметра имени файловой переменной.

8.     После окончания работы с файлом его необходимо закрыть командой c10se.


                                                                            Контрольные вопросы        1 99

Контрольные вопросы

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

2.     На что указывает файловый указатель?

З. Какие действия необходимо совершить, чтобы открыть файл для чтения?

4.     На диске хранился файл ехатр]е. txt, в котором находјллось 5 страниц текста (примерно 9 Кбайт). Программа выполнила следующие действия:

assjgn( f, 'examp]e.txt'):

             rewrjte(f) :              ' Конец ' ) ;

Как изменится количество информации, содержащейся в файле?

5.     Ответьте на предыдущий вопрос, если вместо операции rewrj te поставить операцию append.

6.     В файле хранится строка из 10 цифр (от 0 до 9), разделенных пробелом. Программа считывает их из файла следующим образом:

            for         to 9 do

ЈедЈп read(f,c) : end

Чему будет в результате равна переменная х (Ј , х: Ј nteger ; c : char :)?

ТЕМА 13

Графический режим работы.

Модуль Craph

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

В этой теме мы познакомимся с возможностями среды Turbo Pascal для работы с графической информацией.

Урок 1 3. 1 . Включаем графический режим работы

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

Особенности работы с графикой

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

639

(0,0)

(0,479)

(639,0)

х

479

Рис. 13.1. Система координат пикселов в графическом режиме работы


При испо.льзовании модуля Graph, обслуживающего графический режим, Turbo Pascal умеет работать с разрешениями экрана до 640 480 пикселов.

ВНИМАНИЕ

Ом работы программы в рафичсском режиме необходима спајиапьная программа — Эрайвср тафичсскою режима. обычной установке среды fltrbo fasca[ такой Эрайвср хранится в файпс egavga . bgj . 9скотснЭустся скопировать ао в свой текущий каталог.

Местопо.ложение этого файла можно найти в режиме Поиск и.ли поискать в каталоге BGI установки Turbo Pascal.

Левый верхний пиксел имеет координаты (0,0). Ко.личество пиксе.лов зависит от типа дисплейного адаптера и режгьма его работы. Д.ля современных компьютеров это разрешение (640 х 480) считается уже устаревшим. Но для работы с более высоким разрешением требуется современный драйвер экрана (не egavga . bgj , а svga . bgj , например). Он не входит в стандартную поставку Turbo Pascal, поэтому мы не будем его рассматривать.

Переключение в графический режим видеоадаптера

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

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

Пример 13.1. Заполнение экрана разноцветными точками

uses

              Graph, Crt:          { Подключаем модули.

Cpt нам также понадобится }

Урок 1 3.1 . Включаем графический режим работы

var

Gd,Gm: Integer: { Переменная Gd определяет тип драйвера адаптера }

{ Переменная Gm определяет режим работы адаптера ; по умолчанию выбирается старший режим (с самым высоким разрешением) byte:

begj п

Detect : { Тип драйвера адаптера определяем автоматически }

InjtGraph(Gd,Gm, { Инициализация графики . В кавычках указывается путь к программе-драйверу с расширением bgj Сейчас предполагается , что драйвер находится в вашем текущем каталоге (откуда вы запускали

Turbo Pasca1 )

Если при запуске возникнет ошибка, проверьте, где на самом деле хранится файл egavga . bgj , и укажите этот путь в качестве третьего параметра InjtGraph . Например,

If GraphResu1t <> дрок then { Если инициализация не была успешной остановка }

Ha1t(1) ,

Randomj де ;

{ на экран выводятся разноцветные точки внутри квадрата 100х100, пока вы не нажмете любую клавишу } repeat со Лор Random( 15) ,

PutPj хе1 (Random( 100) , Random( 100) , Сол ор) ,

{ Процедура PutPjxe1 перекрашивает пиксел с координатами (Х, У) в цвет С.

Мы задаем координаты случайным образом }

De1ay(10) untj1 keyPpessed:

с 1 oseGraph { эта процедура выключает графический режим и снова делает экран текстовым }

Задание 13.1. Измените программу так, чтобы: + пиксе.лы светились по всему экрану (640 ><480);

+ пиксе.лы светились в верхней половине экрана;

+ пикселы светились в нижней половине экрана (добавляйте постоянное к случайной координате).

Урок 1 3.2. Продолжаем изучать возможности модуля Craph

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

Рисование линий средствами модуля Graph

Рассмотрим пример рисования линий. Цвет рисуемой линии устанавливается процедурой SetC01 ос. Сами линии рисуются процедурой Ljne.

Пример 13.2. Вычерчивание линий в цикле

uses

Graph, Cpt: var


Урок Л 3.2. Продолжаем изучать возможности

Gd , : Integep : byte : begj п

If GraphResu1t <> дрок then

ndomj де :

{ на экран выводятся разноцветные линии (отрезки) внутри квадрата 200х200, пока вы не нажмете любую клавишу }

{ Процедура  рисует отрезок.

Начало отрезка координаты (Х1,У1) , конец отрезка координаты (Х2, У2) Координаты мы задаем случайным образом .

Процедура SetC010p(lJBeT) устанавливает цвет линий цвет мы тоже задаем случайно! }

repeat

15) ) :

                   lj                      200) , Random( 200) ,

Random( 200) , Random( 200) ) :

De1ay(10) untj1 keyPpessed: Read1n:

с 1 oseGraph

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

Задание 13.2. Измените программу так, чтобы линии выводились по всему экрану (640 х 480), все отрезки начинались бы в центре, а конец отрезков задавался бы случайно (рис. 13.2).

Рис. 13.2. Пояснительный рисунок к заданию 13.2

Задание 13.3. Измените программу так, чтобы на экране было два пучка отрезков: из левого нижнего угла и из правого верхнего угла (рис. 13.3).

Рис. 13.3. Пояснительный рисунок к заданию 13.3

Рисование окружностей средствами модуля Craph

Теперь рассмотрим, как в графическом режиме можно рисовать окружности. Этим занимается процедура СЈ рсЛ е.

Пример 1 3.3. Вычерчивание разноцветных концентрических окружностей uses

Graph, Crt: var

Сд(Ј , Gm, Р: Integer: byte :

ЬедЈ п

If GraphResu1t <> дрок then

Вы воды

Ha1t(1) : Randomj де:

{ на экран выводится 10 разноцветных окружностей } for to 10 do begjn

SetC01 16) ) :

Cj r*5) { Процедура Cj рисует окружность с центром в точке (Х, У) и радиусом R }

Read]n:

СЛ oseGraph

В модуле Graph имеется еще очень много процедур (например, eHjpse, bar, rectang]e, outtext), однако их мы предлагаем вам изучить самостоятельно.

СОВЕТ

8сди вы наберетс в реЭакторс %rbo fasca/ слово Grapl1, установите на него курсор и нажметс Ctr[+F1, то вы получите справку по продеЭураш и ФУНКДНЯШ тоЭупя Graph. ДТостарайтссь Добраться Эо 2оп1овых примеров, скопировать их в свою программу 14 поэкспериментировапљ.

Выводы

1.     Работать с монитором можно в графическом режиме. Для этого надо в своем рабочем каталоге иметь файл с драйвером — в нашем с.лучае egavga . ЬдЈ .

2.     При работе в графическом режиме изображение строится из пиксе.лов (640 х 480). Их количество (иначе говоря, разрешающая способность экрана) определяется типом адаптера монитора и его режимом работы.

З. Процедуры для работы в графическом режиме хранятся в библиотечном модуле Graph.

4.     Д.ля перек.лючения в графический режим работы испо.льзуется процедура InjtGraph. Для возврата в текстовый режим — C10seGraph.

5.     Биб.лиотечный модуль Graph имеет очень много процедур д.ля рисования различных геометрических фигур. Д.ля справки по процедурам модуля Graph нужно установить курсор на с.лово Graph в программе и нажать Ctrl+F1.

Контрольные вопросы

1.     Чем отличаются друг от друга графический и текстовый режимы работы?

2.     Из каких элементов строится изображение в графическом режиме работы?

З. Какие координаты имеет точка, находящаяся в правом нижнем углу экрана? А в центре экрана?

4.     Какие команды нужно написать, чтобы нарисовать две перелинии, идущие по диагоналям экрана?

5.     Какие команды нарисуют ряд концентрических окружностей, расходящихся из центра экрана на равном расстоянии друг от друга?


ТЕМА 14

Операторы, изменяющие естественный ход программы

Язык Паскаль задумывался как структурный язык. То есть любой алгоритм в нем можно описать в виде набора операторов условј,ш и цикла, каждый из которых можно рассматривать как отдельный блок. В блоки «вкладываются» более мелкие блоки, и т. д. Поэтому реализация программы легко осуществляется в рамках структурного программирования.

Ряд языков программирования (таких, например, как Фортран и Бейсик) не удовлетворяют свойству структурности: в них для описания алгоритма приходится использовать, например, оператор безусловного перехода (goto).

Для первых языков программирования использование безусловного перехода являлось совершенно естественным, так как сама инструкция безусловного перехода используется в каждой программе, написанной на машинном коде. Без нее нельзя реализовать такие, например, конструкции, как if...e[se.

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

Однако после была выработана идея, что использование в программе безусловного перехода сильно запутывает программу, осооенно если этот переход осуществляется «наверх», то есть возвращает нас в программе к тем операторам, которые уже выполнялись. Тезис структурного программирования призывает вовсе не использовать в программе оператор goto. Для людей, которые, вероятно, первый раз о таком слышат, это кажется вполне естественным. А вот для программистов, которые привыкли мыслить «в терминах goto», отказ от его использования был очень странен и вызывал много возражений.

. Использование оператора безусловного перехода 21 1

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

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

Урок 14.1. Использование оператора безусловного перехода goto

Споры между противниками и сторонниками goto не утихают до сих пор. Мы приведем один из основных примеров оправданного использования goto.

Рассмотрим задачу проверки элементов массива на уникальность (определить, все ли элементы массива разлущны).

Проанализируем задание и методы его решения.

Что означает, что все элементы различны? Это значит, что в массиве нет ни одной одинаковой пары элементов.

А как это проверить? Задачу проще решать от противного: постараемся найти в массиве два одинаковых элемента. не найдется, значит, все элементы различны.

Так как одинаковые элементы могут быть как угодно разбросаны по массиву, необходимо сравнить каждый элемент с каждым. То есть нужно сравнить первый элемент со всеми  (это цикл), затем второй элемент со всеми остальными (и это цикл), и так переорать все элементы. Это означает, что мы должны использовать вложенные циклы. Всего при этом у нас получится около АР сравнений (точнее, (N2 —N)/2).

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

Пример 14.1. Проверка элементов массива на уникальность program use goto:

1abe1 Копес: { Чтобы указать, в какое место программы будет переход оператором goto,


й

это место необходимо пометить . В разделе Labe1 описывается имя будущей метки, чтобы Паскаль не посчитал метку ошибочным оператором } const N-10, { Размер массива }

var  .N1 of jntegep:

Ј  { Счетчики циклов }

f1 :b001ean:

ЬедЈ п

{ Заполним массив и выведем его на экран } randomj де: for Ј  to  do begjn

end • wrjte1n:

=fa1 se,  Переменная f1 (флаг) нужна нам для того, чтобы по выходе из цикла определить, вышли мы по окончании обоих циклов или раньше }

for Ј           to М-1 do for       to do jf аГЈ1 - аГј1 then

ЈедЈп

                               goto Копес         { переходим к метке ” Копес'

Копес                  { это место программы мы и пометили как цель безусловного перехода }

j f f1 then wrjte1n( В массиве есть одинаковые элементы ' )

                      Использование оператора безусловного перехода      213

wrjte1n( Все элементы массива уникальны ' ) ; read1n

Итак, использование goto считается оправданным, если таким способом происходит выход из нескольких вложенных циклов вперед.

Справедливости ради приведем пример той же программы без использования goto.

Пример 14.2. Проверка элементов массива на уникальность без испол ьзования goto program wjthout goto:

const N-1D: { Размер массива } var  .N1 of jntegep:

{ Счетчики циклов }

f1 :b001ean:

ЬедЈ п

{ Заполним массив и выведем его на экран } randomj де: for Ј         to do begjn

end • wrjte1n:

{ Переменная f1 (флаг) нужна нам для того, чтобы по выходе из цикла определить, вышли мы по окончании обоих циклов или раньше }

{ Вместо fop приходится использовать whj1e, так как появляется еще одно условие окончания цикла


-1, whj1e (j<N) and not $1 do


{ В каждом цикле мы проверяем два условия выход за границы массива и флажок окончания поиска }

й

begjn

whj1e (j<-N) and not $1 do

ЈедЈп jf аГЈ1 - аГј1 then

end,

j f f1 then wrjte1n( В массиве есть одинаковые элементы ' )

wrjte1n( [Все элементы массива уникальны ' ) ; read1n

Программа стала чуть сложнее из-за применения while вместо for, зато мы обошлись без goto.

Урок 1 4.2. Операторы, изменяющие ход выполнения цикла

В Паскале имеются еще два оператора, действие которых напоминает действие оператора безусловного перехода. Оба они применяются для изменения хода выполнения цикла (напоминаем, что в Паскале есть три вида циклов — fop, whj Ле и repeat).

Оператор break

Оператор break прерывает действие текущего цик.ла и передает управ.ление тому оператору программы, который до.лжен выпо.лниться пос.ле окончания цикла.

Разумеется, оператор bpeak должен находиться внутри те.ла цик.ла.

Рассмотрим применение оператора break.

Пример 14.3. Вывод на экран первого по счету двузначного числа. сумма квадратов цифр которого делится на Л 7 var j :jntegep:

ЬедЈ п for Ј to 99 do jf (sqp(j mod 10) + sqp(j djv 10) ) mod 17 - 0


                 Л 4.2.                                                 ход выполнения цикла         21 5

then break: wrjte1n(j): read1n

Вместо применения оператора break можно испо.льзовать переменную-ф.лажок, по значению которой определять, нужно .ли выпо.лнять цикл далее.

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

Пример 1 4.4. Вывод на экран первого по счету двузначного числа. сумма квадратов цифр которого делится на Л 7 (без оператора break) var j : jnteger: begj п whj]e (sqr(j mod 10) + sqr(j djv 10)) mod 17 <> 0 do

read]n

Оператор continue

Оператор contjnue заканчивает выполнение текущего шага цикла. То есть после оператора contjnue сразу выпо.лнг(тся проверка условия окончания цикла. Если цикл еще должен продолжаться, начнет снова выполняться тело цикла.

Оператор contjnue, так же как и break, должен находиться внутри те.ла цгж•.ла.

Рассмотрим применение оператора contj пие.

Пример 14.5. Вывод на экран всех двузначных чисел. сумма квадратов цифр которых не делится на 17 var j :jntegep: begj п for to 99 do begjn jf (sqr(j mod 10) + sqp(j djv 10) ) mod 17 - 0 then contjnue: wrjte1n(j)

14.

read1n

Как и в случае с break, использование оператора contj пие не является обязательным.

Выводы

1.     Паскаль — структурный язык программирования. Любая программа на нем может быть составлена из блоков.

2.     Для удобства работы программистов старой школы в языке сохранен оператор безусловного перехода goto. Он позволяет выйти из блока операторов в другую точку программы.

З. Для указания той точки, куда может выйти оператор goto, используется метка — произвольный идентификатор, после которого стоит двоеточие.

4.     Идентификатор метки должен быть описан в разде.ле [abel.

5.     На одну метку может ссылаться несколько операторов goto.

6.     Для досрочного завершения выполнения те.ла цик.ла можно испо.льзовать операторы break и contjnue.

7.     Оператор break прекращает выполнение цикла. Пос.ле его выполнения программа выполняет оператор, с.ледующий после цикла.

8.     Оператор contj пие прекращает выполнение текущего тела цикла. После его выполнения программа проверяет условие окончания цикла.

Контрольные вопросы

1.    Какую функцию выполняет оператор безусловного перехода goto?

2.    Почему нежелательно использовать в программе оператор goto? З. Чем отличается действие оператора break от действия оператора contjnue?

4. В каком случае в программе удобно использовать оператор break? Можно ли без него обойтись?

ТЕМА 1 5

Группируем данные: записи


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

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

Предположим, вы собираетесь хранить в программе информацию о некоем ученике. Для этого вам нужно хранить его фамушию, имя, рост, вес, пол, номер класса. Все эти параметры имеют разный тип данных (фамилия, имя и класс — строковые, рост — целый, вес — вещественный, пол экономнее хранить как b001 еап). Можно, конечно, выделить для кткдого параметра отдельную переменную. Но тогда эти переменные не будут связаны между собой. Нам самим придется помнить, что этот вот набор переменных соответствует этому объекту, а вот тот другому! Гораздо удобнее было бы 1,ьметь некий способ объединить все эти переменные в единую конструкцию! Особенно если таких объектов (учеников) в вашей программе планируется несколько.

Вот именно о таком способе объединения данных и пойдет речь далее.

Урок 15.1 . Описание типа данных record

Урок 1 5. 1 . Описание типа данных record

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

Описание структуры данных типа запись:

record <перечисление названий и типов данных полей через точку с запятой> end;

Пример 15.1. Описание переменных типа Запись

var

Vasya , Petya: record

Мате , Famj Лу , ass : strj пдГ201 :

НеЈ ght : Ј nteger :

                                          Wejght :         :

Меп : 5001 еап end:

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

ЗАМЕЧАНИЕ

Нат бы хотепось пишний раз обратить ваше внимание на важность выбора итен переменных и типов Данных. Оействитепьность такова, что програтты чаще читают, чем пишут. частности, это связано с тем, что бопыиие протатмы разрабатывают не в оЭиночку, а туппати иноюа очень бопыиити). Естественно, при этом разные пюЭи изучают одну и ту же протатту на праист исправпения в ней ошибок ипи внесения в нее Эо попнительных функјий. Тоэтому особенно важно, чтооы ва1иа п огратта быпа сразу понятна пюбому человеку, который буЭет ее чш1тть. пя этого нужно выбрать аЭекватные и емкие по смыспу имена переменных, а также всегда писать комментарии к проратте. частности, описывать назначение испопьзуегњи переменных.

данные:

Пример 15.2. Использование переменных типа Запись var

Vasya ,Petya: { Обрабатываем данные о двух учениках } record

                            Name ,                                                   Имя ученика }

                  Famj Лу,                            Фамилия ученьна }

Название его класса } НеЈ ght : Ј nteger : Его рост } wejght:rea] : Его вес }

                 Меп :        еап                     Его пол (мужской? да/нет)

begj п

 

 

Vasya .Name:

Василий '

{ Для обращения к конкретному

полю переменной типа запись нужно написать имя переменной, точку и имя поля }

{ Работать с такой конструкцией можно как с обычной переменной read1n(Vasya .Famj Лу) :

Vasya

. НеЈ ght+5 :

Vasya

Vasya

Vasya.C]ass•. =' 10а ' :

Petya •=Vasya: Однотипные переменные типа Запись можно присваивать одну другой При этом копируется содержимое всех полей записи }

Термин ос)нотипные в этом примере означает, что переменные либо должны быть описаны в разделе var через запятую, либо необходимо объявлять в разделе type свой собственный тип данных с определенным именем и тогда эти переменные должны быть описаны как такой тип данных. Если же для каждой из них дать отдельное описание (свою конструкцию record ... end), пусть даже они будут полностью идентичны, Паскаль будет считать их принадлежащими к разным типам данных.


записи

ЗАМЕЧАНИЕ

Испопьзованис в протаммс записей аде оЭин спучай (кроте оператора case), когда копичсство операторов end в програтте не Эопжно совпаЭать с копичсством операторов ЬедЈ п.

Урок 1 5.2. Когда и как разумно использовать записи

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

Например: координаты точки на плоскости (record х , у : реа end); фами.лия, имя и пол человека (pecord пате, fj rstname strj ng(3D1 : тап:Ьоо1еап end).

Создание собственного типа данных — запись

При использовании записей очень удобно описать собственный тип данных (в разделе type) и потом использовать переменные этого типа.

Пример 15.3. Рекомендуемое описание переменных типа Запись type coord=recopd х,у: реал

реор1 e=recopd

пате , famj Лу : strj пдГЗ03 : тап : b001 еап

var роЈ ntA, роз nt3           coopd : Vasya          реор1е:

Массив записей

Описание сооственного типа данных позволяет создать массив записей. При обращении к полям элементов такого массива нужно

данные:

будет указать имя массива, индекс в квадратных скобках, точку и имя по.ля, к которому вы обращаетесь.

Пример 15.4. Описание и использование массива записей type coord=record

end,• var роЈ nts array [1. .201 of coord;

{ После служебного слова of в описании массива нельзя написать record и описать структуру записи

Можно только использовать уже описднный тип данных } Ј nteger : begj п for Ј = 1 to 20 do begj п

end

Оператор присоединения with

При работе с переменными типа Запись иногда возникает неудобство, связанное с тем, что написание этих переменных оказывается очень длинным. Если вам нужно обратиться к большому количеству полей переменной типа Запись в одном месте программы, вы можете выделить этот блок программы (операторными скобками) и указать перед ним, что к именам полей, употребляемым в блоке, нужно присоединить имя переменной типа Запись. Формат оператора присоединения:

wjth <переменная типа запись> do <оператор, для которого выполнится присоединение>

Пример 15.5. Использование оператора присоединения with type coord=record х, у: реал end: var роЈ nts аррау Г 1. .201 of coopd: Ј ntegep:

записи

ЬедЈ п for Ј - 1 to 20 do wjth pojnts[j1 do begjn

Присоединение выполняется не ко всем переменным, находящимся под действием оператора wjth, а только к тем, которые по имени совпадают с именами полей переменной в операторе wjth. В примере 15.5 pojnts[j 1 присоединяется к по.лям х и у и не присоединяется к переменной Ј.

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

Пример 15.6. Сокрытие области видимости переменных. имена которых совпадают с именами полей в операторе with type coord=record х, у: гед] end: var роЈ nts array Г 1. .2D] of coord; Ј nteger: begj п х = 10, for Ј = 1 to 20 do wjth pojnts[j1 do begj п х х*5: { 3 обеих частях оператора присваивания обращение происходит к полю  К переменной х здесь обратиться нельзя }

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

данные:

11ример выбора структуры данных

Рассмотрим задачу обработки книг, хранящихся в библиотеке.

Так как книг много, имеет смысл хранить их в массиве.

Так как параметры книги разнотипны, имеет смысл создать тип данных «книга», хранящий в себе все параметры книги.

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

Анализируем типы данных. Название, автор и издательство — это строковые данные (strj пд). Имеет смысл только огрангщить для каждого поля длину, чтобы не хранить слишком много ненужных символов. Год издания и количество страниц — целые числа (Ј nteger). Цена — количество рублей и копеек. Тут есть два варианта — хранить цену как целое количество копеек и при выводе делить на 100, чтобы получить рубли, или хрангггь цену как дробное число. Первое немного экономит память. Второе — проще. Выберем для разнообразия реал . Вот что у нас получилось:

Пример 15.7. Создание структуры данных для хранения информации о книгах в библиотеке const п=100; type book=record           Книга библиотеки tjt]estrj :  Название authorstrj пдГ251 :  Автор pub1 jshjngstrj пдГ253 :       Издательство yearЈ nteger : Год издания pagesЈ ntegep :         Количество страниц prjceреал :       цена end:

var books         аррау Г 1. . п] of book:

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

записи

писание. Иногда это занимает несколько дней, неде.ль, месяцев. Чтобы не мучиться через некоторое время, вспоминая, зачем вам бы.ла нужна когда-то переменная с именем ЈЈЈ , лучше сразу дать этой переменной осмысленное имя (по которому можно догадаться о ее назначении; например, count реор1е) и в разде.ле описаю,ш оформить ее смысл так, чтобы он сразу бросался в г.лаза:

var count реор1е : jntegep: { Количество учеников , зарегистрированных в базе данных }

Записи записей

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

Пример 15.8. Использование записей и массивов для создания сложных структур данных

{ Структура данных компьютерной игры, в которой по экрану 'ползает” несколько змеек const п 100: { Наибольшая длина змейки type роЈ nt=record { Координаты одной клетки змейки

: jntegep    Координаты точки экрана end,

.n1 of pojnt: { Все тело змейки snake record  Вся информация о змейке bodyТело змейки ength . jntegep:       длина змейки sta . jntegep: Положение головы змейки fjnjsh . jntegep        Положение хвоста змейки

end, var

 

pjtons:array [1. .51 of snake:

{ Будет 5 змеек

begj п

{ Пример обращения к координате ”у” начальной клетки 3-й змейки } рЈ tons [31 . stapt1 . у :

данные:

Выводы

1 Д.ля удобства программирования и последующего анализа программы рекомендуется структурировать данные.

2. Ес.ли в программе обрабатывается информация об объекте с неско.лькими параметрами, рекомендуется создать д.ля этого объекта отде.льный тип данных — запись — который будет хранить все параметры объекта.

З. Запись описывается парой операторов record и end, между которыми перечисляются имена полей записи с указанием их типов данных.

4.     Можно создавать массивы записей. При этом нужно обязательно создать свой тип данных — запись.

5.     Для сокращения написания переменных типа Запись при обращении к нескольким полям одной переменной используют оператор присоединения wj th.

Контрольные вопросы

1.     Зачем нужно использовать записи в программе?

2.     Какими операторами оформляются записи?

З. Опишите структуру данных, хранящую информацию о марке автомоби.ля (название, цена, максима.льная скорость, время разгона до 100 км ч, объем двигателя).

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

5.     Приведите пример обращения к полю «цена» 5-го автомоби.ля из п. 4.

6.     Как можно сократить написание имен переменных туша Запись при ооращении к нескольким полям одной переменной?


ТЕМА 16

Динамические переменные

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

В этой беседе мы рассмотрим другой способ работы с памятью — Динамический. Память при этом выделяется именно в тот момент, когда становится нужна программе, и освооождается тогда, когда больше не нужна. Этот способ более удобен с точки зрения экономии памяти, но требует больше умственных затрат и аккуратности программиста.

Урок 1 6. 1 . Выделение памяти

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

В компьютере имеется два «руководителя», которые управляют работой всего и всех — процессор и операционная система. Процессор — аппаратная часть компьютера (hardware). Он выполняет программы и управляет компьютером на уровне электрических сигналов. Операционная система — часть программного обеспечения компьютера (software). Причем самая главная ее часть. Самая главная программа, которая запускается практически сразу после включения компьютера и управляет всеми ресурсами компьютера, а также общается с пользователем (человеком). Без

Урок 16. Л . Выделение памяти

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

Любая программа, которую выполняет процессор, до.лжна находиться в основной памяти компьютера. Ее назначение — хранение программ, выполняющихся в конкретный момент времени процессором, и данных, которые эти программы обрабатывают. Это микросхемы, которые в современных компьютерах распо.ложены внутри, на материнской п.лате. Основная память разде.ляется на постоянную (ПЗУ, ROM) и оперативную (ОЗУ, RAM). Нас будет интересовать ОЗУ — оперативная память компьютера (ПЗУ нужна д.ля первонача.льного включения компьютера, в нее нельзя ничего записать).

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

Всеми этими процессами — загрузкой программы в ОЗУ, выделением памяти д.ля тела программы и для ее данных, завершением программы и освобождением памяти (и еще много чем другим) управляет операционная система (ОС). Программа как бы «просит» операционную систему выделить ей память, и ОС выделяет ее программе (если такое количество свободной памят1,1 в данный момент имеется).

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

Ес.ли операционная система однозадачная (как MS-DOS, например), программа может рассчитывать на всю оперативную память, которая имеется в данный момент в компьютере (и.ли почти всю). Если многозадачная — память распреде.ляется между неско.лькими программами.


Среда программирования Turbo Pascal написана под операционную систему MS-DOS. Ввиду некоторых особенностей работы этой ОС, ес.ли не использовать дополнительных менеджеров памяти, в своих программах на Паскале вы можете рассчитывать то.лько на 64 Кбайт (65 536 байт) оперативной памяти (именно сто.лько может адресовать 16-разрядный процессор). И хотя у вас почти наверняка памяти гораздо больше, и процессор гораздо .лучше, вы не сможете (без специальных программ) создать, например, це.лочис.ленный массив из 40 000 элементов (40 000 по 2 байта на каждый э.лемент — это больше, чем 65 536).

Ес.ли вам нужно работать с данными большего размера, имеет смыс.л подумать о другой, более современной среде программирования (например, Borland Delphi). Язык программирования тот же (Паска.ль), а возможностей больше.

Урок 1 6.2. Адреса

Для хранения информации о том, где в памяти находится место, куда вы собираетесь к.ласть свои данные, используются аДреса. Это це.лые чис.ла (в зависимости от ОС и процессора, 16-, 32- или 64-разрядные). Они соответствуют номеру ячейки памяти (байта), начиная с которого хранятся ваши данные.

Ес.ли вы написа.ли в вашей программе: var а , nteger, это означает, что вы просите выделить вам 2 ячейки памяти, достаточные д.ля хранения двух чисел типа Integer (в Turbo Pascal это 2 байта, в Delphi — 4). При этом компилятор (программа, которая переводит вашу программу с языка Паскаль на язык процессора) записывает себе в специальную таблицу относительные адреса для ячейки памяти а и для ячейки Ь.

ЗАМЕЧАНИЕ

Относитепьные потому что компипятор не знает, какие конк етно адреса памяти выЭспит Эпя вашей протагпиы опсртјионная система.

о он знает, скопько всао памяти требуется протамтс. И он запоминает в отЭепьной табпшјс, что ячейка а буЭст находиться, например, по адресу 4» относитспьно начапа бпока памяти, выЭспснною программой, а ячейка Ь — по аэресу «+6». Когда программа запустится, к аэресу начапа выделенною бпока прибавится запомненное смещение, и программа буЭет обращаться уже к конкретному аэресу ОЗУ.

Урок 16.3. Указатели

Эти адреса записаны прямо в теле программы. Места хранеш,ш ячеек а и Ь не меняются за все время ее выполнен»ш.

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

Итак, заранее предупреждаем — то, что мы сейчас расскажем, само по себе не имеет никакого практического смыс.ла. Это хорошо то.лько в с.лучае «хитрого» использования. Но чтобы научиться де.лать что-то «хитрое», нужно сначала овладеть техно.логией.

Урок 1 6.3. Указатели

Указатели на отдельные переменные

Для хранения адресов ячеек памяти используются переменныеуказатели. Как и обычные переменные, они описываются в разделе var. Формат описания:

•<имя переменой>: А <имя типд данных, на переменные которого будет указывать указатель>:

Для указания того, что переменная является указателем, перед именем типа данных нужно поставить символ А («шляпка», Shift+6 на клавиатуре). Автор Паскаля предполагал использовать для этой цели символ «стрелочка» (Т), чтобы подчеркнуть, что он на что-то указывает. Но такого символа на клавиатуре нет, поэтому используют как бы стрелочку без вертикальной палочки. Пример: var p: A jnteger:

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

ПРИМЕЧАНИЕ

С точки зрения продессора, нет никакой разншјы между адресом депочиспенной ячейки памяти и адресом пюбой Эруюй, например вещественной. ДЗО всех случаях хранснис аЭрсса требует оЭинаковос копичество байт. Но програтты с указатспями потсюјиапьно содержат в себе гораздо бопьше OlUUOOk, чем обычные. Ивтор %аскапя намеренно требует указывать, на ячейку какою типа Данных ссыпается указатель, чтобы хотя бы часть OlUUбок можно Ято «отпоить» на этапе компипяјии (эти ошибки возникают [6-367 того, что указатспи по вине программиста указывают вовсе не на 1110, на чт•о протаммист прсЭпопатст).

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

Основная идея этого заключается в динамическом выде.лении памяти. Вы ведь помните, что означает «выделение памяти»? Если память будет выдаваться нашей программе не сразу, а в какой-то другой момент времени, операционная система сообщит ей адрес выде.ленной ячейки. А его ведь нужно будет где-то сохранить, чтобы впос.ледствии к этой памяти обращаться! При статическом выде.лении памяти этим занимался компилятор. Он сохраня.л адреса всех переменных и подставлял эти адреса во все те места программы, в которых мы к переменным обраща.лись. А при динамическом выделении памяти компилятор не может нам помочь — он ведь не знает адреса, который сообщит нам операционная система. Значит, нужно самим предусмотреть место, где этот адрес можно будет хранить. Преимущественно д.ля этого и нужны переменные-указате.ли.

Для того чтобы в переменную-указатель по.ложить адрес статической ячейки памяти (а мы пока с другими работать и не умеем), используется символ @ («собачка», коммерческое А Т, Shift+2). Пример:  (если р описан как указатель на тот же тип данных, что и count, то переменная р будет содержать адрес ячейки памяти, в которой хранится переменная count).

Чтобы обратиться к содержимому ячейки памяти, на которую ссылается переменная-указатель, используется тот же символ

(«ш.ляпка»), стоящий после имени переменной-указате.ля. Пример: -10 : (по.ложить в ячейку, на которую указывает р, чис.ло 10).

Пример 16.1. Простейшее использование переменных-указателей var vasya , petya .  переменные }

                                    . A jntegep: { два указателя на jnteger            

ЬедЈ п vasya


р - @vasya:             Указатель р содержит адрес

q

р;

Указатели можно присваивать друг другу }

-15,

Переменная vasya хранит число 15, так как q указывает на ту же ячейку, что и р, д р указывает на vasya }

=qA+5;

Переменная vasya увеличилась на 5 }


Указатели на блоки переменных

К переменным-указателям применимы операции Јпс и dec. Они уве.личивают и уменьшают значение указателя. Но не на 1 (как можно бы.ло бы предположить), а на то количество, которое занимает в памяти переменная того типа данных, на который ссылается указате.ль. Это свойство бесполезно при работе с отде.льными переменными, но его можно использовать для обращения к э.лементам массива.

Пример 16.2. Использование операции приращения указателя для обращения к элементам массива var array [1. .201 of jnteger; р : A jnteger: Ј nteger : begj п

Положим в переменную-указатель р адрес первой ячейки массива }

{ Переберем все ячейки массива } for to 20 do begj п

{ В текущий элемент массива положим число }

Јпс(р) Переменная р ссылается на следующий элемент массива } end

Урок 1 6.4. Динамическое выделение памяти

В Паскале существует два способа динамического выделения памяти — простой и «продвинутый». В исходном Паскале был предусмотрен только простой способ. В современных Паска.лях доступны оба.

New и Dispose

Первый способ осуществляется парой процедур New и Dj spose. Первая выделяет память, вторая — освобождает. В обеих процедурах требуется один параметр — переменная-указатель.

Процедура New выделяет область памяти такого размера, чтобы в нее поместился объект того типа данных, на который может указывать указатель. После выполнения процедуры New указатель содержит адрес созданного объекта. Пример: New( р). Как вы понимаете, при этом происходит передача параметра по адресу. То есть параметром процедуры New может быть только переменная и не может быть выражение.

Процедура Dj spose освобождает область памяти, выделенную предварительно процедурой New для объекта, на который ссылается указатель. Пример: Dj spose(p). Указатель р должен к этому моменту ссылаться на предварительно выделенную область памяти. Если это не так — последствия процедуры Dj spose не определены. Скорее всего, ваша программа «вылетит» с сообщением об ошибке.

После выполнения процедуры Dj spose значение переменнойуказателя считается неопределенным. Попытка прочитать данные, на которые указывает указатель, или записать туда новые данные может иметь непредсказуемый результат. Хотя, скорее всего, значение переменной-указателя при этом не изменится. И сами данные не изменятся. Но эта область памяти уже не будет считаться доступной вам. Это можно себе представить, как если бы вы жили в какой-то квартире, а потом продали ее и уехали, но оставили себе при этом ключи. С одной стороны, вы имеете возможность (может быть) в квартиру войти и даже что-то из нее забрать. С другой стороны, последствия вашего поступка при этом будут непредсказуемы.

Пример 16.3. Использование процедур New и Dispose var a,vasya . jntegep: q,p : ntegep:

ЬедЈ п new(p): { Создаем новую переменную типа jnteger

Переменная р содержит ее адрес }

 

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

нельзя было бы освободить.

Необходимо следить, чтобы адреса динамических переменных не терялись! Вторая переменная хранит число 3 }

dj spose(p) :

Память, выделенная под вторую переменную, освобождается . Значение                пропадает }

Переменная р снова содержит адрес первой переменной }

 

dj spose(p) :

Освобождаем пдмять, выделенную для первой переменной }

Неосмотря на то, что переменная q все еще содержит адрес ячейки , где хранилось число 10, обращаться к этой ячейке запрещено. Она чужая для нашей программы. Трогать чужое нельзя!

 

-10, Новая переменная хранит число 10 } Запомнили ее адрес еще раз } new(p) :Создали еще одну переменную.


Вероятно, вы обратили внимание, что адреса всех ячеек памяти, которые мы выделяли процедурами New, необходимо где-то хранить. Например, выделять для каждой переменную-указатель. Но при этом получается, что для хранения в памяти переменной требуется меньше места, чем для выделения памяти для хранения указателя для нее (например, в Turbo Pascal для Integer нужно 2 байта, а для указателя — 4). В этом случае использование динамических переменных не экономјгг память, а еще больше тратит ее .

Если не использовать специальные структуры данных, которые мы будем обсуждать в следующем уроке, то динамическое выделение памяти нужно только в случае одновременного выделен»ш больших объемов памяти (преимущественно массивов).

Динамическое выделение памяти для массивов

Рассмотрим пример. В программе нам требуется использовать два массива очень большого размера. Такого, что каждый такой массив едва помещается в имеющуюся оперативную память, а сразу оба — не поместятся. Но каждый из этих массгпзов нужен нам не одновременно, а по очереди — сначала один, потом другой. Вы скажете, ну и используем один и тот же массив для разных целей! Это можно сделать, только если в них хранятся однотипные данные. Как же быть?

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

Пример 16.4. Попеременное использование большого объема памяти для хранения разных данных type of jnteger: of char:

var а masA: Указатель должен ссылаться на тип данных, имеющий имя . Нельзя написать А дргдуГ1. .30000] of jnteger; } Ь *masB: begj п

Здесь делаем какие-то начальные действия.

new(a); Выделяем память под первый массив } { Здесь работаем с элементами этого массива. для обращения, например, к пятому элементу нужно написать аАГ51 } dj spose(a): { Освобождаем память, занятую первым массивом }

           new(b) :             { Выделяем память под второй массив }

{ Здесь работаем с элементами второго массива } dj spose(b) : { Освобождаем память, занятую вторым массивом }

ЗАМЕЧАНИЕ

[Термином «Динамический массив» в настоящее время принято называть не то, что мы топько что продемонстрировали массив, поэ хранение

которого память выЭслсна Энналшчсски), а массив неопреЭеленной Эпины. %ритенение подобных массивов нами нс одобряется, и ил изучение выходит за рамки нашею курса. furbo fasca/ они нс Эопускаются и воЗМожны топько в (De/phi. Окрапнјс, такие массивы описываются, например, так: а : array 0f jnteger. Опя указания Эпины, которую нужно выЭепить Эпя ячеек тассива, испопьзуют продсЭуру set]ength. У нее первый параметр иги шассива, втором- — Эпина. Например,  , 20).

ЗАМЕЧАНИЕ

ДЗозтожно, вас Давно уже мучает резонный вопрос: зачем вообще нужно выделять память поэ массивы Эннапшчески? Нс пронзе пи заранее зарезервировать стопько памяти, скопько можно, и с ней работать?! ДЗсЭь бО(1ЬШС памяти все равно испопьзовать НЕЛЬЗЯ.

Есть некоторая поимка в этом утвержЭснии. Но Эля СОВРСМСННЫХ компьютеров Эннтиичсскос выЭслснис памяти становится все бопсс актуа(1ЬНЫШ. ДЗеЭь почпш всаЭа вы работаетс в многозадачной операдионной системе. И это значит, что одновременно с программой, которую написан вы, работает аде нсскопько программ. Например, играет тиЗыкс7, работает антивирус и из Интернета скачиваются фотографии ваших Эросй. ваша программа — обычная записная книжка, в которой вы храните нужные телефоны. Спрашивается: какой объем памяти Эопжна зарезервировать программа Эпя своей работы! ? 8спи заранее описать массив из небопьшого чисда эдементов, их может в какой-то момент не хватить. [74 сс(1и заказалп, бопьшое чиспо эпементов, во-первых, их все равно когЭа-ннбуЭь тожет не хвапшпљ, во-вторых, окажется, что бопьшой объем па,ГИПIН обычно не испопьзуется. Соыаситесь, быпо бы нс очень хорошо, если записная книжка при старте резервирована поэ себя почти всю память, и эпю тешапо бы работе Эрушх протамм! Значит, правипьнес всего резервировать спюпько па,ГИПIН, скопько нужно в настоянјш7 момент. ТонаЭобится увепнчипљ попросит бопьше.

GetMem и FreeMem

Гораздо бо.лее гибкий (и более трудоемкий с точки зрения работы программиста) способ управления динамической памятью пред.лагают нам процедуры GetMem и FpeeMem. Как и в случае с парой New-Dj spose, первая процедура выделяет память динамически, вторая — освобождает. Но в отличие от New и Dj spose, которые сами понимали, какого размера нужно выделить память и какого размера б.лок памяти нужно освободить, для процедур GetMem и FreeMem это до.лжен указать программист. Формат использования:

памяти в байтах>) , памяти в байтах>) ,

При этом переменная-указатель может быть описана как указате.ль на .люооЙ тип данных. Программист также должен заботиться о том, чтооы заказываемого объема данных хвати.ло д.ля переменных этого типа. Чтобы не ошибиться в количестве байт, обычно испо.льзуют функцию sj zeof. Ее аргументом нужно указать имя типа данных; ее результатом является число байт, требуемых д.ля хранения переменной этого типа данных.

Обычно парой GetMem-FpeeMem пользуются для динамического выде.ления памяти под массивы, длина которых заранее не известна. Так, например, для динамического выделею,ш памяти под це.лочис.ленный массив из 300 элементов разумно написать так: GetMem(a , Sj где переменная а — указате.ль на Ј nteger (а ntegep).

Обращение к элементам массива, созданного динамически

Неудобством этого способа является не очень прямой доступ к э.лементам такого массива. Ведь описав обычный массив, можно ооратиться к его произвольному элементу путем индексации: например д.ля обращения к десятому элементу массива mas достаточно написать mas Г 101. А к десятому элементу то.лько что рассмотренного массива а так просто не обратишься. Перебрать по порядку все элементы легко. Это можно сделать точно таким же способом, как в примере 16.5.

Пример 16.5. Последовательное обращение к элементам динамически созданного массива

var а р . A jntegep: Ј ntegep: begj п

GetMem(a : { Выделили память под массив }


Динамическое

{ Переберем все ячейки массива } р а, { Чтобы не потерять адрес начала массива , для обращения к его элементам используем дополнительную переменную-указатель } for Ј to 300 do begjn

 { В текущий элемент массива положим число }

Јпс(р) { Переменная р ссылается на следующий элемент массива }

 { Освободили память , ранее под массив }

Ес.ли нужно получить прямой и быстрый способ обращения к указанной ячеике такого массива, можно испо.льзовать процедуру 1 пс с двумя аргументами. Первый аргумент — переменная-указате.ль, хранящая адрес начала такого массива. Второй аргумент — на сколько нужно «сдвинуться» относите.льно первого э.лемента. Например, для обращения к десятому элементу можно написать так:

Теперь qA интересующий нас элемент.

Массив переменной длины

Эффект массива переменной длины можно создать самостоятельно, используя процедуры GetMem-FreeMem. Рассмотрим самый типичный случай — необходимо сохранить в памяти данные, вводимые с клавиатуры, количество которых заранее неизвестно. При появлении каждого нового элемента будем увеличивать размер выделенной памяти на 1. Конечно же, имеющиеся на данный момент элементы массива придется переписывать во вновь выделенную память.

Пример 16.6. Массив переменной длины


var а : A jntegep: Ь А Ј ntegep:

{ Указатель на хранимый массив } временная переменная для указателя на новый массив }


                 TeMa        AMHammqecKvte nepenneHHble

pa,pb . A i nteger ; nepetqe11EHY19 no anetqeHTaM MaCCVlBOB }

x                 i nteger;

BBonnoe yucno }

 

n                 i nteger;

Pagmep maccna }

 

                                           i nteger : begi n

      um«na }

HaganbHblVl paamep MdCCVlBa }

 

readl n(x) ;

LIVITaeM nepwe HVICJIO }

 

  whi l e        do

5yneM COXPdH9Tb gucna, no«a

 

begi n

 

BBOLVlMOe gucno He paBH0

}

i nc(n) ;

HOBa9            MdCCVlBa }

 

{ TeKY111VlVl MdCCVlB VIMeeT pagtqep Ha 1 MeHb11]e , get,l Tpe6yeTc9 . BblneJIVlM HY)KHoe HVICJIO fgeel{: } GetMem(b, si zeof(i pb:=b;  YCTaHOBVlM y«a3aTeJ1b pb

Ha nePBblVl anetqeHT MaCCVlBa b } { (leperlklluetvl BCe anemeHTbl MdCCVlBa a (n-1 111TH) B MaCCVlB b:

i f            then begi n

 Ecnvt HY)KHO nepenVlCblBaTb anemeHTbl

   pa

 YCTaHOBVlM y«a3aTeJ1b pa

Ha nepBblVl aneyeHT Maccna a }

 

for                  to begin

n-l do { nepe6epeM BCe areyeHTbl }

 

         pb A :     ;

 (onupyeM onetqeHTbl "3 a B b }

 

i nc(pa) ;

 Ilepetqelllaetqcf Ha cnenyolll"li       MdCCVlBa a }

 

inc(pb)

end end,

 nepetqelllaetqcf Ha cnenY10111VlVl

       Maccna b }

 

pbA -x :

3arlV111Jet*l BBeneHoe HVICIO X

 

nocneLHVlM anetGHTOM Maccna b }

{ OCBOÖOLVIM naM9Tb , gaHfTY}0 CTaPblM MaCC"BOM a : } FreeMem(a , si zeof(i  :

{ YCTaHOBVlM y«aaaTe1b MdCCVIBa a Ha MaCC"B b: }

readln(x)  { llpoymaet*l cnenyuee 9"CJIO } end end

Bbl, KOHeHHO, 3aMeTVfJIH HenOCTaTOK, IVHBeneHHoro npHMepa.

Km«noro HOBOro 3J1eMeHTa npmxonvncfl nepeHHCb1BaTb 3a-

Динамическое

ново всю предыдущую часть массива. Можно избавиться от этого недостатка, если выделять память не каждый раз, а размерами, равными степеням двойки. То есть если в какой-то момент размер массива равен, например, 8 и требуется добавить еще один э.лемент, нужно выде.лить сразу 16 ячеек. В этом случае в пос.ледующие 7 добав.лений (ес.ли они потребуются) не нужно будет переписывать предыдущую часть массива. Памяти для такого метода потребуется не бо.лее чем в 2 раза больше, чем нужно д.ля данных.

Пример 16.7. Массив переменной длины с линейной сложностью добавления элементов

var

Указатель на хранимый массив } Временная переменная для указателя на новый массив }

pa,pb : nteger:

Временные переменные-указатели для перемещения по элементам массивов }

х       Ј nteger :

Вводимое число }

п       Ј nteger :

Размер памяти, выделенной для массива }

к        Ј nteger :

Количество занятых ячеек массива }

                            Ј nteger : begj п

Счетчик цикла }

Начальный размер массива }

Число занятых ячеек массива } zeof(jnteger) ) :            Память под одну ячейку }

Читаем первое число }

{ Будем сохранять числа, пока вводимое число не равно нулю } begj п jnc(k) : { Новая длина массива }

{ Проверим, помещается ли новое число в выделенную память или требуется выделить новую } jf then { Не помещается } begjn п:=п*2: { Удваиваем требуемую память } { Выделим память под новый массив }

Перепишем все элементы массива а (К-1 штук) в массив Ь }

{ Установим указатель ра на первый элемент массива а }

                 Тема       Динамические переменные

pb:=b: Установим указатель рь на первый элемент массива Ь } for to К-1 do { Переберем все элементы } ЈедЈп

pbA

Копируем элементы из а в Ь }

Јпс(ра):

Перемещаемся на следующий элемент массива а }

jnc(pb) end,

Перемещаемся на следующий элемент массива Ь }

рЈ А :

Запишем введеное число х

последним элементом массива Ь } { Освободим память, занятую старым массивом а }

{ Установим указатель массива а на массив Ь }

{ Если в массиве а есть еще место } begjn

{ Переместим указатель ра на первый незанятый элемент массива а }

Јпс(ра , К-1) :

    рад

{ Запишем введеное число х }

read1 п(х)

{ Прочитаем следующее число }

end

Выводы

1.     При ооъявлении переменных в разделе var память д.ля выде.ляется в момент запуска программы и занята все время работы программы. Это называется статическим выделением памяти.

2.     Д.ля хранения информации об адресе переменной испо.льзуют переменные-указатели.

З. Для указания того, что переменная является указателем, перед именем типа данных, на который создается указатель, ставят знак А («шляпка»).


Контрольные вопросы

4.     Д.ля создания указателя на нестандартную ячейку памяти (массив и.ли запись) необходимо создать тип данных, имя которого указывается после А при описании переменной-указате.ля.

5.     Д.ля обращения к той ячейке, адрес которой хранится в переменной-указателе, ставят знак А после имени переменнойуказате.ля.

6.     Д.ля создания динамической переменной (выделения под нее памяти) используют процедуры New или GetMem.

7.     Д.ля уничтожения динамической переменной (освобождения занимаемой ею памяти) используют процедуры Dj spose и.ли

Контрольные вопросы

1.    В чем отличие динамического выделения памяти от статического?

2.    Кто управляет использованием памяти компьютера? З. Как в программе создать переменную-указатель?

4.     Как обратиться к ячейке памяти, на которую ссы.лается переменная-указатель?

5.     Как переместить переменную-указатель на следующую ячейку памяти?

6.     Как в программе выделить память под динамическую переменную?

7.     Как освободить память, выделенную под динамическую переменную?

8.     Д.ля программы:

var a:jntegep: p: A jntegep: begj п { Момент 1) } { Момент 1 } new(p) : { Момент 2 } -15, { Момент З } djspose(p):{ Момент 4 } Момент 5 }

{ Момент 6 }

а) в какой момент выделяется память для переменной а?

                 Тема       Динамические переменные

б) в какой момент освобождается память, выделенная д.ля переменной а?

в) в какой момент выделяется память для переменнои р

г) в какой момент освобождается память, выделенная д.ля переменной Р А ?

ТЕМА 17

Динамические структуры данных. Стек


Самый удооный способ выделения памяти — когда ее выделяется ровно столько, сколько в каждый конкретный момент времени требуется программе. В этой теме мы начинаем рассказ о том, какими способами можно приблизиться к этому волшебному состоянию. «Приблизиться» в данном случае значит, что программа будет требовать памяти почти ровно столько, сколько ей нужно, плюс небольшие «накладные расходы».

Как вы, вероятно, уже поняли, основная проблема состоит не в том, чтобы выделить памяти ровно столько, сколько нужно. Это как раз просто: понадобился нужный «кусок» — попросил дали. Проблема в том, что при каждом выделении памяти нужно хранить адрес этого самого выданного «куска». «Кусков» много. А так как количество «кусков» заранее неизвестно, создать заранее массив для адресов не получается (при объявлении массива должен быть известен его размер). Предлагаемый метод хранит информацию в виде цепочки. Ка.,ждый раз, когда выделяется новый «кусок» памяти, в нем предусмотрено место для хранения адреса следующего «куска». Для реализации этой идеи достаточно описать только одну статическую переменную — адрес начала такой цепочки, состоящей из объектов вида: данные + адрес следующего объекта.

Урок 1 7. 1 . Опишем тип данных

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

Урок 17.1 . Опишем тип данных

торого быть не может. В языке Паскаль для примерно таких нужд имеется специа.льное служебное слово пЈ1. Оно означает «адрес, которого нет», «ссылка на несуществующую область памяти». Это и кладут в ячейку-адрес (рис. 17.1 ).

 

с

 

с

 

с

 

с

 

 

 

Рис. 17.1. Организация простейшей динамической цепочки данных

type МуЕ] ement=record data данных, которые храним>; на точно такой же элемент данных, как этот>

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

type МуЕ] ement=record data :strjng:

next : А МуЕ] ement end,

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

Пример 17.1. Описание простейшей динамической структуры данных type

РЕЛ ement= A E1ement: { Создаем указатель на тип данных, который будет описан позже. Так как все указатели в памяти все равно занимают одинаковое количество байт, такое впередссылающееся” описание компилятор не смущает }

E1ement recopd


     data         strj пд ;           

    next      РЕ] ement       

var рведЈп              РЕ] ement :

Здесь можно использовДть любой тип данных. Можно описать несколько полей } это ссылка на следующий элемент структуры данных }

Достаточно выделить только одну статическую переменную, чтобы можно было хранить цепочку неопределенной ДлинЫ }


Обратите внимание: э.лемент нашей структуры данных — зашлсь. Мы описываем тип данных только одного э.лемента. Мы не описываем тип данных для всей конструкции. Конструкция состоит из однотипных э.лементов, связанных между собой. С точки зрения Паска.ля это просто описание одной переменной. При создангп,т каждого нового э.лемента Паскалю нет никакого дела, что за структура данных у нас будет получаться. То, что элементы будут соединяться друг с другом, «нанизываясь на цепочку», — дело рук программиста.

Урок 1 7.2. Создание стека и основные операции со стеком

Мы нарисовали общую конструкцию. В таком виде эта структура данных (когда элементы связаны друг с другом в одну цепочку и каждый элемент хранит адрес следующего) называется оДнонаправленным списком.

Рассмотрим частный случай однонаправленного списка — когда операции добавления и удаления элементов разрешены только с одной стороны. На языке программистов такая конструкция называется LIFO («last in — first out», что означает «последним пришел — первым уйдешь»). По-другому это можно себе представить как стопку книг. Добавлять в нее книги можно только сверху, и забирать из нее книги можно только сверху. Последняя книга, положенная в стопку, будет забрана из нее первой. Такая структура данных называется стек (стопка).

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


type pStack-pE1ement:

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

type pStack- A Stack:

Stack=record data :strj пд: next : pStack end,

В разде.ле описания переменных для описания стека нужна одна-единственная переменная — адрес последнего э.лемента, который по.ложен в стек. Он называется вершина стека. Его описание будет выг.лядеть так:

var

Мы добавили к имени Тор (вершина) букву р, чтобы лишний раз показать, что эта переменная является указателем. Также в приведенном примере мы сразу задали начальное значение переменной рТор (т Л ). Это значит, что в момент старта программы стек пуст (в нем пока ничего не хранится).

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

var pTop:pStack; begj п

Как мы уже говорили, при работе со стеком допустимы только две операции: положить элемент на стек и взять верхний элемент со стека. Опишем эти действия в виде подпрограмм. Имена для этих подпрограмм дадим в соответствии со сложившейся традицией: помещение элемента на стек называется Push, изъятие элемента с вершины стека называется Рор.

Добавление элемента в стек (Push)

Начнем с реализации подпрограммы Push. Д.ля нача.ла уясним, процедура это или функция. Результатом работы этой подпрограммы до.лжно быть действие помещения указанного значения в стек. никакого значения подпрограмма Push возвращать не до.лжна. То есть Push процедура. А так как д.ля своей работы она до.лжна «знать», что нужно положить на стек, то это процедура с параметром. Так как параметр в результате работы процедуры изменяться не должен, передача параметра происходит по значению.

По.лучаем описание заголовка:

procedure

Здесь chto — то значение, которое мы кладем на стек. Так как при описании типа данных Stack мы договорились, для определенности, что в стеке мы будем хранить строки, то и тип данных параметра chto — строка.

Представим себе состояние стека к моменту, когда мы собираемся положить на его вершину новый элемент (рис. 17.2).

 

data

 

р Тор

data

 

data

 

Рис. 17.2. Пример состояния стека перед тем, как в него будет положен еще один элемент

Д.ля нача.ла нужно создать в памяти место, в котором будет храниться новый элемент. Это должна быть процедура new, в качестве параметра которой нужно сообщить переменную-указатель на такой тип данных, под который собираемся выде.лить память. Выде.лять память собираемся под элемент стека (Stack). На него умеет ссы.латься переменная типа pStack. Нам нужно, чтобы адрес вновь создаваемой ячейки где-то сохранился. Д.ля этого в процедуре Push создадим специальную вспомогательную переменную типа pStack: var p:pStack:

Теперь можно создать новую ячейку памяти: new(P) '

У этой ячейки два поля: поле данных и поле-указате.ль на с.ледующий э.лемент.

Запо.лнить поле данных просто — в нем должны храниться те данные, которые передаются в качестве значения, помещаемого на стек. То есть значение параметра chto:

РА . data

Обратите внимание на то, как мы обращаемся к полю данных: переменная р указывает на нужную нам ячейку памяти. Чтобы обратиться к значению этой ячейки, мы используем знак А , то есть Р А . А теперь нужно обратиться к полю data этой записи. Значит, нужно поставить точку и имя поля. Все вместе получается: рд . data.

Теперь нужно заполнить значение второго поля — адрес следующей ячейки стека. Так как наша новая ячейка должна оказаться верхней, сама она должна ссылаться на следующую за ней ячейку. Для того нужно где-то взять ее адрес. Если мы посмотрим на структуру стека (см. рис. 17.2), то поймем, что на этот элемент в данный момент как раз указывает переменная РТ ор. Значит, в поле next нужно положить ее значение:

Р А . next

Вроде бы оба поля вновь созданного элемента стека мы заполнили. Нужно ли сделать что-нибудь еще? Посмотрим внимательно на текущую структуру стека (рис. 17.3).

 

ch to

 

р

р Тор

da ta

 

da ta

 

Рис. 17.3. Состояние стека после добавления нового элемента

(указатель на вершину пока еще ссылается на прежнюю вершину)

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

Значит, остается переставить его на вновь созданный э.лемент. Его адрес храниться в переменной р. Получаем: ртор :

Сведем все сказанное вместе:

Пример 17.2. Процедура Push (добавление элемента на вершину стека) procedure var р pStack : { Вспомогательная переменная } begj п

{ Выделяем память под новый элемент }

{ Записываем в него сохраняемое значение }

Залаем указатель на слепующиЙ }

Делаем новый элемент вершиной стека }

Заметим, что мы написали процедуру Push, рассуждая о стеке, в котором уже есть хотя бы один элемент. А еще нужно рассмотреть с.лучай, когда стек пуст. Однако этот случай от.личается от непустого стека только тем, что переменная РТ ор вместо адреса верхнего э.лемента стека хранит значение пЈ Л. Рассмотрев только что написанную процедуру Push, можно убедиться, что в с.лучае пустого стека она создаст новый (и единственный) э.лемент стека и запишет в его поле-указатель на следующий элемент текущее значение рТор, то есть пЈ1. Но в случае пустого стека именно это и нужно сделать — записать пЈЛ в качестве ссы.лк•и на второй элемент стека. Значит, наша процедура Push одинаково работает в обоих случаях.

Извлечение элемента из стека (Рор)

Вторая подпрограмма работы со стеком (Рор) должна извлекать верхний элемент стека. При этом этот элемент должен из стека удаляться, а его значение должно возвращаться. Значит Рор — функция. Так как никакой дополнительной информации при вызове мы ей не сообщаем, параметров у нее нет. А тип данных возвращаемого значения должен совпадать с типом данных того, что хранится в стеке. В нашем случае — строка: functjon Pop:stpjng:

Самое простое, что должна сделать функция Рор , вернуть значение по.ля данных верхнего элемента стека. На него указывает переменная ртор, поэтому действие очевидно: Рор : =рТорА .data :

Осталось только удалить этот элемент из стека. Но под действием «удалить» мы должны понимать два действия: во-первых, нужно очистить память, которую этот элемент занимает, вовторых, нужно изменить стек так, чтобы этого элемента в нем больше не было. Оба действия сами по себе просты: для очистки памяти нужно вызвать dj spose(pTop), а для того, чтобы этого элемента в стеке больше не было, нужно переместить указатель стека РТ ор на следующий элемент (его адрес хранится в поле next верхнего элемента) — рТор . next.

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

Пример 17.3. Функция Рор (удаление элемента с вершины стека) functjon Pop:strjng:

var р pStack ; { Вспомогательная переменная } begj п р: =рТор: { Запоминаем адрес вершины стека }

Рор : .data : { Извлекаем данные из верхнего элемента} Перемещаем указатель вершины стека на следующий элемент } dj spose(p) { Освобождаем память } end,

11роверка стека на пустоту (StacklsEmpty)

Еще одна подпрограмма нужна для работы со стеком — функция, которая проверяет стек на пустоту (что в стеке нет ни одного элемента). Это очень просто проверить убедиться, что указатель на вершину стека равен пЈ 1:

Пример 17.3. Функция StacklsEmpty (проверка стека на пустоту) functjon StackIsEmpty:b001ean: ЬедЈ п

StackIsEmpty           рТор=пЈ 1 end;

Урок 1 7.3. Использование стека

В качестве примера применения стека приведем программу перевода натурального числа в другую систему счисления. Стек нужен

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

Пример 17.4. Перевод натурального числа в другую систему счисления с использованием стека var

{ Считаем что здесь приведены описания подпрограмм

Push, Рор и StackIsEmpty

(которые мы опустили для лаконичности) } begj п wrjte( ' Введите число и основание системы счисления :

whj]e do Пока число не станет равным нулю } begj п

Push(x mod д): { Извлекаем из него млДдший разряд и кладем его в стек } х:=х djv а               { Вычеркиваем млДдший разряд числа } end, whj]e not StackIsEmpty do { Пока стек не пуст } wrjte(Pop) { Извлекаем из стека цифры по одной и выводим их на экран }

Заметим, что данная программа работает то.лько при ус.ловии, что основание не больше 10. В противном случае приш.лось бы писать допо.лнительное условие для вывода на экран цифр, бо.льших, чем 9.

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


Урок 17.3. Использование стека

При испо.льзовании стека очень важно перед каждым изв.лечением данных из стека проверять, не является .ли он пустым.

ПРИМЕЧАНИЕ

Иноюа возникает ситутјия, коюа из стека нужно извпсчь верхний эдемеюн, но извпсчсннос значснис нс нужно никуда использовать. этот спучас нужно просто испопьзовать фунюјшо Рор так, как будто бы она явпястся продеЭурой. [То есть вместо тою, чтобы писать wrjte( Рор) Х : =Рор, тожно просто написать отЭспьным оператором Рор; . Д<отпипятор поймет, что нужно вызвать функдшо, но сс значение нс нужно никуда сохраняпн, вывоДить.

ПРИМЕЧАНИЕ

ДЗ привеЭснных примерах считается, чию в программе топько один стек. ДТоэтоту в подпрограммах испопьзустся мобапьная переменная р Тор, копюрая неявно изменяется при испопьзовании Push и Рор. (Усти возникает необходимость в ИСПОПЬЗОВс7Н[Ш нсскопьких стеков, Эм каждой ПОЭПРОТТИ(ИЫ (push, Рор и StackIsEmpty) нужно Добавить Эопопншнспьный параметр-псреТОЧНУЮ — адрес вершины стека, передавать соответствующую переменную при каждом их вызове и испопьзовать сго при обращении к вершине стека.

Задание 17.1. Напишите программу, которая вводит с к.лавиатуры чис.ла, пока не будет введено число ноль. После этого программа выводит на экран введенные числа (кроме нуля) в обратном порядке.

Задание 17.2. Напишите программу, которая вводит с клавиатуры натуральное четное число М, после чего вводит последовательность из еще N целых чисел. Программа должна проверить, является ли введенная последовательность «зеркальной» (палиндромом). То есть если элементы последовательности переставить в обратном порядке, последовательность останется той же.

Задание 17.3. Напишите программу, которая вводит с клавиатуры числа, пока не будет введено четное число. После этого выводит на экран среднее в списке число (например, если ввели 7 или 8 чисел, нужно вывести четвертое по счету).

Задание 17.4. Напишите программу, которая вводит с клавиатуры числа, пока не будет введено число 100. Вывести на экран первую половину чисел, если их количество четно, лиоо вторую (66.льшую) по.ловину чисел, если их количество нечетно. Чис.ла выводить в том же порядке.

Программирование стека при помощи массива

Нам бы не хотелось, чтобы у читателя создалось впечатление, что стек — обязательно динамическая структура данных. Принцип LIFO очень удобен при программировании. В частности, для функционирования самого компьютера вызов любых подпрограмм и обработка любых событий (например, операций ввода-вывода) организованы при помощи аппаратно реализованного стека, при этом команды Push и Рор осуществляет сам процессор. Об этом вы можете прочитать в специальной литературе. Здесь мы собираемся только упомянуть, что стек можно реализовать при помощи ооычного массива. Это даже удобнее, чем динамически, за исключением разве что обычной проблемы при использовании массивов — нужно заранее знать наибольшую длину массива (наибольшее элементов стека). Нужно следить за тем, чтобы не произошло переполнение стека.

Пример 17.5. Реализация стека при помощи массива const п-100: { Размер стека } var  .n1 of stpjng: { Массив для хранения

{ Количество занятых ячеек стека.

Изначально все свободны } procedure begj п jf Тор<П then { Если в стеке есть свободные ячейки } begjn тор : : { Увеличиваем число занятых ячеек }

Stack[Top1              { Сохраняем новое значение } end

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

Контрольные вопросы

functjon Pop:stpjng:

ЬедЈ п

Рор : =Stack гтор1 : Извлекаем верхнее  } тор : =Тор-1 { Уменьшаем число занятых ячеек } end, functjon StackIsEmpty:b001ean: begj п

StackIsEmpty = тор=о

Возможно, вы обратили внимание, что хотя для хранения стека используется совершенно другая структура данных, вызов подпрограмм ничем не отличается их вызовов в динамической реализации стека. Это очень удобно при программировании — если вы в какой-то момент решаете поменять внутреннее представление данных, достаточно будет только поменять тела подпрограмм, и ничего не менять в программе, которая их использует.

Выводы

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

2.     Самая простая такая структура данных называется стек. Для нее определены только две операции — положјлть элемент в стек и взять элемент из стека.

З. Операции помещения и изъятия элементов из стека определены только с одной стороны. В результате элемент, положенный в стек последним, будет изъят из него первым.

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

Контрольные вопросы

1.     Какая основная идея того, как хранить в памяти множество

э.лементов, заранее не зная их количества? Какая структура

э.лемента данных для этого используется?

2.     Как при описании типа данных для цепочки

э.лементов можно задать «ссылку элемента на свой собственный тип данных» (чтобы не возникло ошибки задания типа данных, описание которого еще не закончено)? З. Дайте определение стека.

4.     Какую аббревиатуру используют для описания основного свойства стека? Что она означает?

5.     Зачем необходимо использование вспомогате.льной переменной при помещении элемента в стек? При изъятии?

6.     Для фрагмента программы:

Push(1) : Push(2):

Push(3) :

Push( Рор+Рор) :

Push(4):

Push( Рор*Рор) : wrjte( Рор-Рор) :

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


ТЕМА 18

Динамические структуры данных. Очередь

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

Урок 1 8. 1 . Принцип работы и описание типа данных

Проана.лизировав указанный порядок помещения и изъятия э.лементов, по.лучаем, что он происходит по принципу «первым прише.л — первым ушел». Это принято называть FIFO (First In — First 0ut) очередь (queue).

Работа программистской очереди действительно похожа на очередь в столовой за булочками (рис. 18.1) — каждый пришедший встает в конец очереди, а булочки выдают в нача.ле очереди. Заметим, что в отличие от человеческой очереди, где каждый че.ловек знает того, кто стоит непосредственно перед ним, компьютерная очередь организована наоборот (в обратную сторону) каждый э.лемент «знает», кто стоит в очереди непосредственно за ним. Это различие возникает оттого, что люди организуют очередь и поддерживают порядок в ней сами. А в компьютерной очереди этот порядок поддерживает «тот, кто выдает булочки». Это можно представить себе так: новый человек, который встает в очередь, пишет на спине последнего человека в очереди свое имя, пос.ле чего сам становится последним. А при извлечении человека из очереди ему дают булочки, считывают у него со спины имя с.ледующего и подзывают того к окошку.

Урок 18.1 . Принцип работы и описание типа данных

                                    1            2             з            4            5                           6

Рис. 18.1. Пример человеческой очереди

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

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

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

Описание структуры данных очереди совпадает со структурой данных стека. Проще всего написать type pQueue pStack. Если такое описание непонятно, приведем его полностью:

type

Queue=recopd data :stpj пд:


next : pQueue

Назовем требуемые глобальные переменные адресов нача.ла и конца очереди рВедЈ п и pEnd. Букву р в начало имени мы добави.ли, чтооы подчеркнуть, что это указатели и чтобы имена переменных не совпа.ли со служебными словами begjn и end. var

Так как очередь изначально пуста, мы присвои.ли обеим переменным указатели на пЈ 1.

Урок 1 8.2. Основные операции с очередью

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

Добавление элемента в очередь (EnQueue)

Назовем подпрограмму EnQueue (поместить в очередь). Д.ля начала поймем, процедура это или функция. Результатом работы этой подпрограммы до.лжно быть действие помещения указанного значения в очередь. Значит, никакого значения подпрограмма Епаиеие возвращать не должна. То есть EnQueue — процедура. А так как для своей работы она должна «знать», что нужно положить в очередь, то это процедура с параметром. Так как параметр в результате работы процедуры изменяться не должен, передача параметра до.лжна происходить по значению. То есть все как при помещении

э.лемента в стек:

procedure :

chto — то значение, которое мы отправляем в очередь.

Процедура помещения элемента в очередь зависит от того, пуста очередь или в ней уже есть хотя бы один элемент. Разл,щить эти два случая можно по указателю на начало очереди. Если в очереди нет ни одного элемента (указатель на начало очереди равен пЈ 1 нужно создать новый элемент (new( pEnd)), в его поле данных

Основные операции с очередью

по.ложить chto (pEnd A . data :=chto), а его указатель на с.ледующий э.лемент сос.лать на пЈ1 (pEnd A  После этого нужно указате.ль на начало очереди установить на этот же (единственный) э.лемент (рведЈ

Ес.ли в очереди есть хотя бы один элемент, нужно присоединить новый э.лемент к последнему элементу очереди: new(pEnd A next ) . Заметим, что при этом происходит сразу два нужных нам деиствия: в памяти создается новыи элемент, и последнии э.лемент очереди сразу ссылается на него. Теперь остается переставить указате.ль конца очереди на этот новый элемент (pEnd : =pEnd A next) и запо.лнить его поля. В по.ле данных положить chto (pEnd A data :=chto), в поле ссылки на следующий элемент по.ложить пЈ Л (pEnd A .next

Сведем все вместе:

Пример 18.1. Процедура EnQueue

(добавление элемента в конец очереди) procedure begj п jf рВедЈп=пЈ] then Если очередь пуста } begj п new(pEnd) : Выделяем память под новый элемент } рведЈ п: =pEnd: Указатель начала стека устанавливаем на этот же элемент } pEnd A . data : =chto: { Записываем в него сохраняемое значение }

Указатель на следующий делаем пЈ Л

e]se { Очередь не пуста }


begj п new(pEndA .next) :

pEnd =pEnd A . next :

pEndA data •=chto:

end;

{ Выделяем память под новый элемент и ссылаем последний элемент очереди на не го }

{ Перемещаем указатель конца очереди на новый элемент }

{ Записываем в него сохраняемое значение }

Указатель на следующий делаем пЈ 1


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

Пример 18.2. Процедура EnQueue

(добавление элемента в конец очереди).

Более короткая запись procedure : begj п jf рведЈп=пЈ1 then { Если очередь пуста } begjn

new(pEnd) ;

{ Выделяем память под новый элемент }

рведЈ п : =pEnd

{ Указатель начала стека устанавливаем на этот же элемент }

{ ОЧереДЬ не пуста } begjn

new(pEndA . next) ;

{ Выделяем память под новый элемент и ссылаем последний элемент очереди на не го }

pEnd                   . next :

{ Перемещаем указатель конца очереди на новый элемент }

pEnd A .data Записываем в него сохраняемое значение } pEnd A .next Указатель на следующий делаем пЈ Л } end;

Извлечение элемента из очереди (DeQueue)

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

functjon DeQueue:strjng:

В от.личие от добавления элемента в очередь, при изв.лечении не обязате.льно рассматривать различные случаи. Даже с.лучай единственного элемента в очереди, который на первый взгляд нужно рассматривать отдельно, вполне укладывается в оощую схему. Снача.ла можно получить значение первого э.лемента очереди и выдать его в качестве значения функции (DeQueue : =рВедЈ П А data). Пос.ле этого останется только переместить указате.ль нача.ла


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

. data : .next: dj spose(p):

Пример 18.3. Функция DeQueue

(извлечение элемента из начала очереди) functjon Deaueue:strjng:

var р раиеие: begj п

Вспомогательная переменная }

Запоминаем адрес начала очереди }

DeQueue : . data :

Извлекаем данные из первого ЭЛегчен ТД очереди }

рВедЈп:

Перемещаем указатель начала очереди на слепующий элемент }

dj spose(p)

Освобождаем память }

Проверка очереди на пустоту (QueueIsEmpty)

Как и в с.лучае со стеком, нужна еще одна подпрограмма для работы с очередью — функция, которая проверяет очередь на пустоту (когда в очереди нет ни одного элемента). Это очень просто проверить — убедиться, что указатель на начало очереди равен пЈ Л

Пример 18.4. Функция QueueIsEmpty (проверка очереди на пустоту) functjon QueueIsEmpty:b001ean: begj п

QueueIsEmpty             рведЈ п=пЈ Л end,

Урок 1 8.3. Использование очереди

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

Рассмотрим решение такой задачи. На вход программе подается пос.ледовательность символов неизвестной д.лины, заканчивающаяся точкой. Других символов «точка» в последовате.льности нет. Необходимо вывести на экран слова, содержащиеся в пос.ледовате.льности, которые длиннее, чем самое короткое с.лово пос.ледовате.льности. Словом будем считать любую последовате.льность симво.лов, отделенную от другого слова одним или неско.лькими пробе.лами. Известно, что каждое слово не д.ш,шнее 50 симво.лов. С.лова вывести по одному в строке, в том же порядке.

Очевидно, что входную последовательность необходимо просмотреть два раза — для определения самого короткого с.лова и д.ля отбора только тех слов, которые длиннее его. Опреде.лить д.лину самого короткого слова в общем случае можно не раньше, чем будут перебраны все слова. Значит, нужно где-то хранить всю (в худшем случае) последовательность слов. Выделить под это массив не.льзя, потому что неизвестна его длина. Так как с.лова нужно будет вывести в том же порядке, наибо.лее разумно испо.льзовать очередь.

Пример 18.5. Вывести по одному все слова входной последовательности. которые длиннее самого короткого слова

{ Считаем что здесь приведены описание типа данных очереди, описания подпрограмм Епаиеие, DeQueue и QueueIsEmpty, а также переменные рВедЈп и pEnd

(которые мы опустили для лаконичности) } var ch:chap:

mjn:jntegep:

ЬедЈ п

S    { Здесь накапливаем текущее слово } { Минимальная длина слова } repeat read(ch): { Читаем очередной символ } jf '  . ' ) then

{ Если не конец слова }

                S =S+Ch      { Добавляем символ к слову }

                  jf                     then { Если конец имеющегося слова }

ЬедЈп jf  then { Не являет я ли длина слова минимальной }

EnQueue(s) ; { Добавляем слово в очередь } { ”0бнуляем” слово }

untj] ch { Конец последовательности символов } whj]e not QueueIsEmpty do begj п

{ Извлекаем слово из очереди } then { Если оно длиннее ггЈп }

{ Выводим его на экран }

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

ПРИМЕЧАНИЕ

Как и в спучас со стеком, во всех приведенных примерах считается, что в проташшс топько одна очерсЭь. ДТоэтошу в подпрограммах ИСПОДЬЗУются ыобадьныс персмснныс рЗедЈп и pEnd, копюрыс неявно иЗл4сняюп1ся при ИСПОПЬјоваН1,Ш Епаиеие и DeQueue. Как и в спучас с несколькими стеками, еспи возникает необходимость в использовании нескольких очередей, нужно Эпя кажЭой подпрограммы (EnQueue, DeQueue и QueueIsEmpty) Добавить Эопопнительные параметры-переменные — адрес начала очереди Эпя DeQueue и QueueIsEmpty адреса начала н конда очереди Эпя Епаиеие и передавать соответствующие перелтенные при каждом их вызове и использовапљ их при реапизтјии всех прел подпрограмм.

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

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

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

11рограммирование очереди при помощи массива

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

Здесь мы собираемся показать, как очередь можно реалгвовать при помощи обычного массива. Хотя это удобнее, чем динамически, напоминаем об обычной проблеме при использовании массивов — нужно заранее знать наибольшую длину массива (наибольшее число элементов очереди).

Заметим, что при реализации очереди в массиве возникает проблема эффективности. Если считать, что начало очереди — в первом элементе массива, то добавление элемента в конец очереди не вызывает проблем. Считаем, что позиция конца очереди хранится в целочисленной переменной, и увеличиваем ее при добавлении элемента.

Но если считать, что начало очереди находится в первом элементе массива, то при извлечении элемента из очереди возникает необходимость сдвинуть все элементы массива на одну позицию в сторону начала массива. Очевидно, что при значительной длине массива это весьма неэффективно. Хотелось бы, чтобы извлечение элемента из очереди было так же быстро, как и добавление элемента. Используем еще одну переменную — позицию начала очереди в массиве будем считать, что очередь хранится в массиве между элементами, номера которых хранятся в этих целочисленных переменных. Назовем их для определенности Ј ВедЈп и Ј End (индексы начала и конца очереди).

Еще одна тонкость, которую нужно предусмотреть: что делать, когда конец очереди находится в последнем элементе выделенного массива, и нужно добавить еще один элемент. Если при этом сдвигать всю очередь в начало массива, снова по.лучается неэффективно. Нужно считать, что массив «замкнут цик.лически», что пос.ле пос.леднего элемента массива идет первый (то есть п+ 1 = 1 ). Заметим, что при этом индекс начального элемента очереди не всегда будет меньше, чем индекс последнего.

Еще один вопрос, который стоит задать: что будет признаком того, что очередь пуста или, наоборот, полна? Ведь оба индекса — Ј ВедЈ п и Ј End — могут принимать любые значения от 1 до п. На первый взг.ляд, можно считать очередь заполненной, ес.ли конец «догна.л» начало, то есть jEnd «на 1 меньше», чем ЈВедЈп. «Меньше» в кавычках означает «с учетом циклической замкнутости очереди в массиве». Но этот же признак хочется считать пустой очередью. Чтобы от.личать эти два состояния, нужно не допускать того, чтобы Ј End оказа.лся «на 1 меньше» чем ЈВедЈп. Тогда запо.лненная очередь — когда jEnd «на 2 меньше» чем ЈВедЈп.

Пример 18.6. Реализация очереди при помощи массива const п=100: { Размер очереди } var  .n] of strjng: Массив для хранения элементов очереди } Индекс ндчдльного элемента очереди в массиве }

Ј End : jnteger=n:    Индекс  элемента очереди в массиве } procedure : begj п

{ Если в очереди есть свободные ячейки } jf (j3egjn-jEnd+n) mod then begj п jf jEnd<n then { Конец не в последнем элементе }

Ј End End+1

Конец В  }

{ Перемещаем конец в начало } аиеиеГЈ End1   :

end

{ здесь нужно как-то обрабатывать ситуацию переполнения очереди } end; functjon Deaueue:stpjng:

ЬедЈ п

           :                Вед 1 п 1 :

jf Ј ВедЈп<п then { Начало не в последнем элементе } Ј ведЈ п ведЈ п+1 начало в ПОСЛЕ днем  }

{ Перемещаем начало в первый элемент } end; functjon QueueIsEmpty:b001ean: begj п

QueueIsEmpty         (jBegjn-jEnd+n) mod п 1 end;

В этой программе мы использовали не совсем очевидный способ вычис.ления признаков «меньше на 1» и «меньше на 2».

Выводы

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

2.     В очередь элементы можно добавлять только в конец, а изв.лекать — только из начала.

З. Д.ля быстрого добавления и извлечения данных из очереди хранят адреса не только начала, но и конца очереди.

Контрольные вопросы

1.     В чем отличие очереди людей от компьютерной очереди?

2.     Каков критерий целесообразности применения

З. Почему далеко не всегда реализуют очередь при помощи массива?


ТЕМА 19

Динамические структуры данных. Однона.правленный

СПИСОК

Мы подробно рассмотрели структуры данных, в которых операции добавления и извлечения элементов строго ограничены правилами — FIFO и LIFO (очередь и стек). Теперь пришло время рассмотреть структуру данных, которая организована по точно такому же принципу, но в которой отсутствуют какие-либо ограничения на способ добавления/удаления элементов.

Урок 1 9. 1 . Описание типа данных и принцип работы

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

Чтобы иметь возможность обращаться к однонаправ.ленному списку, достаточно хранить только одну переменную — адрес начала списка.

Как мы уже писали в теме 17, описание структуры данных д.ля однонаправ.ленного списка такое же, как у стека и у очереди:

type pLj stE1 ement=A LjstE1 ement:

LjstE1ement pecopd data :stpjng:

next : pLj stE1 ement

Г.лобальную переменную, хранящую адрес начала списка [1] , назовем рВедЈп:

var p3egjn:pLjstE1ement пЈ1 :

Урок Л 9.2. Основные операции с однонаправленным

Можно представлять себе список так, как показано на рис. 19.1. Важно понимать, что при использовании стека и операцј,ш по.лучения значений элементов обязательно сопровождается уда.лением самих элементов из структуры данных. Д.ля однонаправ.ленного списка это вовсе не обязательно. Скорее наоборот, обычной ситуацией д.ля списка является операция просмотра значений всех э.лементов и либо проверка их значений, либо осуществ.ление с ними каких-либо действий. При этом удаления э.лементов из списка обычно не происходит.

pBegin

Рис. 19.1. Организация однонаправленного списка

Урок 1 9.2. Основные операции с однонаправленным списком

При работе со списком нужно уметь добавлять элемент в список, удалять из него элемент и последовательно просматривать все элементы списка.

Последовательный просмотр всех элементов списка

Назовем подпрограмму РрЈ ntLjst (печать списка). Чтобы перебрать все элементы списка, нужно ввести дополнительную переменную-указатель, которая будет по очереди «пробегать» адреса всех элементов списка, от первого до последнего. Функция этой переменной очень похожа на функцию счетчика цикла при переборе всех значений одномерного массива.

Так как количество элементов списка нам, в общем случае, неизвестно, будем использовать цикл whj Ле. Перед началом цикла присвоим переменной цикла значение адреса начала списка. Будем перемещать эту переменную на следующий элемент списка до тех пор, пока ее значение не станет равно пЈ (признаку конца списка).


Пример 19.1. Процедура PrintList (просмотр всех элементов однонаправленного списка)

procedure PpjntLjst:

 

var p:pLjstE1ement:

ЬедЈ п

Временная переменная-указатель на элементы списка }

Устанавливаем ее начальное значение на адрес начала списка }

whj 1e р <> пЈ1 do begjn

Пока не достигнем конца списка }

      wrjte1n(pA   

Здесь можно осуществить любое требуемое действие с текущим элементом списка }

     р :      . next                

Перемещаем переменную-указатель на следующий элемент списка }

end;

Помещение элемента в список

Назовем подпрограмму PutInLj st (поместить в список). По аналогии с рассуждениями о добавлении элементов в стек и в очередь это процедура с параметрами. Но в отличие от очереди и стека, где помещение элементов происходит в строго определенное место, в списке это ограничение отсутствует. В общем случае нужно уметь помещать элемент в начало списка, в конец, в середину и в пустой список. Значит, нужно передавать этой процедуре параметр, указывающий место списка, в которое нужно поместить добавляемый элемент. Так как про значения элементов списка мы ничего не знаем, эта информация должна быть указателем (адресом). Рассмотрим случай помещения элемента в список в середину (рис. 19.2).

Рис. 19.2. Общий случай помещения нового элемента в однонаправленный список

Урок Л 9.2. Основные операции с однонаправленным

При добав.лении элемента в его поле next нужно поместить адрес э.лемента, который следует после него (это нетрудно). А адрес нового элемента нужно поместить в поле next того э.лемента, который будет находиться в списке непосредственно перед ним. Но д.ля этого нужно знать адрес этого элемента (после которого мы помещаем новый элемент). Это и должно быть значением второго параметра процедуры PutInLj st.

procedure kuda : pLj stE1 ement) :

chto — то значение, которое мы помещаем в список пос.ле э.лемента, адрес которого указан в параметре kuda.

Проана.лизировав действия, которые нужно совершить при помещении элемента в середину и в конец списка, можно обнаружить, что они совпадают. Поэтому можно считать эти два с.лучая одним. Тот же вывод можно сделать, проанализировав с.лучаи помещения э.лемента в начало списка и в пустой список. По.лучаем всего два случая: помещение в начало списка и после указанного э.лемента. Но при помещении в начало списка не.льзя указать адрес э.лемента, после которого следует вставлять в список новый э.лемент. Значит, нужно условиться, как сообщить процедуре, что э.лемент с.ледует поместить в начало. Проще всего в этом с.лучае в качестве параметра kuda передать пЈ1 (не существует э.лемента, пос.ле которого нужно вставить новый). Сведем все сказанное вместе:

Пример 19.2. Процедура PutlnList (помещение элемента в однонаправленный список)

procedure  kuda :pLjstE1ement) ; var p:pLjstE1ement: { Временная переменная-указатель }

ЬедЈ п jf kuda=nj1 then { Помещаем элемент в начало списка }


begjn

Р А . data •=chto

{ Помещаем begjn

{ Выделяем память под новый элемент }

Отсылаем его на начало списка } { Указатель начала стека устанавливаем на этот элемент }

{ Записываем в новый элемент сохраняемое значение } элемент после указанного }


new(P): { Выделяем память под новый элемент } Р А . next . next : { Ссылку с нового элемента на следующий делаем равной адресу элемента, стоявшего после kuda }

kuda A .next                 

{ Устанавливаем ссылку с элемента kuda на новый }

Р А . data              

{ Записываем в новый элемент сохраняемое значение }

end;

Возможно, вы заметили, что в обоих случаях первое и пос.леднее действия совпадают. Более короткую версию процедуры Put InL Ј st пред.лагаем написать самостоятельно.

Удаление элемента из списка

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

Так как удаляемое значение не требуется возвращать в вызывающую программу, подпрограмма RemoveFromLj st (удалить из списка) будет процедурой. Ей нужно указать параметр — какой э.лемент нужно удалять. Рассмотрим самый общий случай — уда.ление э.лемента из середины списка. Наиболее удобным представ.ляется сообщить процедуре RemoveFpomLj st непосредственный адрес удаляемого элемента.

Анализируем действия, которые при этом требуется совершить: установить ссылку элемента, стоящего перед уда.ляемым, на э.лемент, стоящий после удаляемого, и освободить память. По.лучить адрес э.лемента, стоящего после удаляемого, просто. Он хранится в по.ле next. А вот адрес предыдущего элемента по.лучить проб.лематично. Ведь у нас список однонаправленный. В обратную сторону по такому спуску не пройти. Единственное, что можно сде.лать — перебирать все элементы от начала, пока по.ле next не будет содержать адрес удаляемого элемента. Но это очень до.лго. Лучше придумать что-нибудь эффективнее. Проще передавать не адрес уда.ляемого элемента, а адрес предыдущего. Зная его, по.лучить адрес удаляемого и адрес следующего очень легко.

Урок 19.3. Обработка

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

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

Пример 19.3. Функция RemoveFromList (удаление элемента из списка) procedure  ;

var р         pLjstE]ement:

{ Вспомогательная переменная }

begj п

 

jf gde - then begj п

Удаление первого элемента }

Запоминаем адрес первого элемента }

      рведЈ п:          . next :

Переставляем начало списка на следующий элемент }

dj spose(p)

end begj п

Освобождаем память, занимаемую первым элементом }

Удаление не первого элемента }

       .next:

Запоминаем адрес удаляемого элемента }

gde A . next : next : { Переставляем ссылку с предыдущего элемента на следующий } dj spose(p) Освобождаем память, занимаемую удаляемым элементом }

end,

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

Урок 1 9.3. Обработка списков

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

В качестве примера обработки списка приведем программу, которая встав.ляет в список новый элемент Х перед каждым вхождением в список элемента У. Заметим, что данная задача имеет смыс.л то.лько при Х * У.

При состав.лении программы нужно обратить внимание на два момента. Во-первых, если просто перебирать э.лементы списка и сравнивать каждый из них с У, возникает проб.лема добав.ления в список э.лемента перед найденным. Ведь ссылку на новый э.лемент нужно поместить в поле next элемента, стоящего перед У, а мы его уже «прош.ли». Значит, нужно проверять на равенство У не текущий э.лемент списка, а следующий после текущего. Тогда нам будет известен адрес э.лемента, в котором нужно изменить по.ле next. Во-вторых, нужно отдельно рассмотреть ситуацию, если первый э.лемент в списке равен У. Ведь перед первым э.лементом нет предыдущего, поэтому под рассмотренную общую схему он не попадает.

Пример 19.4. Добавление в однонаправленный список нового элемента. равного Х. перед каждым вхождением в список элемента У var

{ Считаем, что здесь приведено описание процедуры

PutInLjst (которое мы опустили для лаконичности) { Временная переменная } begj п wrjte( Введите Х: ' ) :

wrjte( Введите У: ' ) :

{ Устанавливаем    на начало списка } пЈ] then { для пустого списка задача не          сМЫСЛ а begj п

Г Список пуст ' ) :

end jf p A .data = у then { Если Х в начале списка }  { Добавляем перед ним У } whj 1e p A .next <> пЈ1 do { Пока не достигнем конца списка }

ЬедЈп jf РА . next A .data        х then { Если элемент,


begjn

PutInLj st(y , р)

. next end,•

. next end

Урок 19.3. Обработка

следующий после текущего, равен Х }

Вставляем перед ним У }

Переходим к следующему }

Переходим к следующему }


Заметим, что в случае нахождения элемента, равного Х, про-

грамма два раза выполняет переход к следующему элементу. Если этого не сделать, при простом переходе к следующему элементу текущим элементом станет У, а следующим за ним снова будет Х. Тогда перед ним снова будет вставляться У, и так бесконечно.

ПРИМЕЧАНИЕ

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

Целесообразность использования однонаправленного списка

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

Таблица 19.1. Сравнение эффективности работы с одномерным массивом и с однонаправленным списком

Операция

Одномерный массив

Однонаправленный список

Последовательный просмотр всех элементов

Одинаковая сложность

Обращение к произвольному элементу

Прямой доступ. Очень быстро

Последовательный

переоор элементов. Долго

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

Операция

Одномерный массив

Однонаправленный список

Добавление (вставка) или удаление э.лемента

Сдвиг всех следующих элементов. Долго

Небольшое число операций. Быстро

Обращение к одному из соседних элементов, в произвольном направлении

Прямой доступ. Очень быстро

Последовательный просмотр, в худшем случае большинства элементов. Долго

Выделение памяти

Нужно знать максимальное число элементов в момент разработки программы

Можно выделить память в произвольный момент работы программы

Задание 19.1. Напишите программу, которая создает однонаправ.ленный список и заполняет его случайным количеством ( 10—20) случайных целых чисел. Вывести список на экран. Найти среднее арифметическое всех четных элементов списка.

Задание 19.2. Д.ля списка задания 19.1 напишите подпрограмму, которая:

а) разворачивает список в обратном порядке за один проход по списку;

б) подсчитывает число элементов списка, которые бо.льше предыдущего, но меньше следующего;

в) проверяет, есть ли в списке хотя бы два одинаковых э.лемента;

г) из каждой группы подряд идущих равных э.лементов списка остав.ляет только один;

д) порождает из списков А и В список С, который состоит из

э.лементов, которые содержатся только в А или только в В.


Контрольные вопросы

Выводы

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

2. Каждый э.лемент однонаправленного списка содержит в себе адрес с.ледующего э.лемента.

З. Для хранения однонаправленного списка достаточно использовать только одну глобальную переменную — адрес первого элемента.

4. Основные действия по обработке однонаправленного списка — последовательный просмотр всех элементов, добавление удаление элемента в указанном месте.

Контрольные вопросы

1.     Какой тип данных может храниться в поле data однонаправленного списка?

2.     Какое начальное значение имеет переменная-указатель начала списка?

З. Какую алгоритмическую конструкцию имеет смысл использовать д.ля просмотра всех элементов списка?

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

5.     Какой допо.лните.льный параметр приходится передавать в процедуры добавления и удаления элемента списка? Чем неудобен этот параметр, особенно при удалении элемента? Почему все же применяют именно такое решение?

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

ТЕМА 20

Рекурсия

Решение ряда задач (не обязательно алгоритмических) оказывается простым и удобным, если считать, что умеешь решать такую же задачу, но меньшего размера. Такое соотношение между задачей бо.льшей и меньшей размерности называется рекуррентным, а метод решения — рекурсией.

Урок 20.1 . Описание принципа

Программа, которая в процессе работы вызывает саму себя, но меньшей размерности, называется рекуррентной. В Паскале нельзя сде.лать рекуррентной саму программу, но можно написать рекуррентную подпрограмму (процедуру или функцию). Такая подпрограмма будет содержать в своем теле хотя бы один вызов подпрограммы с точно таким же именем [2]

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


Надеемся, вы обратили внимание на главный признак («закон»)  использование решения такой же задачи, но меньшей размерности. Решение большой задачи ссы.лается на решение (такое же) меньшей. То, в свою очередь, еще меньшей. И так до... до каких пор? Здесь возникает второй «закон» рекурсиј,к д.ля некоторой размерности, самой маленькой, решение до.лжно быть примитивным и известным. При ссылках на  все бо.лее ме.лких задач нужно когда-нибудь остановиться.

В качестве первого примера приведем определение факториала. Нерекуррентное определение: п! — произведение первых п натуральных чисел.

Рекуррентное определение состоит из двух частей. Первая часть — рекуррентный переход: п! = (п — 1)! х п. Вторая часть — остановка: 1 ! = 1. Так, при рекуррентном вычислении 4! вызовется программа вычисления 3!, она вызовет вычисление 2!, она вызовет вычисление 1!, его значение известно (=1) и будет выдано д.ля вычис.ления 2! (=1 х 2), его результат (=2) — д.ля вычис.лешчя З! (=2 х З), а его результат (=6) — для вычис.ления, наконец, 4! (=6 х 4=24). Этот процесс отражен на рис. 20.1. При каждом вызове рекурсии процесс вычисления приостанав.лива.лся, запоминая свое текущее состояние, и происходи.ло вычис.ление такой же задачи, но меньшей размерности.

Рис. 20.1 . Последовательное обращение к подпрограмме при рекуррентном вычислении факториала

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

1 Значения функции факториал п! даже при не очень оо.льших значениях п становятся очень оольшими. Поэтому правильнее оы.ло оы использовать для типа возвращаемого значения функции оо.льшую размерность, чем jntegep. Хотя бы 1отдЈ Tt.

Урок 20.1 . Описание принципа

Пример 20.1. Рекуррентная функция вычисления факториала

functjon :jnteger:

ЬедЈ п

           1f п-1 then                      { Если вычисляется 1!

е ГО  ИЗ вест НО }

{ и равно 1 }

{ Если п не равно 1 }

Fact:=Fact(n-1)*n { Используем рекуррентный вызов }

Возможно, вы уже оценили элегантность такого решения по сравнению с традиционным (которое использует цик.л от 1 до п и накап.ливает значение факториала последовате.льно). Но очень важно сразу предупредить вас, что использовать рекуррентное решение нужно то.лько при необходимости. Проблема в том, что при реализации рекурсии происходит вызов подпрограмм. А вызов подпрограмм д.ля компьютера сопряжен с допо.лнительными нак.ладными расходами [3] . Поэтому если нерекуррентное решение известно и не требует от программиста значительных усилий на свою реализацию, предпочтите.льнее использовать его. Использование рекурсии це.лесообразно то.лько при значительных затруднениях в программировании другого, нерекуррентного, варианта. Либо при его отсутствии.

Задание 20.1. Напишите рекуррентную программу, которая вводит с к.лавиатуры число п и вычисляет п-й элемент последовательности Фибоначчи (последовательность чисел, начинающаяся с двух единиц, в которой каждый последующий элемент является суммой двух предыдущих).

ПРИМЕЧАНИЕ

Шы просим вас вычиспшпь эпемснт последовательности фнооначчн рекуррентным способом топько Эпя того, чтобы вы на эпют простот и понятном притере потренировапись писать рекуррентные програтты. ДЗ Действительности чиспа Фибоначчи ни в коем случае не нужно вычиспять рекуррентно, потому что при этом множество начапьных значений поспеЭоватепьности вычиспястсязаново мною раз. Три этот спожность рекуррентною раиения составпяст примерно 2d (Юс п — номер вычиспяемого эпемента поспеЭоватспьности). то время как поспеЭоватепьное вычиспение всел эпементов по порядку, запоминая всао пишь Эва преЭыЭунјих, требует поряЭка Эействий.

Урок 20.2. Ханойские башни

Одним из лучших примеров целесообразности испо.льзования рекурсии является решение задачи о Ханойских башнях. Постановка задачи такова: имеется три стержня, на один из которых надета стопка из нескольких дисков (п), все они разного размера, снизу большего диаметра, сверху — меньшего (как детская пирамидка, рис. 20.2). Нужно переложить все диски с этого стержня на соседний. При этом необходимо соблюдать два прави„ла: 1) перек.ладывать диски можно только по одному; 2) никогда диск бо.льшего диаметра не должен лежать поверх диска меньшего диаметра.

                                   в                     с

Рис. 20.2. Ханойские башни

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

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

Урок 20.2. Ханойские башни

нужно в какой-то момент переложить самый нижний диск. Это возможно то.лько в том случае, если на первом стержне надето это самое бо.льшое кольцо, а второй стержень пустой. Значит, все оста.льные диски (п —1) находятся на третьем диске. По.лучается, нужно пере.ложить стопку из п—1 дисков на третий стержень, потом пере.ложить последний диск на второй стержень. Остается пере.ложить стопку из диска на второй стержень. Такое описание решения как раз и является рекуррентным. Запишем его бо.лее формально.

Пример 20.2. Рекуррентное описание решения задачи о Ханойских башнях procedure ПереложитьСтопкуДисков(

N Сколько дисков }

                                  А C какого стержня }             

                                 В { На какой стержень }          

C Через какой стержень } :jnteger) ; begj п jf N = 1 then

ПереложитьДиск(А, В)

begj п

ПереложитьСтопкуДисков(М-1, А, ПереложитьДиск(А, С):

ПереложитьСтопкуДисков(М-1, С,

end,

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

Задание 20.2. Исправьте приведенную в примере 20.2 процедуру так, чтобы она выводила на экран последовате.льность перек.ладывания дисков, необходимую для перекладывания стопки из 5 дисков. Убедитесь, что результат содержит 31 перек.ладывание.

Урок 20.3. Структура рекуррентной подпрограммы

Пришло время сформулировать правила написания рекуррентной подпрограммы. Во-первых, обязательно нужно поставить условие остановки — условие того, что размерность задачи стала такой маленькой, что ее можно вычислить/выполнить простым, нерекуррентным способом. Во-вторых, организовать рекуррентный переход к задаче меньшей размерности. То есть вставить в тело подпрограммы вызов подпрограммы с таким же именем, что и сама описываемая подпрограмма, у которой меньший параметр размерности.

Самая простая рекуррентная программа — так называемая хвостовая рекурсия. В этом случае рекуррентный вызов стоит последней командой рекуррентной подпрограммы. Такое использование рекурсии легко для понимания, и мы ниже предложим вам написать несколько таких программ. Но нужно сознавать, что практического применения такое решение не имеет, потому что его можно легко превратить в цикл whj Ле, который, в отличие от многочисленных вызовов рекуррентных подпрограмм, не гьмеет накладных расходов при выполнении.

Для реализации хвостовой рекурсии можно пользоваться простой схемой:

1)   проверяем условия выхода из рекурсии («начальное» значение аргумента);

2)   если оно выполняется — присваиваем значение (и конец); З) если нет — выполняем нужные на этом шаге действия и 4) вызываем себя с меньшим аргументом.

Пример 20.3. Рекуррентное решение задачи: с клавиатуры вводятся целые числа. пока не будет введен ноль: посчитать их сумму functjon Sum:jntegep: var x:jntegep: begj п read1n(x):

jf x 0 then

Sum

Sum end;

Урок 20.4. Рекуррентное решение нерекуррентной задачи

Задание 20.3. Напишите рекуррентную программу, которая вводит с к.лавиатуры цифры, пока не будет введен 0, и выводит в обратном порядке.

ПРИМЕЧАНИЕ

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

Урок 20.4. Пример рекуррентного решения нерекуррентной задачи

Рассмотрим задачу нахождения приближенного значения корня некоторой функции f(x) на отрезке [a,b]. Решим эту задачу методом де.ления отрезка пополам. Решение действите.льно при ус.ловии, что на отрезке [а,Ь] функция f(x) непрерывна и гьмеет единственный корень. Будем искать решение с точностью Е, то есть найденный корень будет отличаться от истинного не бо.лее чем на Е.

Метод решения прост. Найдем середину отрезка и вычис.лим значешле f(x) в этой точке. Если значениеј(х) д.ля левой границы отрезка имеет знак отличный от знака середины, то корень находится слева от середины. Если знаки одинаковы, то корень находится справа. Будем повторять это действие д.ля соответствующей половины отрезка до тех пор, пока его длина не станет меньше Е. Тогда любую точку на отрезке можно считать приближенным значешлем корня.

В описании решения явно прослеживается рекуррентная схема: решение задачи сводится к решению такой же, но меньшей размерности (половина отрезка) и есть условие остановки рекурсии (д.лина отрезка меньше Е).

Пример 20.3. Рекуррентное решение задачи нахождения приближенного значения корня функции (методом деления отрезка пополам) functjon Djchotomy(a :реа1) : реал : var c:rea1 ЬедЈ п

jf b-a<eps then Dj chotomy

jf f(a)*f(c)<0 then

                Dj chotomy       chotomy(a , с , eps )

Dj chotomy eps)

При этом мы полагаем, что описание функции f(x) известно и приведено ранее.

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

Урок 20.5. Пример рекуррентного решения рекуррентной задачи

Пусть требуется вывести на экран все двоичные вектора веса п длины К, то есть все последовательности, состоящие из п цифр 0 и 1, в которых ровно К единиц. «Лобовое» решение этой задачи состоит в том, чтобы перебирать все возможные двогшные числа длины п, считать в них количество единиц и выводить на экран только те, в которых оказалось К единиц. Такое решение работает, но очень неэффективно. Очень большое количество чисел будет порождено зря и отброшено.

Правильнее научиться сразу порождать только нужные числа.

При использовании рекурсии это оказывается легко и удобно. Алгоритм такой: поставим на первую позицию сначала 0, потом 1 . Если мы поставили 0, то на оставшейся п—1  нужно расставить все те же К единиц. А если мы поставили 1, то на оставшейся п— 1 позиции осталось расставить еще К—1 единицу. Вот и все решение. Нужно только в каждом случае добавить проверку того, что такая расстановка возможна: что число расставляемых единиц не больше числа оставшихся позиций и что оно не меньше нуля. А также условие окончания рекурсии: что число позиций не стало равно 1 (тогда можно поставить на нее 0 или 1).

Урок 20.5. Рекуррентное решение рекуррентной задачи

Осталось подумать еще об одном: как выводить по.лучившуюся пос.ледовате.льность. Ведь наш алгоритм порождает пос.ледовательности постепенно, по одной цифре. При этом считается, что на текущую позицию ставим 0 или 1, а на тех позициях, которые уже рассмотрены, цифры уже расставлены. Значит, нужно где-то хранить эти расставленные цифры и выводить текущую пос.ледовате.льность в тот момент, когда ставится последняя цифра. Лучше всего д.ля этой цели подходит глобальный массив д.лины бо.льшеи, чем п. Чтобы при установке в массив новой цифры не высчитывать позицию, на которую она должна быть постав.лена, проще расстав.лять цифры не с начала в конец, а с конца в начало. Тогда параметр п (чис.ло позиций, на которые предстоит расставить К единиц) и будет номером той позиции, на которую устанав.ливаем текущую цифру

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

Пример 20.4. Порождение всех двоичных последовательностей длины п, в которых ровно К единиц var of jnteger: Массив для хранения порождаемой последовательности }

Ј { Счетчик цикла и длина последовательности для вывода на экран } procedure :jnteger) : begj п jf п-1 then { Если осталась одна позиция } begj п

{ Устанавливаем на первую позицию 0 или 1

То есть число оставшихся единиц,

(которое хранится в переменной К) for Ј to d1jna do wrjte(aEj1): { Выводим по одной все цифры } wrjte1n

begjn агп1      { Ставим на п-ю позицию

jf п-1>=К then { Если число позиций не меньше числа расставляемых единиц }

{ Расставляем на п-1 позиции К единиц } { Ставим на п-ю позицию ” 1” } then { Если число расставляемых единиц

                                                 не                нуля }

{ Расставляем на п-1 позиции К-1 единицу }

begj п

Выводы

1.    Программа, которая в процессе своей работы обращается к самой себе, называется рекуррентной.

2.    Рекуррентная программа должна содержать проверку условги окончания рекурсии и вызов самой себя меньшей размерности. З. Использование рекурсии рационально только в том случае, если нет возможности написать нерекуррентный алгоритм.

Контрольные вопросы

1.     Какими двумя свойствами должна обладать рекуррентная программа?

2.     Чем хорошо и чем плохо использование рекуррентных подпрограмм?

З. В каком случае рекурсия называется хвостовой?


ПРИЛОЖЕНИЕ 1

Элементы блок-схем

Таблица П. 1 . Элементы блок-схем

Наименование

Обозначение

Описание

Начало/конец

Начало и конец .люоой о.локсхемы

Действие

 

 

Любое простое (линейное) действие, кроме ввода-вывода, например оператор присваивания (А := В + С)

 

Условие

Ветвление алгоритма. Имеет два выхода — «Да» и «Нет». Проверяется условие, записанное внутри блока. Если условие истинно — далее выполняется часть олок-схемы по метке «Да».

Если ложно — по метке «Нет»

Ввод-вывод

Общение алгоритма с внешним миром. Первое слово внутри олока всегда — «Ввод» или «Вывод». Первое слово означает, что данные из внешнего мира попадают в алгоритм (ввод); второе из алгоритма выдаются во внешний мир (вывод)

Процедура

 

 

Вызов алгоритма, написанного отдельным модулем (то есть имеющим свое «Начало» и «Конец»). Обычно это вспомогательный алгоритм

 

                                                   Приложение Л . Элементы блок-схем         295

Наименование

Обозначение

Описание

Цикл со счетчиком

Внутри олока ооычно указывается выражение вида I = 1, N. Это означает, что нижеследующие олоки алгоритма (заканчивающиеся возвратом к о.локу «Цикл со счетчиком») оудут выполняться N раз. То есть на каждом шаге цикла переменная I будет последовательно принимать значения от 1 до N с шагом 1

Несколько примечаний к таблице.

1.     На языке блок-схем оператор присваивания принято ооозначать стрелкой влево — А 4— В + С. Мы используем ооозначение присвт,шания из языка Паскаль, так как в этой книге нам оно кажется более уместным.

2.     На языке блок-схем можно установить шаг цикла, не равный единице: I = 3, N, 2 — переменная I меняется от 3 до N с шагом 2.

З. Иногда в литературе обозначают параметры изменения цик.ла со счетчиком так: i 1,N .

4.     Все б.локи соединяются между собой тонкими линиями, которые до.лжны быть горизонтальными или вертика.льными (но не нак.лонными). Соединительная линия всегда до.лжна входить в б.лок сверху посередине, а выходить снизу посередине. Исключение составляет блок «условие», у которого выхода два и они могут отходить влево, вправо или вниз.

5.     Каждый блок должен иметь ровно один вход (сверху) и ровно один выход (снизу). Исключение составляют б.локи нача.лаконца, б.лок условия и цикл со счетчиком.

ПРИЛОЖЕНИЕ 2

Задачи

lnteger. Описание. Ввод. Вывод. Операции

1.    Введите с клавиатуры целое чис.ло. Выведите на экран с.ледующее чис.ло, удвоенное значение числа, противопо.ложное чис.ло, моду.ль числа, квадрат числа.

2.    Введите с клавиатуры два целых числа. Выведите на экран сумму чисел, разницу между числами, квадрат разности между числами, частное и остаток от деления первого числа на второе.

З. Введите с  два натуральных числа — длины сторон прямоугольника. Выведите на экран периметр и площадь прямоугольника.

4.        Введите с клавиатуры координаты точки на плоскости (целочисленные). Выведите на экран квадрат расстояния от начала координат до этой точки.

5.        Введите с клавиатуры целое число. Выведите на экран 4-ю, 6-ю и 8-ю степени числа. При вычислениях используйте минимальное количество действий.

Real. Описание. Ввод. Вывод. Операции и функции

6.        Введите с клавиатуры вещественное число. Выведите на экран (в неэкспоненциальном виде) модуль числа, квадрат чис.ла, умноженное на л число, арктангенс и тангенс чис.ла.

7.        Введите с клавиатуры вещественное число (радиус окружности). Выведите на экран длину этой окружности и площадь круга, который она описывает.


298

8.        Введите с клавиатуры координаты точки на п.лоскости (два вещественных числа). Выведите на экран по.лярные координаты точки (расстояние от точки до начала координат, уго.л нак.лона прямой, проведенной в точку из начала координат, к прямой ОХ), синус, косинус и тангенс угла наклона.

9.        Введите с клавиатуры вещественное число. Выведите на экран ее антье (целую часть числа), мантиссу (дробную часть чис.ла) и б.лижайшее целое к этому числу.

10.     Введите с клавиатуры два вещественных Выведите на экран их сумму, разность, произведение, частное и первое чис.ло в степени второго.

ReaI. Запись и вычисление выражений

11.     Записать на языке Паскаль:

 

5х — 61

 

Х 2 — 3 -4- 2,4

2

— 31n— 5х

12.     Записать на языке Паскаль:

а) у = 21х—3РХ + 5 ,

5

 

410gx-2 З + х

2tg2 2х— 1

                     4      

13.     Вычислить по действиям значение выражения с указанием для каждого действия типа данных результата (Real или Integer):

    а) 2-13        7/3+sqr(4);

б) abs(8-sqrt(2/0 .5)+4 mod 3);

в) 12*3 djv 5/3.5+sqr(1).

                                                                  Воо1еап. Запись выражений         299

Char. Описание. Ввод. Вывод. функции [4]

14.     Введите с клавиатуры символ. Выведите на экран три симво.ла, следующие в таблице ASCII после введенного симво.ла.

15.     С к.лавиатуры вводятся две латинские буквы (неизвестно, бо.льших или малых). Выведите на экран, стоит ли в алфавите первая буква перед второй (Да/Нет).

16.     Введите с клавиатуры три строки. Выведите на экран пос.ледовате.льность четырех символов, составленную из: первого симво.ла первой строки; второго символа второй строки; симво.ла, следующего (в ASCII) после первого симво.ла третьей строки; сукмво.ла, предшествующего (в ASCII) второму симво.лу третьей строки.

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

18.     С клавиатуры вводятся три символа: цифра, символ плюс и еще одна цифра. Выведите на экран результат операции.

Воо1еап. Запись выражений

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

Составьте для кткдого пункта логическое выражение, которое истинно, если сладость, купленная в буфете является:

а) мо.лочной шоколадкой;

б) конфетой черного шоколада с орехами;

в) черной шоколадкой без начинки;

г) изде.лием из молочного шоколада с начинкой без орехов;

д) шоко.ладкой с начинкой или конфетой без орехов;

е) изделием из черного шоколада либо с орехами, либо с начинкой (но не одновременно);

зоо

ж) мо.лочной конфетой без орехов и начинки и.ли черной шоко.ладкой с орехами;

з) мо.лочной шоколадкой без начинки или с орехами.

Резу.льтат каждого логического выражения выведите на экран.

Воо1еап. Вычисление выражений

2(). Запишите на языке Паскаль выражение, которое истинно, ес.ли переменная х принадлежит заштрихованной об.ласти:

                   1        2        з       х

б)

                    1        2        з       х

в)

                    1        2        з       х

г)

                   1        2        з        4      х

7

д)

                   1        2        З        4       х

                   1        2        З        4       х

21.     Запишите на языке Паскаль выражение, которое истинно, если переменная х:

а) принадлежит области [—1; 1] U [З; 5];

б) не принадлежит области

в) принадлежит области [—оо; 1] U [2; 4] U [5; + х].

If. Простые сравнения.

22.     Ввести с  2 различных числа. Вывести большее

из них.

                                                                                   For Перечисления         301

23.     Ввести с клавиатуры З различных числа. Вывести меньшее из них.

24.     Ввести с клавиатуры З различных числа. Вывести их в порядке возрастания.

25.     Ввести с к.лавиатуры число. Вывести на экран знак чис.ла (+1 — ес.ли число больше нуля, —1 — если чис.ло меньше ну.ля, 0 — ес.ли число равно нулю).

26.     Ввести с к.лавиатуры 4 чис.ла. Вывести на экран номер наибо.льшего из них.

27.     Ввести с клавиатуры натуральное число. Вывести, является ли число двузначным.

If. Уравнения и неравенства с параметрами

Д.ля заданий 28—32: для любых а и Ь, введенных с к.лавиатуры:

28.     Решить уравнение ах + Ь = О.

29.     Решить уравнение а.е + bx = О.

30.     Решить неравенство ах + Ь < О.

31.     Решить уравнение alxl = Ь.

32.     Решить неравенство ах? + Ь > О.

33.     Решить уравнение ах4 + bx2 + с = О для любых а, Ь и с, введенных с клавиатуры.

For. Перечисления

34.     Введите с клавиатуры 10 чисел. Выведите на экран произведение этих чисел.

35.     Выведите на экран последовательность первых 20-ти нечетных чисел (1, З, 5, 7, ...).

36.     Выведите на экран первые 15 чисел последовате.льности: 1, 4, 7, 10, 13, .

37.     Выведите на экран последовательность четных чисе.л от —30 до +30.


38.     Введите с клавиатуры два целых нечетных чис.ла (первое бо.льше второго). Выведите на экран последовате.льность нечетных чисел, расположенных между этими чис.лами.

39.     Выведите на экран первые 20 чисел последовате.льности Фибоначчи: 1, 1, 2, З, 5, 8, ... (каждый последующий равен сумме двух предыдущих).

For. Вычисления со счетчиком цикла

4(). Ввести с клавиатуры 10 целых чисел. Посчитать количество нечетных.

41.     Вывести таб.лицу кубов первых 10-ти нечетных натуральных чисел .

Пример оформ.ления решения:

Число Куб

1 1 з           27 5         125

             7        343

             9        729

42.     Вывести таблицу степеней двойки (от нулевой до десятой).

Пример оформления решения: Степень Результат

0                         1

1                         2

2                         4

8

4                      16

5                      32

43.     Вывести на экран таблицу значений выражения —2,л« + 5х-3 в диапазоне от —2 до 2 с шагом 0,5. Пример:

                                                                               Whi1e-Repeat. Поиск         зоз

-10 4

-6.1

-1.1

-0.4

44.     Ввести с клавиатуры последовательность из 5 вещественных чисе.л, и после ввода кткдого числа выводить на экран среднее арифметическое полученной части последовате.льности.

For. Перебор со сравнениями

45.     Введите с клавиатуры 10 натуральных чисел. Выведите на экран сумму четных чисел.

46.     Введите с клавиатуры 10 натуральных чисел. Выведите на экран количество двузначных чисел.

47.     Введите с клавиатуры 10 двузначных чисел. Выведите на экран сумму цифр тех из них, которые не делятся на З.

48.     Введите с клавиатуры 10 чисел. Выведите на экран количество тех из них, синус которых больше косинуса.

49.     Введите с клавиатуры натуральное число N. Выведите на экран те натуральные числа, не  N, которые делятся на 13 или на 7.

While-Repeat. Поиск

50.     С к.лавиатуры вводятся числа, пока не будет введено отрицате.льное число. Посчитать их количество.

51.     С к.лавиатуры вводятся числа, пока не будет введено чис.ло, де.лящееся на 5. Посчитать сумму тех из них, которые бо.льше 10.

52.     С клавиатуры вводятся натуральные числа, пока не будет введено не двузначное. Посчитать сумму цифр во всех двузначных числах.

53.     С к.лавиатуры вводится два натуральных чис.ла — А и В. Посчитать частное и остаток от деления А на В, не испо.льзуя операцию деления (нужно вычитать из первого второе и считать, ско.лько раз это удалось сделать).

54.     С к.лавиатуры вводятся символы, пока не будет введена точка. Посчитать количество цифр (используйте в цик.ле вместо read]n оператор read; после цикла поставьте read]n без параметров).

55.     С к.лавиатуры вводится натуральное число. Посчитать количество цифр в двоичном представлении чис.ла (нужно делить чис.ло на 2 и считать остатки).

56.     С к.лавиатуры вводится натуральное число. Посчитать двоичный вес числа (количество единиц в двоичном представ.лении чис.ла).

WhiIe-Repeat. Ряды

57.     Введите с клавиатуры целое число N (N > 1). Выведите на экран первые N чисел ряда:  Посчитайте 1,1х сумму.

58.     Введите с клавиатуры целое число N (N > 1). Выведите на

1 3 5 7 9 11

экран первые N чисел ряда: T'T7'S'T6'32' П осчитайте их сумму.

59.     Введите с клавиатуры целое число N (N > 1) и вещественное число х. Выведите на экран первые N чисе.л ряда: 2, 4х, 6х , 8х3, 10х4, Посчитайте их сумму.

60.     Введите с клавиатуры число Е (0 < Е < 1) и вещественное число х (0 < х < 1,5). Выведите на экран числа ряда:

5

ТТ'Тб%б' 65 536  которые больше Е. Посчитайте их сумму.


Графика. Окружности

Графика. Прямые

Нарисуйте следующие картинки:

61.                    62.          

63.                                      64.

65.                                     66.

Графика. Окружности

Нарисуйте следующие картинки:

67.                                     68.

69.                                       70.

71.                                      72.

Массивы. Заполнение, вывод, сумма/количество

Для массива (например, из 20 элементов):

73.     Заполнить элементы массива последовательностью чисел:

2, 5, 8, 11,

74.     Заполнить элементы массива последовательностью чисел:

2, 4, 8, 16, 32,

75.     Запо.лнить элементы массива последовательностью чисе.л: 1, з, 7, 15, 31, ..

76.     Найти произведение всех элементов массива.

77.     Найти среднее арифметическое всех элементов массива.

78.     Найти среднее отличие элементов массива от их правого соседа. Например, для массива (5, 1, З, 8) (из четырех э.лементов) имеется три пары соседних элементов (5 и 1, 1 и З, З и 8). От.личия в парах составляют 4, 2, 5 соответственно. Среднее от.личие равно (4 + 2 + 5)/3 — 3,67).

79.     Обну.лить все отрицательные элементы массива и посчитать ко.личество остальных.

80.     Все четные положительные элементы целочисленного массива уменьшить вдвое, все нечетные положительные увеличить на 2, а у всех остальных поменять знак.

81.     Найти количество отрицательных элементов массива.

82.     Найти среднее арифметическое всех нечетных элементов целочисленного массива.

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

Массивы. Перестановки

Д.ля некоторого массива (например, из 20 элементов):

84.     Развернуть массив в обратном порядке.

85.     Поменять местами элементы массива в парах (из массива

(1, 2, З, 4, 5, 6, 7, 8, 9, 10) нужно получить массив (2, 1, 4, З, 6, 5, 8, 7, 10,

Массивы. Поиск

86.     Сдвинуть все элементы массива на одну позицию в.лево (ци-

к.лически). Первый элемент должен оказаться на месте по-

с.леднего.

87.     Сдвинуть все элементы массива на одну позицию вправо (цик.лически). Последний элемент должен оказаться на месте первого.

88.     Развернуть обе половинки массива в обратном порядке. Считать, что массив имеет четное чис.ло э.лементов (из массива (1, 2, З, 4, 5, 6, 7, 8, 9, 10) нужно по.лучить массив

(5, 4, з, 2, 1, 10, 9, 8, 7,

89.     Поменять местами половинки массива (из массива (1, 2, З,

4, 5, 6, 7, 8,9, 10) нужно получить массив (6, 7, 8, 9, 10, 1, 2, З,

90.     Ввести два целых числа от 1 до 20 (первое меньше второго). Развернуть в обратную сторону часть массива, расположенную между элементами с такими номерами.

91.     Сдвинуть все элементы массива на К позиций влево (циклически). Каждый элемент нужно переставлять не более двух раз. Разрешается использовать не более К дополнительных ячеек памяти.

Массивы. Поиск

Во всех заданиях этого блока считать неизвестным, имеется ли в массиве искомый элемент.

Для некоторого массива (например, из 20 элементов):

92.     Найти номер первого положительного элемента массива.

93.     Найти номер второго четного элемента массива.

94.     Найти номер предпоследнего нечетного элемента массива.

95.     Ввести с клавиатуры число. Найти номер э.лемента массива, равного этому чую.лу.

96.     Найти д.лину самой длинной последовательности одинаковых

э.лементов.

Массивы. Проверки

Д.ля некоторого массива (например, из 20 элементов):

97.     Проверить, что в массиве все элементы положите.льны.

98.     Проверить, что все элементы массива равны друг другу.

99.     Проверить, есть ли в массиве элемент, равный заданному значению (ввести с клавиатуры).

100.  Проверить, что массив упорядочен по возрастанию (каждый элемент массива больше своего левого соседа или равен ему). 101. Проверить, что в массиве все элементы кратны трем и больше нуля.

102.     Проверить, что в массиве есть более чем два различных элемента.

103.     Проверить, что в массиве нет ни одного одинакового элемента.

Массивы. Максимумы

Для некоторого массива (например, из 20 элементов):

Найти номер наибольшего элемента.

105.     Найти номер элемента, наименее отличающегося от среднего арифметического всех его элементов.

106.     Посчитать количество элементов, равных максимальному.

107.     Найти номера элементов, равных максимальному.

108.     Найти номер наибольшего отрицательного элемента.

109.     Найти номер элемента массива, наиболее отличающегося от своих соседей (по сумме модулей разностей).

110.     Найти номер элемента, значение которого меньше максимума, но больше всех остальных элементов.

Подпрограммы без параметров

1 11. Напишите процедуру, выводящую на экран вертика.льно 20 раз слово «Hello!»

Строки. Часть

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

113.     Напишите функцию, вычисляющую количество трехзначных чисел, делящихся на 13 и не оканчивающихся на З.

114.     Напишите подпрограмму, которая выводит на экран таб.лицу умножения размером 9х9.

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

116.     Напишите подпрограмму, которая сравнивает два введенных с клавиатуры числа и возвращает результат их сравнения (первое больше, меньше или равно второму).

Строки. Часть

117.     Ввести строку. Поменять местами ее левую и правую пол овины .

118.     Ввести строку. Посчитать количество пробелов в ней.

119.     Ввести строку. Удалить из нее все лишние пробелы (все подряд стоящие пробелы большие, чем один).

120.     Ввести строку. Заменить все слова Masha на слова I rj sha.

121.     Ввести строку. Посчитать количество цифр в ней.

122.     Ввести строку. Определить, есть ли в ней хоть одна заг.лавная „латинская буква.

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

Строки. Часть ll

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

124.     Ввести строку, в которой содержатся две точки. Вывести на экран часть строки, находящуюся между этими точками.

125.     Ввести строку. Вывести на экран эту же строку столько раз, сколько точек содержится в строке.

126.     Ввести строку, состоящую из трехзначного числа, запятой и еще одного трехзначного числа (всего 7 символов). Вывести на экран результат сравнения этих двух чисел (больше, меньше или равно).

127.     Ввести строку, состоящую из некоторого количества точек, знака «минус» и еще некоторого количества точек (меньшего первого). Вывести на экран «разницу» — столько точек, сколько останется, если из первого количества точек «отнять» второе количество.

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

129.     Ввести строку, состоящую из трех чисел, разделенных запятыми. Вывести на экран большее из трех чисел.

11одпрограммы с параметрами. Часть

130.     Напишите процедуру, выводящую на экран указанное число раз указанную текстовую строку.

131.     Напишите функцию sjgn (возвращает знак це.лого чис.ла: +1 ,

0 и.ли —1)

132.     Напишите функцию, вычисляющую значение чис.ла Х, возведенного в степень У.

133.     Напишите процедуру, удваивающую значение числа.

134.     Напишите процедуру, меняющую два числа местами.

Подпрограммы с параметрами. Часть l l l

135.     Напишите функцию, возвращающую количество раз, которое одна строка встречается в другой строке.

Подпрограммы с параметрами. Часть II

136.     Напишите процедуру, выводящую на экран все делители указанного числа.

137.     Напишите функцию, проверяющую, является ли число простым.

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

139.     Напишите функцию, возвращающую количество цифр в десятичном представлении целого числа.

140.     Напишите процедуру, «разворачивающую» целое число в обратном порядке (в десятичной записи).

Напишите функцию, проверяющую, является ли число палиндромом в десятичном представлении.

11одпрограммы с параметрами. Часть I I I

Напишите подпрограмму с параметрами, которая:

142.     Из четырех чисел выдает число, наиболее близкое к среднему арифметическому

143.     Выдает номер старшего разряда двоичного представленгш натурального числа.

144.     Вычисляет число ненулевых двоичных разрядов натурального

145.     Выполняет разворот двоичного представления натурального числа.

146.     Вычисляет средний бит двоичного представления натурального         (среди значащих цифр).

147.     Меняет местами старшую и младшую половинки двоичного представления натурального числа.

148.     Выпо.лняет циклический сдвиг влево двоичного представ.ления натурального числа (старший значащий бит (всегда = 1 ) поставить в конец, остальные сдвинуть влево).

Файлы

Для некоторого текстового файла:

149.     Посчитать число строк файла.

150.     Найти самую длинную строку.

151.     Посчитать число слов в файле. Слова считать разделенными одним или несколькими пробелами.

152.     Породить новый файл с переставленным порядком строк (в парах).

Пример:

Было Стало

1                    2 2                  1 з                  4 4                 

                 5            5

153.     Посчитать количество строк файла, которые не содержат слово «begin».

154.     Вывести на экран строки файла, в которых совпадают первый и последний символы.

155.     Вывести на экран все строки файла развернутыми в обратную сторону.

156.     Вывести на экран строки файла нечетной длины, в которых средний символ — цифра.

157.     Вывести на экран все строки файла, в них все цифры. 158. Вывести на экран строки файла, которые содержат не менее двух запятых.

159.     Записать в другой файл только те строки исходного фай.ла, в которых число цифр больше числа пробелов.

160.     Посчитать, сколько раз в файле встречается отде.льное с.лово «end» (слева и справа от слова находятся пробе.лы и.ли нача.ло конец строки).

Однонаправленный список

161.     Д.ля текстового файла (например, программы на Паскале) определить, соответствует ли каждому слову «begin» свое с.лово «end». Считать, что программа не содержит комментариев и апострофов. Не обязательно считать, что эти «begin» и «end» яв.ляются отдельными словами.

162.     Вывести файл на экран со словами, в обратном порядке. Слова считать разделенными одним и.ли неско.лькими пробелами.

Пример: Строку «Вася Петя Саша» вывести как «Саша Петя Вася».

Однонаправленный список

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

163.     Заполнить однонаправленный список случайным количеством (10—20) случайных целочисленных значений (на интервале —15...+15). Вывести получившийся список на экран. 164. Посчитать количество нечетных элементов списка. Вывести результат на экран.

165.     После каждого нечетного элемента списка добавить элемент, который на один больше. Вывести получившийся список на экран.

166.     Удалить все четные элементы списка. Вывести получившийся список на экран.

167.     Перед каждым положительным элементом списка добавить элемент, равный нулю. Вывести получившийся список на экран.

168.     Поменять местами соседние элементы списка ( 1—2, 3—4, ...). Вывести получившийся список на экран.

169.     Найти самый большой и самый маленький элементы списка и уве.личить на 50 все элементы, находящиеся между ними. Вывести получившиися список на экран.

170.     Пос.ледовательно, строчка за строчкой, вывести на экран э.лементы списка, значения которых принад.лежат первому, второму, третьему и т. д. десяткам натуральных чисе.л.

Рекурсия

171.     Ввести натуральное число. Вывести на экран его дворщное представ.ление.

172.     Ввести натуральное число. Вывести старшую цифру его десятичного представления.

173.     Ввести натуральное число. Вывести наименьшее число, которое является степенью двоики и которое не меньше данного числа.

174.     Ввести натуральное число. Вывести число, в котором об ратный порядок цифр.

175.     Найти наибольший общий делитель двух натуральных чисел, введенных с клавиатуры.

176.     Вывести на экран все возможные комбинации числа в К-й системе счисления. М и К — натуральные, вводятся с клавиатуры (пример ЗИК 2: 000, 001, 010, 01 1, 100, 101, 110, 111).

177.     Вывести на экран все возможные комбинации N различных цифр. N — целое, от 2 до 9, вводится с клавиатуры (вывести все перестановки).

178.     Ввести с клавиатуры натуральное число. Вывести все представ.ления числа в виде суммы натуральных чисе.л, без повторений. Пример: 4 = 3+ 1 = 2 + 2 = 2+ 1 + 1 = 1 + 1 + 1 + 1.


Денис Михайлович Ушаков, Татьяна Анатольевна Юркова Паскаль для школьников

2-е издание

Заведующий редакцией

А. Кривцов

Руководитель проекта

А. Юрченко

Ведущий редактор

Ю. Сергиенко

Литературный редактор

О. Некруткина

Художественный редактор

.И. АДуевская

Корректор

Н. Периакова

Верстка

Е. Егорова

Подписано в печать 14.11.10. Формат 60х90/16. У сл. п. л. 21. Тираж 3000. Заказ ООО «Лидер», 194044, Санкт-Петербург, Б. Сампсониевский пр., 29а.

Налоговая льгота          общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 литература учебная.

Отпечатано в соответствии с предоставленными материалами в ЗАО «ИПК Парето-Прин», г. Тверь, www.pareto-print.m

пзпдтепьскпй пом

7nnrpWWW.PlTER.COM

ПРЕДСТАВИТЕЛЬСТВА ИЗДАТЕЛЬСКОГО докт «ПИТЕР» предлагают эксклюзивный ассортимент компьютерной, медицинской, психологической, экономической и популярной литературы

РОССИЯ

Санкт-Петербург м. «Выборгская», Б. Сампсониевский пр., д. 29а тел./факс: (812) 703-73-73, 703-73-72; e-mail: sales@piter.com

Москва м. «Электрозаводская», Семеновская наб., д. 2/1, корп. 1, 6-й этаж тел./факс: (495) 234-38-15, 974-34-50; e-mail: sales@msk.piter.com

Воронеж Ленинский пр., д. 169; тел./факс: (4732) 39-61-70 e-mail: piterctr@comch.ru

Екатеринбург ул. Бебеля, д. 1 1а; тел./факс: (343) 378-98-41, 378-98-42 e-mail: office@ekat.piter.com

Нижний Новгород ул. Совхозная, д. 13; тел.: (8312) 41-27-31 e-mail: office@nnov.piter.com


Новосибирск ул. Станционная, д. 36; тел.: (383) 363-01-14 факс: (383) 350-19-79; e-mail: sib@nsk.piter.com

Ростов-на-Дону ул. Ульяновская, д. 26; тел.: (863) 269-91-22, 269-91 -30 e-mail: piter-ug@rostov.piter.com

Самара ул. Молодогвардейская, д. 33а; офис 223; тел.: (846) 277-89-79 e-mail: pitvolga@samtel.ru

УКРАИНА

Харьков ул. Суздальские ряды, д. 12, офис 10; тел.: (1038057) 751-10-02 758-41-45; факс: (1038057) 712-27-05; e-mail: piter@kharkov.piter.com

Киев Московский пр., д. 6, корп. 1, офис 33; тел.: (1038044) 490-35-69 факс: (1038044) 490-35-68; e-mail: office@kiev.piter.com

БЕЛАРУСЬ минск ул. Притыцкого, д. И, офис 2; тел./факс: (1037517) 201-48-79, 201 -48-81 e-mail: gv@minsk.piter.com

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

Телефон для связи: (812) 703-73-73. E-mail: fuganov@piter.com

..у Издательский дом «Питер» приглашает к сотрудничеству авторов. Обращайтесь по телефонам: Санкт-Петербург — (812) 703-73-72, Москва — (495) 974-34-50

.у Заказ книг для вузов и библиотек по тел.: (812) 703-73-73.

Специальное предложение — e-mail: kozin@piter.com

Заказ книг по почте: на сайте www.piter.com; по тел.: (812) 703-73-74 по lCQ 413763617


пзадппьскпй дом УВАЖАЕМЫЕ господА! КНИГИ ИЗДАТЕЛЬСКОГО ДОМА «ПИТЕР»


7nnrpWWW.PITER.COM

ДАЛЬНИЙ ВОСТОК

Владивосток

«Приморский торговый дом книги» тел факс: (4232) 23-82-12 e-mail: bookbase@mail.primorye.ru

Хабаровск, «Деловая книга», ул. Путевая, д. 1а тел.: (4212) 36-06-65, 33-95-31 e-mail: dkniga@mail.kht.ru

Хабаровск, «Книжный мир» тел.: (4212) 32-85-51 , факс: (4212) 32-82-50 e-mail: postmaster@worldbooks.kht.ru

Хабаровск, «Мирс» тел.: (4212) 39-49-60 e-mail: zakaz@booksmirs.ru

ЕВРОПЕЙСКИЕ РЕГИОНЫ РОССИИ

Архангельск, «Дом книги», пл. Ленина, д. 3 тел.: (8182) 65-41-34, 65-38-79 e-mail: marketing@avfkniga.ru

Воронеж, «Амиталь», пл. Ленина, д. 4 тел.: (4732) 26-77-77 http://ww.amital.ru

Калининград, «Вестер», сеть магазинов «Книги и книжечки» тел./факс: (4012) 21-56-28, 65-65-68 e-mail: nshibkova@vester.ru http:TMw.vester.ru

Самара, «Чакона», ТЦ «Фрегат» Московское шоссе, д. 15 тел.: (846) 331-22-33 e-mail: chaconne@chaccone.ru

Саратов, «Читающий Саратов», пр. Революции, д. 58 тел.: (4732) 51-28-93, 47-00-81 e-mail: manager@kmsvrn.ru

СЕВЕРНЫЙ ПвПз

Ессентуки, «Россы», ул. Осябрьская, 424 тел ./факс: (87934) 6-93-09 e-mail: rossy@kmw.ru

ВЫ МОЖЕТЕ ПРИОБРЕСТИ ОПТОМ И В РОЗНИЦУ У НАШИХ РЕГИОНАЛЬНЫХ ПАРТНЕРОВ.

СИБИРЬ

Иркутск, «ПродаЈ1итЪ» тел.: (3952) 20-09-17, 2$17-77 e-mail: prodalit@irk.ru http://MM.prodalit.irk.ru

Иркутск, «Светлана» тел./факс: (3952) 25-25-90 e-mail: kkcbooks@bk.ru http://ww.kkcbooks.ru

Красноярск, «Книжный мир» пр. Мира, д. 86 тел./факс: (3912) 27-39-71 e-mail: book-world@public.krasnet.ru

Новосибирск, «Топ-книга» тел.: (383) 336-10-26 факс: (383) 336-10-27 e-mail: office@top-kniga.ru http://wM.top-kniga.ru

ТАТАРСТАН Казань, «Таис», сеть магазинов «Дом книги» тел.: (843) 272-34-55 e-mail: tais@bancorp.ru

УРАЛ

Екатеринбург, ООО «Дом книги» ул. Антона Валека, д. 12 тел./факс: (343) 358-18-98, 358-14-84 e-mail: domknigi@k66.ru

Екатеринбург, ТЦ «Люмна» ул. Студенческая, д. 1 в тел./факс: (343) 228-10-70 e-mail: igm@lumna.ru http://wM.lumna.ru

Челябинск, ООО «ИнтерСервис ЛТД» ул. Артиллерийская, д. 124 тел.: (351) 247-74-03, 247-7Ф09, 247-7Ф16 e-mail: zakup@intser.ru http://wM.fkniga.ru, wwj.intser.ru


ВАМ НРАВЯТСЯ НАШИ книги? ЗАРАБАТЫВАЙТЕ ВМЕСТЕ С НАМИ!

У Вас есть свой сайт?

Вы ведете блог?

Регулярно общаетесь на форумах? Интересуетесь литературой, любите рекомендовать хорошие книги и хотели бы стать нашим партнером?

ЭТО ВПОЛНЕ РЕАЛЬНО!

СТАНЬТЕ УЧАСТНИКОМ

ПАРТНЕРСКОЙ ПРОГРАММЫ ИЗДАТЕЛЬСТВА «ПИТЕР»!

Зарегистрируйтесь на нашем сайте в качестве партнера по адресу www.piter.com/ePartners

Получите свой персональный уникальный номер партнера

Выбирайте книги на сайте www.piter.com, размещайте информацию о них на своем сайте, в блоге или на форуме и Добавляйте в текст ссылки на эти книги (на сайт www.piter.com)

ВНИМАНИЕ! В каждую ссылку необходимо Добавить свой персональный уникальный номер партнера.

C этого момента получайте 10% от стоимости каждой покупки, которую совершит клиент, придя в интернет-магазин «Питер» по ссылке с Вашим партнерским номером. А если покупатель приобрел не только эту книгу, но и другие издания, Вы получаете дополнительно по 5% от стоимости каждой КНИГИ.

Деньги с ви ртуального счета Вы можете потратить на покупку книг в интернетмагазине издательства «Питер», а также, если сумма будет больше 500 рублей, перевести их на кошелек в системе Яндекс.Деньги или Web.Money.

11 ример партнерской ссылки:

http://www.piter.com/book.phtmL?978538800282 - обычная ссылка http://www.piter.com/book.phtmL?978538800282&refer=OOOO - партнерская ссылка, где 0000 - это ваш уникальный партнерский номер

Подробно о Партнерской программе

ИД «Питер» читайте на сайте                                              пзааге,тьскпй аом

WWW.PITER.COM                                                                                        Ипптер

WWW.FlTEH.COM

ЗАКАЗАТЬ КНИГИ ИЗДАТЕЛЬСКОГО доМ «ПИТЕР» МОЖНО ЛЮБЫМ УДОБНЫМ ДЛЯ ВАС спосоБом: о на нашем сайте: uvw.piter.com о по электронной почте: postbook@piter.com  по телефону: (812) 703-73-74 о по почте: 197198, Санкт-Петербург, а/я 1 27,

ООО «Питер Мейл»  по lCQ: 413763617

ВЫ МОЖЕТЕ ВЫБРАТЬ ЛЮБОЙ УДОБНЫЙ ДЛЯ ВАС СПОСОБ ОПЛАТЫ:

Наложенным платежом с оплатой при получении в ближайшем почтовом отделении.

С помощью банковской карты. Во время заказа Вы будете перенаправлены на защищенный сервер нашего оператора, где сможете ввести свои данные для оплаты.

Электронными деньгами. Мы принимаем к оплате все виды электронных денег: от традиционных Яндекс.Деньги и Webтопеу до USD E-Gold, MoneyMail, lNOCard, RBk Мопеу (RuPay), USD Bets, Mobile WaIIet и др.

В любом банке, распечатав квитанцию, которая формируется автоматически после совершения Вами заказа.

Все посылки отправляются через «Почту России». Отработанная система позволяет нам организовывать доставку Ваших покупок максимально быстро. Дату отправления Вашей покупки и предполагаемую дату доставки Вам сообщат по e-mail.

ГРИ ОФОРМЛЕНИИ ЗАКАЗА УКАЖИТЕ:

   фамилию, имя, отчество, телефон, факс, e-mail;

   почтовый индекс, регион, район, населенный пункт, улицу, дом, корпус, квартиру;

   название книги, автора, количество заказываемых экземпляров.

пзааткпвскпй аом

ИпптертWWW.PIVER.COM

www.piter.com

И наконец, вы нигде не купите наши книги дешевле!



[1] В этой теме мы оудем использовать термин «список» как синоним термина «однонаправленный список». В оощем случае список может оыть каким-ниоудь еще, например двунаправленным.

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

[3] Если вам интересно, при вызове подпрограмм процессор должен приостановить вычисление текущей задачи и начать решать новую. Чтооы он имел возможность вернуться в то же свое состояние, в котором находился до приостановки, процессор должен запомнить всю текущую «среду вычислений». А после возврата в это место восстановить всю «среду вычислений» ооратно. Это запоминание и восстановление треоует некоторого, не слишком короткого, времени.

[4] При необходимости используйте функцию upcase(x), она преобразует строчную латинскую оукву в прописную. Любой другой символ остается оез изменений.