МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ ФГБОУ ВПО «ЧЕЧЕНСКИЙ ГОСУДАРСТВЕННЫЙ ПЕДАГОГИЧЕСКИЙ ИНСТИТУТ» «УТВЕРЖДЕНО» Декан физико-матем.факультета Доцент Э.М. Джамбетов ______________________ «_____»__________201__г. УЧЕБНО-МЕТОДИЧЕСКИЙ КОМПЛЕКС по дисциплине « ПРОГРАММИРОВАНИЕ» Составитель: доцент Хатаева Р.С. Грозный - 2011г. 1 УМК рекомендован на заседании кафедры информатики, « __ » ________ 2011г. Зав. кафедрой информатики, ________________________ /Хатаева Р.С./ Составитель: ___________________________________ доцент Р.С. Хатаева 2 СОДЕРЖАНИЕ Программно-планирующий блок. Рабочая программа 1.1 Пояснительная записка. Требования ГОС ВПО к обязательному минимуму содержания основной образовательной программы подготовки специалиста………………………..………6 1.2. Цель и задачи………………………………………………………………………..6 1.3. Место дисциплины в структуре ООП…………………………………………….7 1.4. Требования к уровню освоения содержания дисциплины ……………………...7 2. Тематический план 2.1. Объём дисциплины и виды учебной работы………………………………...…….8 2.2. Распределение учебного времени по разделам и темам ……………………….…9 3. Содержание дисциплины 3.1. Содержание лекций ………………………………………………………………....10 3.2. Содержание лабораторных занятий…………….. ………… …………………......11 3.3. Содержание самостоятельной работы студентов.………………………………..13 3.4. Вопросы для итогового контроля…………………………………………………14 3.5. Критерий оценки знаний…………………………………………………………..16 3.6. Список основной и дополнительной литературы………………………………..16 Учебно-методический блок 4.1. Теоретическая часть………………………………………………………………….17 4.2. Лабораторный практикум. Тематика заданий……………………………………...95 4.3. Методические рекомендации для преподавателей..…………………………… ...119 4.4. Методические рекомендации для студентов……………………………………... 119 4.5. Глоссарий ……………………………………………………………………………121 Блок наглядно-дидактического материала Лекции-презентации……………………………………………………………………..123 3 МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ ФГБОУ ВПО «ЧЕЧЕНСКИЙ ГОСУДАРСТВЕННЫЙ ПЕДАГОГИЧЕСКИЙ ИНСТИТУТ» «УТВЕРЖДЕНО» Декан физико-матем. факультета ___________ Э.М. Джамбетов «_____»__________201__г. КАФЕДРА ИНФОРМАТИКИ РАБОЧАЯ ПРОГРАММА по дисциплине «Программирование» для специальности 050303.65-«Английский язык и информатика» Форма обучения Очная Согласовано Начальник УМО Рекомендована на заседании кафедры Протокол № . от «__» ________ 2011г. _______________( Идрисова Р.А.) « » 2011 г. ( Грозный - 2011г. 4 ) Рабочая программа дисциплины Грозный: ЧГПИ, 2011г. «Программирование» /сост. Хатаева Р.С.– Рабочая программа предназначена для преподавания дисциплины профессионального цикла (Б студентам очной формы обучения для специальности 050303.65-«Английский язык и информатика». Рабочая программа составлена с учетом Государственного образовательного стандарта высшего профессионального образования по направлению подготовки дипломированных 050303.65«Английский язык и информатика», утвержденного 14.04.2000г. Министерством образования РФ. Составитель ____________________ ( (подпись) Хатаева Р.С.) «_________»______ __________2011 Дополнения и изменения в учебной программе на 2012 / 2013 учебный год В рабочую программу вносятся следующие изменения: _________________ ______________ ________________________________________________________________________________ Рабочая программа пересмотрена и одобрена на заседании кафедры информатики «___»___________2012 г. протокол № Заведующий кафедрой ____Хатаева Р.С.____________________ (Ф.И.О., подпись) Дополнения и изменения в учебной программе на 20 / 20 учебный год В рабочую программу вносятся следующие изменения: ________________________________ ________________________________________________________________________________ Рабочая программа пересмотрена и одобрена на заседании кафедры информатики «___»___________20 г. протокол № Заведующий кафедрой ____Хатаева Р.С.____________________ (Ф.И.О., подпись) Дополнения и изменения в учебной программе на 20 / 20 учебный год В рабочую программу вносятся следующие изменения: ________________________________ ________________________________________________________________________________ Рабочая программа пересмотрена и одобрена на заседании кафедры информатики «___»___________20 г. протокол № Заведующий кафедрой ____Хатаева Р.С.____________________ (Ф.И.О., подпись) Дополнения и изменения в учебной программе на 20 / 20 учебный год В рабочую программу вносятся следующие изменения: ________________________________ ________________________________________________________________________________ Рабочая программа пересмотрена и одобрена на заседании кафедры информатики «___»___________20 г. протокол № Заведующий кафедрой ____Хатаева Р.С._________________ 5 1.1. Пояснительная записка Учебная дисциплина «Программирование» является общепрофессиональной дисциплиной, формирующей базовый уровень знаний для освоения специальных дисциплин. Преподавание дисциплины должно иметь практическую направленность и проводиться в тесной взаимосвязи с другими общепрофессиональными дисциплинами: «Информационные технологии», «Операционные системы и среды», «Дискретная математика», «Архитектура ЭВМ и вычислительных систем». В результате изучения дисциплины студент должен иметь представление: о роли и месте знаний по дисциплине при освоении смежных дисциплин по выбранной специальности и в сфере профессиональной деятельности; о направлениях развития программного обеспечения вычислительной техники; знать: принципы построения алгоритмов; типы данных и базовые конструкции изучаемых языков программирования; основные приемы программирования; интегрированные среды изучаемых языков программирования; основы объектно-ориентированного программирования; уметь: составлять простые блок-схемы алгоритмов; составлять программы на алгоритмическом языке высокого уровня; работать в интегрированной среде изучаемых языков программирования. Настоящая программа учебной дисциплины рассчитана на 99 часов аудиторных занятий, в том числе 45 часов отводится на практические занятия для специальности 050303.65- «Английский язык и информатика» по каждой теме приведены требования к формируемым представлениям, знаниям и умениям. С целью систематизации и закрепления полученных теоретических знаний и практических умений образовательному учреждению рекомендуется в рабочей программе учебной дисциплины предусмотреть самостоятельную работу студентов. При разработке рабочей программы учебной дисциплины образовательное учреждение в зависимости от профиля и специфики подготовки специалистов при условии обязательного выполнения государственных требований по конкретной специальности может вносить изменения в содержание, уровень знаний и умений, последовательность изучения учебного материала и распределение учебных часов по разделам (темам), а также в перечень практических занятий, не нарушая логики изложения дисциплины и не снижая заявленного в программе уровня. 1.2. Цели и задачи изучения дисциплины В процессе развития информатики как прикладной науки появились разные подходы к программированию. Курс "Программирование" призван содействовать знакомству студентов с различными парадигмами проектирования и разработки программного обеспечения. Он важен с той точки зрения, что, являясь составной частью подготовки учителя информатики, способствует развитию алгоритмического мышления, навыков программирования студентов. Цель дисциплины: изучение методов программирования для овладения знаниями в области технологии программирования; подготовка к осознанному использованию как языков программирования, так и методов программирования. Воспитательной целью дисциплины является формирование у студентов научного, творческого подхода к освоению технологий, методов и средств производства программного обеспечения. 6 Основные задачи курса программирования на основе структурного и объектноориентированного подхода: знакомство с методами структурного и объектно-ориентированного программирования как наиболее распространенными и эффективными методами разработки программных продуктов; обучение разработке алгоритмов на основе структурного и объектноориентированного подхода; закрепление навыков алгоритмизации и программирования на основе изучения языка программирования Object Pascal; знакомство с основными структурами данных и типовыми методами обработки этих структур; создание практической базы для изучения других учебных дисциплин, таких, как "Численные методы", "Компьютерное моделирование" и др. Отбор материала основывается на необходимости ознакомить студентов со следующей современной научной информацией: о парадигмах программирования (императивной, функциональной, логической); о технологиях программирования (структурной, модульной, объектноориентированной); об аспектах формализации синтаксиса и семантики языков программирования. Содержательное наполнение дисциплины обусловлено общими задачами в подготовке учителя математики и информатики. Изучение дисциплины базируется на знании математических дисциплин и общего курса информатики. Концепция дисциплины основана на том, что эта дисциплина имеет общеобразовательный и в определенной степени мировоззренческий характер и предназначена для формирования учителя математики и информатики с широким научным кругозором. Научной основой для построения программы данной дисциплины является теоретико-прагматический подход в обучении. 1.3. Требования к уровню освоения дисциплины Выписка из ГОС ВПО специальности 050303.65-«Английский язык и информатика». содержащая требования к обязательному минимуму содержания дисциплины. ДПП.Ф.14. Программирование. Объектно-ориентированная парадигма программирования. Объекты, полиморфизм и наследование. Объектно-ориентированное проектирование. Конструирование объектов: строки, стеки, списки, очереди, деревья. Математические объекты: рациональные и комплексные числа, вектора, матрицы. Библиотеки объектов. Интерфейсные объекты: управляющие элементы, окна, диалоги. События и сообщения. Механизмы передачи и обработки сообщений в объектноориентированных средах. Конструирование программ на основе иерархии объектов. В результате изучения дисциплины студент должен: иметь представление: о конструировании алгоритмов, методах структурного и модульного программирования, 7 абстракциях основных структур данных (списки, множества и т.п.) и методах их обработки и способах реализации, методах и технологиях программирования; уметь: разрабатывать алгоритмы, реализовывать алгоритмы на языке программирования высокого уровня, описывать основные структуры данных, реализовывать методы обработки данных, работать в средах программирования; приобрести навыки: структурного программирования, алгоритмизации, работы в среде программирования (составление, отладка и тестирование программ; разработка и использование интерфейсных объектов). владеть, иметь опыт: разработки алгоритмов, описания структур данных, описания основных базовых конструкций, программирования на языке высокого уровня, работы в различных средах программирования. 2. Объем дисциплины 2.1. Объем дисциплины и виды учебной работы Форма обучения очная Вид учебной работы Семестры Всего часов 3 4 5 99 23 36 36 54 18 18 18 Практические занятия (ПЗ) 45 9 18 18 Самостоятельная работа (всего): 563 187 188 188 63 63 63 62 62 62 Аудиторные занятия (всего): В том числе: Лекции (Л) Лабораторные занятия (ЛЗ) В том числе: Реферат Курсовые работы Темы для самостоятельного изучения Подготовка к зачету 8 Вид промежуточной аттестации 62 Виды отчетности зач 688 Общая трудоемкость дисциплины час. 27 63 63 экз 36 36 2.2. Распределение часов по темам и видам учебной работы Наименование тем и разделов Всего часов № Аудиторные занятия, час. п/п лек ПЗ Самостоятельная работа, час. ЛЗ 3 семестр 214 18 9 187 Введение в алгоритмизацию и программирование. Методологии программирования Алгоритмические структуры 52 4 2 46 53 4 2 47 Синтаксис и семантика формального языка Структурный подход к программированию. Основные конструкции алгоритмических языков 4 семестр 53 4 2 47 57 6 3 47 224 18 18 188 Простые типы данных языка программирования Основные операторы языка 43 3 3 37 43 3 3 37 Структурированные типы языка программирования высокого уровня Алгоритмы поиска и сортировки 46 4 4 38 46 4 4 38 Модульное программирование. Программирование абстрактных типов данных 5 семестр 46 4 4 38 224 18 18 188 10 Процедуры и функции. Модули 43 3 3 37 11 Организация динамических структур данных(абстрактных типов данных): стек, очередь, двоичное дерево поиска. 43 3 3 37 12 Объектно-ориентированное программирование. Введение в объектно-ориентированное 46 4 4 38 1 2 3 4 5 6 7 8 9 9 13 14 программирование Реализация абстракций данных методами объектно-ориентированного программирования Объектно-событийное и объектно-ориентированное программирование Итого 46 4 4 38 46 4 4 38 688 54 45 563 3. Содержание курса 3.1.Содержание курса лекций Раздел 1. Введение в алгоритмизацию и программирование Тема 1. Методологии программирования. Программирование как раздел информатики. Метафоры (парадигмы) программирования. Методологии программирования. Основные понятия и определения. История и эволюция. Классификация по ядрам методологии: императивное программирование, объектно-ориентированное, функциональное, логическое. Топологическая специфика методологий. Тема 2. Алгоритмические структуры. Этапы решения задач на ЭВМ. Понятие алгоритма. Исполнитель, система команд исполнителя. Свойства алгоритмов. Способы записи алгоритмов. Принципы структурного программирования. Основные алгоритмические структуры и их суперпозиции. Тема 3. Синтаксис и семантика формального языка. Естественные и формальные языки. Понятия о синтаксисе и семантике формального языка. Нормальные формы Бэкуса-Наура и синтаксические диаграммы Вирта. Язык программирования. Классификация языков программирования. Система программирования. Раздел 2. Структурный подход к программированию Тема 4. Основные конструкции алгоритмических языков. Общие конструкции алгоритмических языков: алфавит, величина (тип, имя и значение). Выражение. Тип выражения. Арифметическое выражение. Символьное выражение. Логическое выражение. Стандартные функции. Структура программы. Тема 5. Простые типы языка программирования. Общая характеристика языка Object Pascal. Структуры данных: упорядоченность, однородность, способ доступа. Определение констант. Описание переменных. Стандартные типы данных. Целые типы. Символьный и булевский типы данных. Эквивалентность и совместимость типов. Типы, определяемые программистом: перечисляемый, интервальный. Тип дата-время. Тема 6. Основные операторы языка. Перечень операторов Object Pascal. Оператор присваивания. Операторы (процедуры) ввода-вывода. Управление выводом данных в консольном режиме (простейшее форматирование). Условный оператор. Логические выражения. Оператор множественного ветвления. Операторы цикла: с предусловием, с постусловием, с параметром. 10 Тема 7. Структурированные типы языка программирования высокого уровня. Массивы. Примеры задач с численными, символьными, булевскими массивами. Строковый тип данных. Записи. Оператор присоединения. Записи с вариантами. Множественный тип. Задание множественного типа и множественной переменной. Операции над множествами. Операции отношения. Примеры задач на множественный тип. Файлы. Понятие логического и физического файлов. Файловые типы. Общие процедуры для работы с файлами. Типизированные файлы. Текстовые файлы. Нетипизированные файлы и процедуры ввода-вывода. Прямой и последовательный доступ к компонентам файлов. Тема 8. Алгоритмы поиска и сортировки. Простой и бинарный поиск. Сортировки: выбором, обменом, вставкой. Анализ сложности алгоритмов на примере сортировок. Раздел 3. Модульное программирование. Программирование абстрактных типов данных Тема 9. Процедуры и функции. Модули. Подпрограммы. Формальные параметры. Параметры-значения, параметры-переменные, параметры-константы. Локальные и глобальные идентификаторы подпрограмм. Процедуры и функции. Рекурсия. Внешние подпрограммы. Модули. Общая структура модуля. Подпрограммы в модулях. Компиляция и использование модулей. Тема 10. Организация динамических структур данных (абстрактных типов данных): стек, очередь, двоичное дерево поиска. Динамические структуры. Динамическое распределение памяти. Виды списков. Примеры использования списков. Организация динамических структур данных: стек, очередь, двоичное дерево поиска. Раздел 4. Объектно-ориентированное программирование Тема 11. Введение в объектно-ориентированное программирование. Введение в объектноориентированное программирование (ООП) и проектирование. Инкапсуляция, наследование, полиморфизм. Примеры задач. Тема 12. Реализация абстракций данных методами объектно-ориентированного программирования. Математические объекты: рациональные и комплексные числа, вектора, матрицы. Библиотеки объектов. Тема 13. Объектно-событийное и объектно-ориентированное программирование. Идеология программирования под Windows. Событие и сообщение. Виды событий. События от мыши и клавиатуры. Программирование управления событиями. Обработка исключительных событий. Основы визуального программирования. Компонент. Иерархия компонентов. 3.2.Темы практических занятий На практических занятиях программирования. отрабатываются приемы разработки алгоритмов Тема 2. Алгоритмические структуры Разработка линейных алгоритмов. Разработка алгоритмов с ветвлением. Разработка циклических алгоритмов (циклы с пред- и постусловием, цикл с параметром). 11 и Трассировка алгоритма. Разработка алгоритмов с подпрограммами. Тема 6. Основные операторы языка Алгебраические и логические выражения, правила их записи. Присваивание. Совместимость по присваиванию. Ввод и вывод данных в консольном режиме. Условный оператор. Оператор выбора. Операторы цикла (циклы с пред- и постусловием, цикл с параметром). Тема 7. Структурированные типы языка программирования высокого уровня Характеристики структурированных типов данных. Массивы. Линейные и двумерные массивы. Длинная арифметика. Строки. Множества. Записи. Типизированные файлы. Организация файлов записей. Нетипизированные файлы. Текстовые файлы. Прямой доступ к компонентам файлов. Сортировка файлов. Тема 9. Процедуры и функции. Модули Процедуры. Разработка и вызов. Функции. Разработка и вызов. Разработка программ на основе структурного подхода. Внешние подпрограммы. Рекурсивные подпрограммы. Модули. Структура и разработка. Стандартные модули. Тема 10. Организация динамических структур данных (абстрактных типов данных): стек, очередь, двоичное дерево поиска Динамически распределяемая память и ее использование при работе со стандартными типами данных. Однонаправленные списки. Двунаправленные списки. Стеки. Очереди. Деки. Двоичные деревья поиска. Тема 11. Введение в объектно-ориентированное программирование Основные понятия ООП. Разработка программ на основе ООП. Наследование и полиморфизм в ООП. Тема 12. Реализация абстракций данных методами объектно-ориентированного программирования 12 Абстрактные типы и структуры данных. Классы, объекты, поля, методы. Конструкторы и деструкторы. Свойства и методы объектов. Раннее связывание и позднее связывание. Математические объекты: рациональные и комплексные числа, вектора, матрицы. 3.3.Самостоятельная работа Раздел 1. Введение в алгоритмизацию и программирование Тема 1. Методологии программирования. Программирование как раздел информатики. Метафоры (парадигмы) программирования. Методологии программирования. Основные понятия и определения. История и эволюция. Классификация по ядрам методологии: императивное программирование, объектно-ориентированное, функциональное, логическое. Топологическая специфика методологий. Тема 2. Алгоритмические структуры. Этапы решения задач на ЭВМ. Понятие алгоритма. Исполнитель, система команд исполнителя. Свойства алгоритмов. Способы записи алгоритмов. Принципы структурного программирования. Основные алгоритмические структуры и их суперпозиции. Тема 3. Синтаксис и семантика формального языка. Естественные и формальные языки. Понятия о синтаксисе и семантике формального языка. Нормальные формы Бэкуса-Наура и синтаксические диаграммы Вирта. Язык программирования. Классификация языков программирования. Система программирования. Раздел 2. Структурный подход к программированию Тема 4. Основные конструкции алгоритмических языков. Общие конструкции алгоритмических языков: алфавит, величина (тип, имя и значение). Выражение. Тип выражения. Арифметическое выражение. Символьное выражение. Логическое выражение. Стандартные функции. Структура программы. Тема 5. Простые типы языка программирования. Общая характеристика языка Object Pascal. Структуры данных: упорядоченность, однородность, способ доступа. Определение констант. Описание переменных. Стандартные типы данных. Целые типы. Символьный и булевский типы данных. Эквивалентность и совместимость типов. Типы, определяемые программистом: перечисляемый, интервальный. Тип дата-время. Тема 6. Основные операторы языка. Перечень операторов Object Pascal. Оператор присваивания. Операторы (процедуры) ввода-вывода. Управление выводом данных в консольном режиме (простейшее форматирование). Условный оператор. Логические выражения. Оператор множественного ветвления. Операторы цикла: с предусловием, с постусловием, с параметром. Тема 7. Структурированные типы языка программирования высокого уровня. Массивы. Примеры задач с численными, символьными, булевскими массивами. Строковый тип данных. Записи. Оператор присоединения. Записи с вариантами. Множественный тип. Задание множественного типа и множественной переменной. Операции над множествами. Операции отношения. Примеры задач на множественный тип. Файлы. Понятие логического и физического файлов. Файловые типы. Общие процедуры для работы с 13 файлами. Типизированные файлы. Текстовые файлы. Нетипизированные файлы и процедуры ввода-вывода. Прямой и последовательный доступ к компонентам файлов. Тема 8. Алгоритмы поиска и сортировки. Простой и бинарный поиск. Сортировки: выбором, обменом, вставкой. Анализ сложности алгоритмов на примере сортировок. Раздел 3. Модульное программирование. Программирование абстрактных типов данных Тема 9. Процедуры и функции. Модули. Подпрограммы. Формальные параметры. Параметры-значения, параметры-переменные, параметры-константы. Локальные и глобальные идентификаторы подпрограмм. Процедуры и функции. Рекурсия. Внешние подпрограммы. Модули. Общая структура модуля. Подпрограммы в модулях. Компиляция и использование модулей. Тема 10. Организация динамических структур данных (абстрактных типов данных): стек, очередь, двоичное дерево поиска. Динамические структуры. Динамическое распределение памяти. Виды списков. Примеры использования списков. Организация динамических структур данных: стек, очередь, двоичное дерево поиска. Раздел 4. Объектно-ориентированное программирование Тема 11. Введение в объектно-ориентированное программирование. Введение в объектноориентированное программирование (ООП) и проектирование. Инкапсуляция, наследование, полиморфизм. Примеры задач. Тема 12. Реализация абстракций данных методами объектно-ориентированного программирования. Математические объекты: рациональные и комплексные числа, вектора, матрицы. Библиотеки объектов. Тема 13. Объектно-событийное и объектно-ориентированное программирование. Идеология программирования под Windows. Событие и сообщение. Виды событий. События от мыши и клавиатуры. Программирование управления событиями. Обработка исключительных событий. Основы визуального программирования. Компонент. Иерархия компонентов. 3.4.Вопросы к зачету 3 семестр 1. Этапы решения задач с использованием ЭВМ. 2. Понятие алгоритма. Подходы к определению алгоритма. Свойства алгоритма. Способы записи алгоритма. 3. Понятие алгоритма. Понятие исполнителя. Система команд исполнителя. 4. Понятие величины. Типы величин. Присваивание величин. Совместимость по присваиванию. 5. Понятие о структурном программировании. Другие парадигмы программирования: сравнительная характеристика. 6. Языки программирования. Алгоритмические языки (алфавит, синтаксис, семантика). Способы описания синтаксиса (язык металингвистических формул, синтаксические диаграммы). 7. Система программирования Delphi. 8. Структура программы, элементы языка (алфавит). Понятие типа данных. 14 9. Операции (арифметические, логические) на типах. Стандартные функции. Выражения. 10. Процедуры консольного ввода и вывода, управление вводом-выводом. Оператор присваивания. Совместимость по присваиванию. 11. Условный оператор. Оператор множественного ветвления (выбора). 12. Циклы в Delphi: с предусловием, с постусловием. Связь с другими циклами. 13. Циклы в Delphi: с параметром. Связь с другими циклами. 14. Структурированные типы данных. Линейные массивы. Примеры задач. 15. Структурированные типы данных. Двумерные массивы. Примеры задач. 16. Сортировка массивов. Метод выбора. Двоичный поиск в массиве. 17. Сортировка массивов. Метод обмена. 18. Сортировка массивов. Метод вставок. 19. Подпрограммы в Delphi. Основные способы передачи параметров в подпрограмму, их сравнение. 20. Подпрограммы в Delphi. Область видимости. Локальные и глобальные идентификаторы. 21. Процедуры. Организация и вызов. Примеры. 22. Функции. Организация и вызов. Примеры. 23. Простые типы данных в Delphi. 24. Структурированные типы данных. Строковый тип данных в Delphi: основные процедуры и функции, примеры. Вопросы к экзамену 5 семестр Рекурсия. Механизм рекурсии. Примеры. Сортировка массивов. Метод быстрой сортировки. Множества в PascalABC. Примеры. Комбинированный тип данных (записи). Оператор присоединения. Записи с вариантами. Программирование типовых алгоритмов обработки записей. 5. Файловые типы в PascalABC. Общие процедуры для работы с файлами. Компонентные (типизированные) файлы. 6. Текстовые файлы. Текст-ориентированные процедуры и функции. Типовые задачи. 7. Прямой и последовательный доступ к компонентам файла. Процедуры и функции, ориентированные на прямой доступ к компонентам файла. 8. Поиск в типизированных файлах. Сортировка файлов (на примере одного из методов). 9. Типизированные файлы. Файлы записей. Типовые алгоритмы обработки. 10. Статическая и динамически распределяемая память. Пример использования указателей. 11. Динамические структуры данных. Однонаправленный список. Процедуры обработки списка. 12. Динамические структуры данных. Двунаправленный список. Процедуры обработки списка. 13. Динамические структуры данных. Кольцевой список (однонаправленный или двунаправленный). Процедуры обработки списка. 14. Стек. Процедуры обработки. 15. Очередь. Процедуры обработки. 16. Двоичное дерево. Добавление в дерево и поиск в дереве. 17. Двоичное дерево. Удаление элемента из дерева. 18. Модуль. Общая структура модуля. Компиляция и подключение модуля. 19. Объектно-ориентированное программирование. 20. Пример реализации задачи на ООП в PascalABC. 1. 2. 3. 4. 15 3.5 Критерий оценки знаний Проводятся три аттестации студентов на 8-й, 16-й неделях и в сессионный период. Максимальное количество баллов 1-я аттестация - 30 баллов 2-я аттестация - 30 баллов 3-я аттестация премиальные баллы – 10 баллов (экзамен/зачет) - 30 баллов _______________________ Итого - 100 баллов 3.6.Учебно-методическое обеспечение дисциплины Литература Основная 1.Могилёв А.В., Пак Н.И., Хеннер Е.К. Информатика: Учеб. пособие для студ. пед. ву-зов / Под ред. Е.К. Хеннера. — М., Academia, 2004. 2. Сборник задач по программированию. / Авт.-сост. А.П. Шестаков; Перм. ун-т. — Пермь, 2001. (Ч. I — 76 с.; Ч. II (Олимпиадные задачи) — 112 с.) 3. Семакин И.Г., Шестаков А.П. Основы программирования: Учебник. — М.: Мастерство, НМЦ СПО; Высшая школа, 2004. — 432 с. Дополнительная 1.Вирт Н. Алгоритмы и структуры данных. — М.: Мир, 1989. 4. Вирт Н. Алгоритмы + структура данных = программы. — М.: Мир, 1985. 5. Информатика. Задачник-практикум в 2 т. / Под ред. И. Семакина, Е. Хеннера. М.: Лаборатория Базовых Знаний, 1999. 6. Семакин И.Г., Шестаков А.П. Лекции по программированию. — Пермь, изд-во ПГУ, 1998. 7. Семакин И.Г., Шестаков А.П. Основы алгоритмизации и программирования: Учебник для сред. проф. образования / И.Г. Семакин, А.П. Шестаков. — М.: Издательский центр "Академия", 2008. — 400 с. (Допущено Министерством образования и науки Российской Федерации) . Материально-техническое и информационное обеспечение дисциплины При освоении дисциплины для выполнения лабораторных работ необходимы персональные компьютеры с набором программного обеспечения: системы программирования (Turbo Pascal, Delphi, Free Pascal). УМК по дисциплине в электронном виде находится в библиотеке и методическом кабинете кафедры информатики и ВТ ПГПУ, на данном сайте и может быть использован для самостоятельной работы. Дополнительный набор ссылок на Интернет-ресурсы: http://citforum.ru http://delphi.org.ru http://durus.ru http://www.rushelp.com 16 http://www.delphimaster.ru http://www.codenet.ru/cat/Languages/Delphi http://rudelphi.info/ http://www.delphikingdom.com http://www.compdoc.ru http://www.emanual.ru http://www.delphisources.ru/ http://www.delphi.int.ru http://ishodniki.ru http://delcb.com Учебно-методический блок 4.1. Блок лекций Лекция №1. Введение в алгоритмизацию и программирование Основным в процессе программирования является разработка алгоритма. Это один из наиболее сложных этапов решения задачи с использованием ЭВМ. В начале обучения программированию, на наш взгляд, целесообразно не привязываться сразу к какому-либо языку, разрабатывать алгоритмы без записи на языках программирования высокого уровня (ЯПВУ), а, например, с помощью блок-схем или иным аналогичным способом. После такой "чистой" алгоритмизации учащимся или студентам проще перейти к записи того же алгоритма на определённом языке программирования. В настоящей публикации продемонстрирован именно такой подход. Напомним, что основными алгоритмическими структурами (ОАС) являются следование, развилка и цикл. В более сложных случаях используются суперпозиции (вложения) ОАС. Ниже приведены графические обозначения (обозначения на блок-схемах) ОАС. Структура “следование” Полная развилка Неполная развилка 17 Цикл с предусловием (цикл ПОКА) Цикл с постусловием (цикл ДО)Цикл с параметром На схемах СЕРИЯ обозначает один или несколько любых операторов; УСЛОВИЕ есть логическое выражение (ЛВ) (если его значение ИСТИНА, переход происходит по ветви ДА, иначе — по НЕТ). На схеме цикла с параметром использованы обозначения: ПЦ — параметр цикла, НЗ — начальное значение параметра цикла, КЗ — конечное значение параметра цикла, Ш — шаг изменения параметра цикла. Начало и конец алгоритма на блок-схемах обозначают овалом, вводимые и выводимые переменные записываются в параллелограмме. В примерах мы будем использовать запись алгоритмов с помощью блок-схем и словесное описание. Линейные алгоритмы Простейшие задачи имеют линейный алгоритм решения. Это означает, что он не содержит проверок условий и повторений. Пример 1. Пешеход шел по пересеченной местности. Его скорость движения по равнине v1 км/ч, в гору — v2 км/ч и под гору — v3 км/ч. Время движения соответственно t1, t2 и t3 ч. Какой путь прошел пешеход? 1. Ввести v1, v2, v3, t1, t2, t3. 2. S1 := v1 * t1. 3. S2 := v2 * t2. 4. S3 := v3 * t3. 5. S := S1 + S2 + S3. 6. Вывести значение S. 7. Конец. Для проверки работоспособности алгоритма необходимо задать значения входных 18 переменных, вычислить конечный результат по алгоритму и сравнить с результатом ручного счета. Пример 2. Дано натуральное трехзначное число n, в записи которого нет нулей. Составить алгоритм, который возвращает значение ИСТИНА, если верно утверждение: "число n кратно каждой своей цифре", и ЛОЖЬ — в противном случае. 1. Ввести число n 2. A := n mod 10 {разряд единиц} 3. B := n div 100 {разряд сотен} 4. C := n div 10 mod 10 {десятки} 5. L := (n mod A=0) and (n mod B=0) and (n mod C=0) 6. Вывод L 7. Конец На приведенной выше схеме DIV и MOD соответственно операции деления нацело и получения остатка от целочисленного деления. В фигурных скобках записаны пояснения (комментарии) к операторам. Развилка Достаточно часто то или иное действие должно быть выполнено в зависимости от значения логического выражения, выступающего в качестве условия. В таких случаях используется развилка. Пример 1. Вычислить значение функции 19 1. Ввести x. 2. Если x£–12, то y:=–x2 3. Если x<0, то y:=x4 4. y := x–2 5. Вывести y 6. Конец При тестировании алгоритмов с развилкой необходимо подбирать такие исходные данные, чтобы можно было проверить все ветви. В приведенном выше примере должно быть по крайней мере три тестовых набора. Пример 2. Дано натуральное число n. Если число нечётное и его удвоение не приведет к выходу за 32767 (двухбайтовое целое число со знаком), удвоить его, иначе — оставить без изменения. Чтобы удовлетворить условию удвоения, число n должно быть нечетным и меньше 16384. 1. Ввести число n 2. Если число n нечетное и меньше 16384, то n := n * 2 3. Вывод n 4. Конец Рассмотренный пример иллюстрирует неполную развилку. Также следует отметить, здесь логическое выражение, являющееся условием, содержит 2 операнда. Циклы Если какие-либо операторы необходимо выполнить несколько раз, то их не переписывают каждый раз заново, а организуют цикл. Пример 1. Подсчитать количество нечетных цифр в записи натурального числа n. 20 Идея решения. Из заданного числа выбирать из младшего разряда цифру за цифрой до тех пор, пока оно не исчерпается, т.е. станет равным нулю. Каждую нечётную цифру учитывать. 1. Ввести число n 2. K := 0 {подготавливаем счётчик} 3. Если n = 0, переход к п. 7 4. Если n mod 10 mod 2 = 1, то K := K +1 5. n := n div 10 6. Переход к п. 3 7. Вывод K 8. Конец Задача решена двумя способами. Слева решение оформлено с использованием цикла с предусловием, справа — с постусловием. Пример 2. Дана последовательность, общий член которой определяется формулой Вычислить при n>2 сумму тех ее членов, которые больше заданного числа e. При решении задачи находится очередной член последовательно и, если он больше e, добавляется к сумме. 21 1. Ввести e 2. S := 0 3. A := 1/4 4. n := 3 5. Сравнить А с e. Если A>=e, переход к п. 10 6. S := S + A 7. A := (n-1)/(n*n) 8. n := n + 1 9. Переход к п. 5 10. Вывод S 11. Конец В рассмотренных выше примерах количество повторений заранее неизвестно. В первом оно зависит от количества цифр в записи натурального числа, во втором — от числа e. В тех же случая, когда количество шагов известно из условия задачи, проще и предпочтительней использовать цикл с параметром. Пример 3. Найти произведение первых k натуральных чисел, кратных трём. При составлении алгоритма учтем, что первое натуральное число, кратное 3, есть тройка, а все последующие больше предыдущего на 3. 22 1. Ввод k 2. P := 1 {здесь накапливаем произведение} 3. T := 0 {здесь будут числа, кратные 3} 4. I := 1 5. Если I > k, переход к п. 10 6. T := T + 3 7. P := P * T 8. I := I + 1 9. Перейти к п. 5 10. Вывод P 11. Конец Другие примеры будут записаны уже на ЯПВУ. В настоящей же публикации предпринята попытка продемонстрировать, что изучение программирования разумно начинать собственно с разработки алгоритмов, не акцентируя первоначально внимания на записи алгоритма на том или ином языке программирования. В то же время автор, являясь сторонником структурного подхода к программированию, предлагает придерживаться этого подхода и при программировании на уровне блок-схем. Лекция №2 . Структурный подход к программированию Для программной обработки с помощью компьютера данные представляются в виде величин и их совокупностей. Величина — это элемент данных с точки зрения их семантического (смыслового) содержания или обработки. Смысловое (семантическое) разбиение данных производится во время постановки задачи и разработки алгоритма ее решения (входные, выходные и промежуточные). Исходные (входные, аргументы) — это данные, известные перед выполнением задачи, из ее условия. Выходные данные (результаты) — результат решения задачи. Переменные, которые не являются ни аргументом, ни результатом алгоритма, а используются только для обозначения вычисляемого промежуточного значения, называются промежуточными. Необходимо указывать имена и типы данных — целый, вещественный, логический и символьный. Имена в языках программирования принято называть идентификаторами. Есть идентификаторы переменных, констант, типов, функций и т.д. С понятием величины связаны следующие характеристики (атрибуты): идентификатор — это ее обозначение и место в памяти; тип — множество допустимых значений и множество применимых операций к ней; значение — динамическая характеристика, может меняться многократно в ходе исполнения алгоритма. Во время выполнения программы в каждый конкретный момент величина имеет какое-то 23 значение или не определена. Постоянной называется величина, значение которой не изменяется (поскольку такое изменение запрещено) в процессе исполнения алгоритма, а остается одним и тем же, указанным в тексте алгоритма. Переменной называется величина, значение которой меняется (в общем случае, может изменяться) в процессе исполнения алгоритма. Тип выражения определяется типами входящих в него величин, а также выполняемыми операциями. В языке Pascal тип величины задают заранее, т.к. все переменные, используемые в программе, должны быть объявлены в разделе описания с указанием их типа. Различают переменные следующих простых типов: целые (Integer, Byte, ShortInt, Word, LongInt), вещественные (Real, Double, Single, Extended), логический (Boolean), символьный (Char), перечисляемый, диапазонный (интервальный). В целом, иерархия типов в языке Pascal следующая: Объявления служат для компилятора источником информации о свойствах величин, используемых в программе, и установления связи между этими величинами и их идентификаторами, фиксируя тем самым конкретный смысл, предписанный различным идентификаторам в программе. Согласно объявленным переменным и их количеству компилятор резервирует необходимый объем памяти для хранения значений величин, над которыми выполняются требуемые операции. Описание переменной: идентификатор переменной: тип; Пример описания: Var D, C, N : Integer; LogPer : Boolean; A, B : Real; 24 K : Char; Тип переменной определяет диапазон допустимых значений, принимаемых величинами этого типа; набор операций, допустимых над данной величиной; объем памяти, отводимой под эту переменную; способ представления величин в памяти компьютера. Каждый тип имеет свой идентификатор. Идентификатор. Диапазон (множество) значений Операции Количество байт Примечание. Приведенное здесь описание простых типов актуально для Turbo Pascal; в других реализациях Pascal объёмы памяти и значения числовых величин могут лежать в других диапазонах. О представлении информации в памяти компьютера можно прочесть в данной статье. Обмен информацией с компьютером предполагает использование определенных средств ввода-вывода. В ЭВМ основным средством ввода является клавиатура, вывода — дисплей. Процедура, которая в режиме диалога с клавиатуры присваивает значение для переменной величины, называется процедурой ввода. В языке Pascal она выглядит следующим образом: Read(список переменных); Например, Var A : Real; B : Integer; C : Char; Begin Read(A, B, C) End. Читается: “Ввести вещественную А, целую В и символьную С”. Как только в программе встречается вызов процедуры Read, ЭВМ приостанавливает выполнение этой программы и ждет, пока пользователь введет с клавиатуры соответствующие значения, которые будут присваиваться переменным, перечисленным в списке ввода, в порядке перечисления. Значения вводимых данных одновременно отображаются на экране дисплея. После нажатия клавиши enter, когда все переменные примут свои значения из входного набора данных, определенного пользователем, выполнение программы продолжается с оператора, следующего за Read. В списке ввода значения разделяются между собой пробелом (либо каждое вводится в отдельной строке). Присваивание значений из входного потока выполняется слева направо в соответствии с порядком следования переменных в процедуре Read. Процедура ReadLn похожа на Read. Разница лишь в том, что ReadLn реагирует на конец строки, и в случае его обнаружения происходит сразу переход к следующей строке. Примеры ввода данных с помощью процедуры ReadLn: ReadLn(A, B, C); ReadLn(X); ReadLn(LogPer); Процедура, которая позволяет выводить значения переменных (выражений) на экран, называется процедурой вывода. 25 В Pascal эта команда выглядит следующим образом Write (список выражений) Например Write ('Выходное значение: ', C); В списке вывода может быть либо одно выражение, либо последовательность выражений, разделенных между собой запятыми. Процедура Write осуществляет вывод значений выражений, приведенных в его списке, на текущую строку до ее заполнения. С помощью процедуры WriteLn реализуется вывод значений выражений, приведенных в его списке, на одну строку дисплея и переход к началу следующей строки. Примеры вывода данных: Write(A, B, C); WriteLn('Корнем уравнения является ', X); WriteLn(LogPer); Для управления размещением выводимых значений процедуры Write и WriteLn используются с форматами. Под форматом данных понимается расположение и порядок кодирования отдельных полей элементов данных. Процедура вывода с форматом для целого типа имеет вид: WriteLn(A : N, B : M, C : L); Здесь N, M, L — выражения целого типа, задающие ширину поля вывода значений. При выводе вещественных значений Write(R) без указания формата выводит вещественное R в поле шириной 18 символов в форме с плавающей запятой в нормализованном виде. Для десятичного представления значения R применяется оператор с форматами вида WriteLn(R : N : M). В десятичной записи числа R выводится M (0 ≤ M ≤ 24) знаков после запятой, всего выводится N знаков. Примеры: WriteLn(N : 4); WriteLn(K : 10 : 5, S : 7 : 3); Общая структура программы на Pascal такова: Program имя программы; {заголовок} {раздел описаний} Const Константа1 = значение; {объявление констант} Константа2 = значение; ... КонстантаN = значение; Type ...; {объявление типов} Var СписокПеременных1 : Тип; {описание переменных} СписокПеременных2 : Тип; ... СписокПеременныхN : Тип; Label СписокМеток; 26 Function ... Procedure ... Begin {раздел операторов} End. Оператор присваивания — один из самых простых и наиболее часто используемых операторов в любом языке программирования, в том числе и в Pascal. Он предназначен для вычисления нового значения некоторой переменной, а также для определения значения, возвращаемого функцией. В общем виде оператор присваивания можно записать так: переменная := выражение; Оператор выполняется следующим образом. Вычисляется значение выражения в правой части присваивания. После этого переменная, указанная в левой части, получает вычисленное значение. При этом тип выражения должен быть совместим по присваиванию с типом переменной! Как отмечалось ранее, тип выражения определяется типом операндов, входящих в него, и зависит от операций, выполняемых над ними. Примеры присваивания: X := (Y + Z) / (2 + Z * 10) - 1/3; LogPer := (A > B) And (C <= D); Для операций сложения, вычитания и умножения тип результата в зависимости от типа операнда будет таким: Операнд 1 Операнд 2 Результат Integer Integer Integer Integer Real Real Real Integer Real Real Real Real Для операции деления тип результата в зависимости от типа операнда будет таким: Операнд 1 Операнд 2 Результат Integer Integer Real Integer Real Real Real Integer Real Real Real Real В Pascal есть операции целочисленного деления и нахождения остатка от деления. При выполнении целочисленного деления (операция div) остаток от деления отбрасывается. Например, 15 div 3 = 5; 18 div 5 = 3; 123 div 10 = 12, 7 div 10 = 0. С помощью операции mod можно найти остаток от деления одного целого числа на другое. Например, 15 mod 3 = 0; 18 mod 5 = 3; 123 mod 10 = 3, 7 mod 10 = 7. При записи алгебраических выражений используют арифметические операции (сложение, умножение, вычитание, деление), функции Pascal, круглые скобки. Порядок действий при вычислении значения выражения: 1. вычисляются значения в скобках; 27 2. вычисляются значения функций; 3. выполняется унарные операции (унарный минус — смена знака); 4. выполняются операции умножения и деления (в том числе целочисленного деления и нахождения остатка от деления); 5. выполняются операции сложения и вычитания. Встроенные математические функции языка Pascal: Математическая запись Запись на Pascal cos x cos(x) sin x sin(x) ex exp(x) [x] trunc(x) |x| abs(x) x2 sqr(x) sqrt(x) {x} frac(x) arctg x arctan(x) ln x ln(x) p Pi Назначение Косинус x радиан Синус x радиан Значение e в степени x Целая часть числа x Модуль числа x Квадрат числа x Квадратный корень из x Дробная часть x Арктангенс числа x Натуральный логарифм x Число p Возведение в степень (кроме возведения в квадрат и возведения в степень числа e) отсутствует. Для возведения в произвольную степень можно воспользоваться очевидным равенством: xy=ey ln x. Для возведения числа в натуральную степень можно написать собственную функцию. Например, {Функция возведения вещественного числа X в натуральную степень N} Function Stepen(X : Real; N : Integer) : Real; Var I : Integer; St : Real; Begin St := 1; For I := 1 To N Do St := St * X; Stepen := St; End; Другой способ получить натуральное значение z=xy, где x, y — натуральные, это сделать так: Z := Round(Exp(Y * Ln(X))). Примечание. Интересной является задача получения степени любого целого числа (за исключением нуля), если основание степени — неотрицательное целое, без использования развилки. Одно из возможных решений : (–1)*Ord(Odd(Y)) * Exp(Y * Ln(X)) + Ord(Odd(Y+1)) * Exp(Y * Ln(X)). Здесь Ord(K) — функция, возвращающая порядковый номер величины K в том или ином порядковом типе (в примере использовано свойство, что порядковый номер False равен 0, а порядковый номер True — 1). Примеры записи математических выражений: Математическая запись Запись на Pascal 1. x2 – 7x + 6 Sqr(x) - 7 * x + 6 2. (Abs(x) - Abs(y)) / (1 + Abs(x * y)) 28 3. Ln(Abs((y - Sqrt(Abs(x))) * (x - y / (z + Sqr(x) / 4)))) Логический операнд — это конструкция соответствующего языка программирования, которая задает правило для вычисления одного из двух возможных значений: True или False. Чаще всего логические выражения используют в операторах присваивания или для записи того или иного условия. Составными частями логических выражений могут быть: логические значения (True, False); логические переменные; отношения. Например, 1) Y:=True; 2) Z:=False; 3) LogPer:=A > B; 4) Log1:=(A = B) And (C <= D). Как видно из примеров, отношение — это два выражения, разделенных между собой знаком операции отношения (>, <, =, <>, <=, >=). Отношение является простейшей конструкцией логического выражения. Оно вычисляет результат True, если выполняется заданное соотношение, и False — в противном случае. Примечание. Несмотря на то, что операции отношения =, <>, >=, <= определены для вещественных типов, реально они в большинстве случаев корректно не работают в силу того, что множество вещественных величин, представимых в памяти ЭВМ, дискретно. Поэтому их следует, если это возможно, избегать. В том случае, когда всё-таки для вещественных возникает необходимость вычисления указанных отношений, разумно проверять вещественные величины не на равенство, а на близость расположения друг к другу, т.е. заменять отношения вида A=B отношениями вида |A-B|<E, где E — достаточно малое по абсолютной величине число (в общем случае — так называемое машинное епсилон). В языке Pascal операции отношения определены для величин любого порядкового типа (целые, символьный, логический, перечислимый, диапазон). Операции отношения могут быть выполнены также над строковыми выражениями. Сравнение двух строк выполняется посимвольно слева направо в соответствии с их лексикографической упорядоченностью в таблице кодов ASCII. Эта упорядоченность предполагает, что "1"<"2", "a"<"b", "B"<"C" и т.д. Как только в процессе попарных сравнений символов с одинаковой порядковой позицией обнаруживается больший по коду ASCII символ, данный процесс прекращается, и считается, что строка с этим символом соответственно больше другой строки. Если строки имеют разную длину и их символы совпадают до последнего знака, то считается, что более короткая строка меньше. Логическое выражение — это логический операнд или последовательность логических операндов, разделенных между собой знаками логических операций (NOT, AND, OR, XOR). Порядок действий при вычислении значения логического выражения: 1. вычисляются значения в скобках; 2. вычисляются значения функций; 3. выполняется унарные операции (операция NOT); 4. выполняется операция AND; 5. выполняются операции OR, XOR; 6. выполняются операции отношения. Действия выполняются слева направо с учетом их приоритета. Желаемая последовательность операций обеспечивается путем расстановки скобок в 29 соответствующих местах выражения. При реализации некоторых программ удобно использовать функции, которые имеют логическое значение. Обычно они используются для того, чтобы на некоторый вопрос получить ответ “ДА” или “НЕТ”. Например, следующая функция возвращает True, если её аргумент — простое число, и False — в противном случае: Function Simple (Pr : Integer) : Boolean; Var I : Integer; LogPer : Boolean; Begin I := 2; {счетчик} Repeat LogPer := (Pr Mod I = 0); {логическая переменная, принимающая значение TRUE,если число Pr составное} I := I + 1 Until (I > Pr Div 2 + 1) Or (LogPer); {цикл завершаем в том случае, когда счетчик становится больше половины данного числа или обнаруживаем, что число составное} Simple := Not LogPer {значение функции равно TRUE, если число простое, и FALSE — в противном случае} End; Рассмотрим примеры задач, где алгоритм решения является линейным. Задача 1. Скорость первого автомобиля v1 км/ч, второго — v2 км/ч, расстояние между ними s км. Какое расстояние будет между ними через t ч, если автомобили движутся в разные стороны? Согласно условию задачи искомое расстояние s1=s+(v1+v2)t (если автомобили изначально двигались в противоположные стороны) или s2=|(v1+v2)t-s| (если автомобили первоначально двигались навстречу друг другу). Чтобы получить это решение, необходимо ввести исходные данные, присвоить переменным искомое значение и вывести его на печать. Program Car; Var V1, V2, T, S, S1, S2 : Real; Begin Write('Введите скорости автомобилей, расстояние между ними и время движения:'); ReadLn(V1, V2, S, T); S1 := S + (V1 + V2) * T; S2 := Abs((V1 + V2) * T - S); WriteLn('Расстояние будет равно ', S1:7:4, ' км или ', S2:7:4, ' км') End. Заметим, что идентификатор должен начинаться с латинской буквы, далее, кроме латинских букв, может содержать цифры, знак подчеркивания (_). Разумно, чтобы программа вела диалог с пользователем, т.е. необходимо предусмотреть в ней вывод некоторых пояснительных сообщений. В противном случае даже сам программист может через некоторое время забыть, что необходимо вводить и что является результатом. 30 Для всех величин в программе объявлен тип Real, что связано со стремлением сделать программу более универсальной и работающей с как можно большими наборами данных. Задача 2. Записать логическое выражение, принимающее значение TRUE, если точка лежит внутри заштрихованной области, иначе — FALSE. Прежде всего обратим внимание на то, что эту сложную фигуру целесообразно разбить на несколько более простых: треугольник, лежащий в I и IV координатных четвертях, и треугольник, лежащий во II и III четвертях. Таким образом, точка может попасть внутрь одной из этих фигур, либо на линию, их ограничивающую. Количество отношений, описывающих какую-либо область, обычно совпадает с количеством линий, эту область ограничивающих. Чтобы точка попала внутрь области, необходима истинность каждого из отношений, поэтому над ними выполняется операция AND. Так вся область была разбита на несколько, то между отношениями, описывающими каждую из них, используется операция OR. Учитывая приведенные здесь соображения и записав уравнения всех ограничивающих фигуру линий, получаем искомое логическое выражение: (X >= 0) And (Y >= 1.5 * X – 1) And (Y <= X) OR (X <= 0) And (Y >= –1.5 * X – 1) And (Y <= –X) Задача 3. Вычислить значение выражения Для решения задачи достаточно ввести все данные, безошибочно записать выражение и вывести результат. Примечание. При решении этой задачи не учитывается область определения выражения, считается, что вводятся только допустимые данные. Program Expression; Var X, Z : Real; Begin Write('Введите значения переменной X: '); ReadLn(X); Z := 6 * ln(sqrt(exp(x+1)+2*exp(x)*cos(x))) / ln(x - exp(x+3) * sin(x)) + abs(cos(x) / exp(sin(x))); WriteLn('Значение выражения: ', Z : 12 : 6) End. Контрольные вопросы и задания 1. Что такое величина? 2. Какие величины называют аргументами? результатами? промежуточными величинами? Приведите примеры. 3. Каковы атрибуты величины? 4. Какие величины называют постоянными? переменными? Приведите примеры. 5. Какие простые типы величин существуют в языке Pascal? 6. Что определяет тип величины? 7. Расскажите о простых типах данных и их атрибутах. 8. Как осуществляется ввод данных в языке Pascal? Приведите примеры. 9. Как осуществляется вывод данных в языке Pascal? Приведите примеры. 10. Какова общая структура программы в языке Pascal? 11. Расскажите об операторе присваивания и совместимости типов. 12. Что такое формат вывода? 13. Расскажите о правилах вычисления алгебраического выражения. Приведите примеры. 14. Расскажите о правилах вычисления логического выражения. Приведите примеры. 15. Расскажите о логических операциях. Приведите примеры. 31 16. Приведите примеры задач, имеющих линейный алгоритм решения. 17. Определите, какой суммарный объём памяти требуется под переменные в каждом из примеров 1–3. 18. Каково назначение следующей программы? Program Example; Var N : 100..999; Begin Write('Введите натуральное трёхзначное число: '); ReadLn(N); WriteLn('Искомая величина: ', N Div 100 + N Div 10 Mod 10 + N Mod 10); End. 19. Задайте на координатной плоскости некоторую область, которую можно описать математическими уравнениями и заштрихуйте её. Запишите логическое выражение, принимающее значение TRUE, если точка (x, y) лежит внутри заштрихованной области, иначе — FALSE. 20. Выпишите несколько алгебраических выражений и запишите их на языке Pascal. 21. Запишите алгебраические выражения, соответствующие следующим записям на языке Pascal: 22. а) (a + b) / c; б) a + b / c; в) a / b / c; г) a / (b * c); 23. д) (a + b) / (d + c); е) a + b / (d + c); 24. ё) a + b / d + c; ж) (a + b) / d + c Лекция 3. Модульное программирование. Программирование абстрактных типов данных На практике решение большинства задач не удается описать с помощью программ линейной структуры. При этом после проверки некоторого условия выполняется та или иная последовательность операторов, однако происходит нарушение естественного порядка выполнения операторов. Для этих целей используют управляющие операторы. Условный оператор используется для реализации разветвлений в программе, которые происходят при выполнении некоторого условия и имеет следующую структуру IF <логическое выражение> THEN серия1 ELSE серия2; Если логическое выражение, выступающее в качестве условия, принимает значение False, то выполняются операторы, расположенные после else (серия2), если True, — операторы, следующие за then. При записи логического выражения следует избегать знака = (равно) для действительных переменных, так как они представляются неточно, а поэтому может не произойти совпадений значений выражений, стоящих слева и справа от знака равно. Для устранения указанного недостатка следует требовать выполнения условия с заданной точностью, т.е. вместо отношения X = Y рекомендуется, например, Abs(X - Y) < 1E-8. Поскольку развилка может быть неполной, то возможна и неполная форма записи условного оператора: IF <логическое выражение> THEN серия; Условный оператор реализует разветвление вычислительного процесса по двум направлениям, одно из которых осуществляется при выполнении условия, другое — в противном случае. Для реализации разветвлений более чем по двум направлениям необходимо использовать несколько условных операторов. Рассмотрим примеры. Задача 1. Даны действительные числа x, y. Если x и y отрицательны, то каждое значение заменить модулем; если отрицательно только одно из них, то оба значения увеличить на 0,5; если оба значения неотрицательны и ни одно из них не принадлежит отрезку [0,5; 2,0], 32 то оба значения уменьшить в 10 раз; в остальных случаях x и y оставить без изменения. Разработаем алгоритм решения задачи, после чего напишем программу. Алгоритм запишем словесно: 1) ввести значения x, y; 2) если x<0 и y<0, найти их модули и перейти к п. 5, иначе перейти к следующему пункту; 3) если x<0 или y<0, увеличить каждую величину на 0,5 и перейти к п. 5, иначе перейти к следующему пункту; 4) если ни x, ни y не принадлежат отрезку [0,5; 2,0], уменьшить их в 10 раз; 5) вывести значения x и y; 6) конец. Program Usl; Var X, Y : Real; Begin Write('Введите два действительных числа '); ReadLn(X, Y); If (X < 0) AND (Y < 0) THEN Begin X = ABS(X); Y = ABS(Y) End ELSE IF (X < 0) OR (Y < 0) THEN Begin X = X + 0.5; Y = Y + 0.5 End ELSE IF NOT (((X >= 0.5) AND (X <= 2)) OR ((Y >= 0.5) AND (Y <= 2))) THEN Begin X = X / 10; Y = Y / 10 End; WriteLn('Результат:'); WriteLn('X= ', X:10:6); WriteLn('Y= ', Y:10:6) END. Задача 2. Дано действительное число a. Вычислить f(a), если Program Usl1; Var A, F : Real; Begin WriteLn('Введите действительное число: '); ReadLn(A); IF A <= 0 THEN F=0 ELSE IF A <= 1 THEN F = Sqr(A) - A ELSE 33 F = Sqr(A) - SIN(Pi * Sqr(A)); WriteLn('Значение функции F(x) при x = ', A:10:6, ' равно ', F:10:6); END. Кроме условного оператора в качестве управляющей структуры довольно часто используется оператор выбора CASE. Эта структура позволяет переходить на одну из ветвей в зависимости от значения заданного выражения (селектора выбора). Ее особенность состоит в том, что выбор решения здесь осуществляется не в зависимости от истинности или ложности условия, а является вычислимым. Оператор выбора позволяет заменить несколько операторов развилки (в силу этого его ещё называют оператором множественного ветвления). В конструкции CASE вычисляется выражение K и выбирается ветвь, значение метки которой совпадает со значением K. После выполнения выбранной ветви происходит выход из конструкции CASE. Если в последовательности нет метки со значением, равным K, то управление передается внешнему оператору, следующему за конструкцией CASE (в случае отсутствия альтернативы ELSE; если она есть, то выполняется следующий за ней оператор, а уже затем управление передается внешнему оператору). Запись оператора выбора CASE K OF A1 : серия 1; A2 : серия 2; ... AN : серия N ELSE серия N + 1 END; Любая из указанных серий операторов может состоять как из единственного оператора, так и нескольких (в этом случае, как обычно, операторы, относящиеся к одной метке, должны быть заключены в операторные скобки begin..end). Выражение K здесь может быть любого порядкового типа (напомним, что к таким типам относятся все целые типы, Boolean, Char, перечисляемый тип, диапазонный тип, базирующийся на любом из указанных выше типов). Задача 1. В старояпонском календаре был принят двенадцатилетний цикл. Годы внутри цикла носили названия животных: крысы, коровы, тигра, зайца, дракона, змеи, лошади, овцы, обезьяны, петуха, собаки и свиньи. Написать программу, которая позволяет ввести номер года и печатает его название по старояпонскому календарю. Справка: 1996 г. — год крысы — начало очередного цикла. Поскольку цикл является двенадцатилетним, поставим название года в соответствие остатку от деления номера этого года на 12. Program Goroskop; Var Year : Integer; Begin Write('Введите год '); ReadLn(Year); CASE Year MOD 12 OF 0 : WriteLn('Год Обезьяны'); 1 : WriteLn('Год Петуха'); 2 : WriteLn('Год Собаки'); 34 3 : WriteLn('Год Свиньи'); 4 : WriteLn('Год Крысы'); 5 : WriteLn('Год Коровы'); 6 : WriteLn('Год Тигра'); 7 : WriteLn('Год Зайца'); 8 : WriteLn('Год Дракона'); 9 : WriteLn('Год Змеи'); 10 : WriteLn('Год Лошади'); 11 : WriteLn('Год Овцы') END; END. Задача 2. Найти наибольшее из двух действительных чисел, используя оператор выбора. Program Maximum; Var Max, X, Y : Real; Begin Write('Введите два неравных числа:'); ReadLn(X, Y); Case X > Y Of TRUE : Max := X; FALSE : Max := Y End; WriteLn('Максимальное из двух есть ', Max : 12 : 6) End. Задача 3. Преобразовать символ, если он является строчной русской буквой, в заглавную букву. Так как в альтернативной системе кодировки ASCII строчные русские буквы идут не подряд, а с некоторым разрывом, то в данном случае, в зависимости от того, в какую часть таблицы попадает введенная буква, используется та или иная формула. Если введённый символ не является строчной русской буквой, он выводится без изменения. Program UpCase; Var C : Char; Begin Write('Введите символ:'); ReadLn(C); Case C Of 'а'..'п' : C := Chr(Ord(C) - 32); 'р'..'я' : C := Chr(Ord(C) - 80) End; WriteLn(C); End. Как видно из примера, в качестве метки может выступать не только отдельное значение, но и диапазон значений. Кроме того, в качестве метки может выступать перечень значений выражения (значения перечисляются через запятую). 35 на ', S : 7 : 4) End. Достаточно часто цикл с параметром используется при разработке программ обработки массивов. Примечание. Как видно из рассказа, приведённого выше, область применения цикла с параметром в языке Pascal значительно ограничена: ограничения связаны с шагом изменения параметра цикла, с типом параметра цикла, его начального и конечного значения. В некоторых языках, например, в Basic, таких ограничений не существует. По сравнению с циклом с параметром итерационные циклы являются универсальными. Для организации итерационных циклов используются операторы цикла с предусловием while и цикла с постусловием repeat..until. Эти операторы не задают закон изменения параметра цикла, поэтому необходимо перед циклом задавать начальное значение параметра с помощью оператора присваивания, а внутри цикла изменять текущее значение этого параметра. Соответствующие структуры циклов: while B Do Begin <операторы> End; Repeat <операторы> Until C; Здесь B, C — логические выражения. Для оператора цикла с предусловием проверяется значение логического выражения, если оно имеет значение True, то операторы, входящие в цикл, выполняются, в противном случае осуществляется выполнение оператора, следующего за циклом. Цикл с постусловием выполняется хотя бы один раз. Затем проверяется значение логического выражения, если оно False, то операторы, входящие в цикл, выполняются, в противном случае осуществляется выход из цикла. Входить в цикл можно только через его начало, т.е. нельзя входить внутрь цикла с помощью управляющего оператора, т.к. в этом случае параметр цикла не определен. Задача 2. Найти наименьший номер члена последовательности, для которого выполняется условие |an-an-1|<e, где an=arctg an-1+1, a1=0. Вывести на экран этот номер и все элементы ai (i = 1, 2, ..., n). Поскольку по ходу решения задачи необходимо знать an и an-1, будем запоминать их соответственно в переменных ANew и AOld. Program Posled; Var Eps, AOld, ANew : Real; N : Integer; Begin Write('Введите число Epsilon '); ReadLn(Eps); AOld := 0; ANew := ArcTan(AOld) + 1; N := 2; WriteLn(AOld : 8 :5); WriteLn(ANew : 8 :5); While Abs(ANew - AOld) >= Eps Do Begin AOld := ANew; ANew := ArcTan(AOld) + 1; WriteLn(ANew : 8 :5); 36 N := N + 1 End; WriteLn('Искомый номер ', N) End. Внутрь одного цикла может входить один или несколько других. При этом охватывающий цикл называется внешним, а вложенные циклы — внутренними. Правила организации как внешнего, так и внутренних циклов такие же, как и простого цикла. Задача 3. На интервале [2; n] найти натуральное число с максимальной суммой делителей. Предлагаемая задача может быть отнесена к классу «задачи целочисленной арифметики», где аргументы, результаты и промежуточные величины относятся к целому типу. Следует заметить, что в такого рода задачах довольно часто используются операции DIV и MOD; наиболее типичной подзадачей является определение количества цифр в записи числа. Алгоритм решения задачи: 1) ввести число n; 2) переменной для хранения максимальной суммы делителей присвоить значение 1 (это сумма делителей числа 1); 3) запомнить число с максимальной суммой делителей; 4) параметру цикла I присвоить значение 2; 5) если I больше n, перейти к п. 13, иначе - к следующему пункту; 6) переменной для хранения очередной суммы делителей присвоить значение 0; 7) параметру цикла K присвоить значение 1; 8) если K больше I/2, перейти к п. 11, иначе - к следующему пункту; 9) если I делится на K без остатка, добавить K к текущей сумме делителей; 10) увеличить K на 1 и перейти к п. 8; 11) сравнить текущую сумму делителей с максимальной, если максимальная меньше, запомнить новое значение и число, соответствующее этой сумме; 12) увеличить I на 1 и перейти к п. 5; 13) вывести число с максимальной суммой делителей и эту сумму; 14) конец. Program Sum_Del; Var N, I, Sum_Max, Sum, K, Ch : Integer; Begin Write('Введите число N: '); ReadLn(N); Sum_Max := 1; {Максимальная сумма делителей} Ch := 1; {Число с максимальной суммой делителей} For I := 2 To N Do {Это цикл по количеству чисел} Begin Sum := 0; For K := 1 To I Div 2 + 1 Do {В этом цикле находим сумму делителей} If I Mod K = 0 Then {Если I нацело делится на K, то K - делитель I} Sum := Sum + K; Sum := Sum + I; If Sum > Sum_Max Then Begin Sum_Max := Sum; Ch := I End; End; WriteLn('Максимальную сумму делителей ', Sum_Max, ' имеет число ',Ch) End. Задача 4. Дано натуральное число n. Получить все простые делители этого числа. 37 {Программа отыскания простых делителей данного числа} Program Pr_Del; Var N, I, Vsp : Integer; Log_Per, Priznak : Boolean; Begin Write('Введите натуральное число: '); ReadLn(N); Priznak := True; {Признак того, не является ли введенное число простым} {Пока параметр цикла не превысил квадратного корня из данного числа, продолжаем поиск простых делителей} For I := 2 To Round(Sqrt(N)) Do If N Mod I = 0 Then Begin Priznak := False; {Введенное число не является простым} Log_Per := False; {Логическая переменная, принимающая значение True, если нашлись делители I, отличные от 1 и I} Vsp := 2; Repeat If (I Mod Vsp = 0) And (I <> Vsp) Then Log_Per := True; Vsp := Vsp + 1 Until (Vsp > I Div 2 + 1) Or Log_Per; If Not(Log_Per) Then WriteLn(I) {Если число I простое, печатаем его} End; If Priznak Then WriteLn(N) End. Предлагаем читателю самостоятельно разобраться с представленным решением. Лекция №4. Одномерные и двумерные массивы Массив — это пронумерованная последовательность величин одинакового типа, обозначаемая одним именем. Элементы массива располагаются в последовательных ячейках памяти, обозначаются именем массива и индексом. Каждое из значений, составляющих массив, называется его компонентой (или элементом массива). Массив данных в программе рассматривается как переменная структурированного типа. Массиву присваивается имя, посредством которого можно ссылаться как на массив данных в целом, так и на любую из его компонент. Вообще, массив – однородный, упорядоченный структурированный тип данных с прямым доступом к элементам. Переменные, представляющие компоненты массивов, называются переменными с индексами в отличие от простых переменных, представляющих в программе элементарные данные. Индекс в обозначении компонент массивов может быть константой, переменной или выражением порядкового типа (целочисленный, логический, символьный, перечислимый, диапазон). Если за каждым элементом массива закреплен только один его порядковый номер, то такой массив называется линейным. Вообще количество индексов элементов массива определяет размерность массива. По этом признаку массивы делятся на одномерные (линейные), двумерные, трёхмерные и т.д. Пример: числовая последовательность четных натуральных чисел 2, 4, 6, ..., N представляет собой линейный массив, элементы которого можно обозначить А[1]=2, А[2]=4, А[3]=6, ..., А[К]=2*(К+1), где К — номер элемента, а 2, 4, 6, ..., N — значения. Индекс (порядковый номер элемента) записывается в квадратных скобках после имени 38 массива. Например, A[7] — седьмой элемент массива А; D[6] — шестой элемент массива D. Для размещения массива в памяти ЭВМ отводится поле памяти, размер которого определяется типом, длиной и количеством компонент массива. В языке Pascal эта информация задается в разделе описаний. Массив описывается так: имя массива : Array [тип индекса] Of базовый тип; Чаще всего типом индекса является диапазон. Например, Var B : Array [1..5] Of Real, R : Array [1..34] Of Char; — описывается массив В, состоящий из 5 элементов и символьный массив R, состоящий из 34 элементов. Для массива В будет выделено 5*6=30 байт памяти, для массива R — 1*34=34 байта памяти. Базовый тип элементов массива может быть любым простым или структурированным, за исключением файлового. Кроме того, массив можно объявить с использованием собственного типа: Type mas = array[1..100] of integer; mas_ = array[1..50] of real; Var a, b: mas; R: mas_; Заполнить массив можно следующим образом: 1) с помощью оператора присваивания. Этот способ заполнения элементов массива особенно удобен, когда между элементами существует какая-либо зависимость, например, арифметическая или геометрическая прогрессии, или элементы связаны между собой рекуррентным соотношением. Задача 1. Заполнить одномерный массив элементами, отвечающими следующему соотношению: a1=1; a2=1; ai=ai-2+ai-1 (i = 3, 4, ..., n). Read(N); {Ввод количества элементов} A[1]:= 1; A[2]:= 1; FOR I := 3 TO N DO A[I] := A[I - 1] + A[I - 2]; Другой вариант присваивания значений элементам массива — заполнение значениями, полученными с помощью датчика случайных чисел. Задача 2. Заполнить одномерный массив с помощью датчика случайных чисел таким образом, чтобы все его элементы были различны. Program Create; Type Mas = Array[1..100] Of Integer; Var A : Mas; I, J, N : Byte; Log : Boolean; Begin Write(''); ReadLn(N); randomize; A[1] := -32768 + random(65535); For I := 2 To N Do Begin Log := True; Repeat 39 A[i] := -32768 + random(65535); J := 1; While Log and (j <= i - 1) Do begin Log := a[i] <> a[j]; j := j + 1 End Until Log End; For i := 1 to N Do Write(a[i]:7); writeln End. 2) ввод значений элементов массива с клавиатуры используется обычно тогда, когда между элементами не наблюдается никакой зависимости. Например, последовательность чисел 1, 2, -5, 6, -111, 0 может быть введена в память следующим образом: Program Vvod; Var N, I : Integer; A : Array [1..20] Of Integer; Begin Write('Введите количество элементов массива '); ReadLn(N); FOR I := 1 TO N DO Begin Write('Введите A[', I, '] '); ReadLn(A[I]) End. Над элементами массивами чаще всего выполняются такие действия, как а) поиск значений; б) сортировка элементов в порядке возрастания или убывания; в) подсчет элементов в массиве, удовлетворяющих заданному условию. Сумму элементов массива можно подсчитать по формуле S=S+A[I] первоначально задав S=0. Количество элементов массива можно подсчитать по формуле К=К+1, первоначально задав К=0. Произведение элементов массива можно подсчитать по формуле P = P * A[I], первоначально задав P = 1. Задача 3. Дан линейный массив целых чисел. Подсчитать, сколько в нем различных чисел. {Подсчет количества различных чисел в линейном массиве. Идея решения: используем вспомогательный массив, элементами которого являются логические величины (False - если элемент уже встречался ранее, True - иначе)} Program Razlichnye_Elementy; Var I, N, K, Kol : Integer; A : Array [1..50] Of Integer; Lo : Array [1..50] Of Boolean; Begin Write('Введите количество элементов массива: '); ReadLn(N); FOR I := 1 TO N DO Begin Write('A[', I, ']='); ReadLn (A[I]); Lo[I] := True; {Заполняем вспомогательный массив значениями True} End; 40 Kol := 0; {переменная, в которой будет храниться количество различных чисел} FOR I := 1 TO N DO IF Lo[I] THEN Begin Kol := Kol + 1; FOR K := I TO N DO {Во вспомогательный массив заносим значение False, если число уже встречалось ранее или совпадает с текущим элементом A[I]} Lo[K] := (A[K] <> A[I]) And Lo[K]; End; WriteLn('Количество различных чисел: ', Kol) END. Тест: N = 10; элементы массива - 1, 2, 2, 2, -1, 1, 0, 34, 3, 3. Ответ: 6. Задача 4. Дан линейный массив. Упорядочить его элементы в порядке возрастания. {Сортировка массива выбором (в порядке возрастания). Идея решения: пусть часть массива (по K-й элемент включительно) отсортирована. Нужно найти в неотсортированной части массива минимальный элемент и поменять местами с (K+1)-м} Program Sortirovka; Var N, I, J, K, Pr : Integer; A : Array [1..30] Of Integer; Begin Write('Введите количество элементов: '); ReadLn(N); For I := 1 To N Do Begin Write('Введите A[', I, '] '); Readln(A[I]); End; WriteLn; For I := 1 To N - 1 Do Begin K := I; For J := I + 1 To N Do If A[J] <= A[K] Then K := J; Pr := A[I]; A[I] := A[K]; A[K] := Pr; End; For I := 1 To N Do Write(A[I], ' '); End. Тест: N = 10; элементы массива - 1, 2, 2, 2, -1, 1, 0, 34, 3, 3. Ответ: -1, -1, 0, 1, 2, 2, 2, 3, 3, 34. Если два массива являются массивами эквивалентных типов, то возможно присваивание одного массива другому. При этом все компоненты присваиваемого массива копируются в тот массив, которому присваивается значение. Типы массивов будут эквивалентными, если эти массивы описываются совместно или описываются идентификатором одного и того же типа. Например, в описании Type Massiv = Array[1..10] Of Real; Var A, B : Massiv; C, D : Array[1..10] Of Real; E : Array[1..10] Of Real; типы переменных A, B эквивалентны, и поэтому данные переменные совместимы по присваиванию; тип переменных C, D также один и тот же, и поэтому данные переменные также совместны по присваиванию. Но тип переменных C, D не эквивалентен 41 типам переменных A, B, E, поэтому, например, A и D не совместны по присваиванию. Эти особенности необходимо учитывать при работе с массивами. При работе с массивами целесообразно использовать процедуры и функции. Вот типовые процедуры: Type mas = array[1..100] of LongInt; {Заполнение массива с помощью ввода с клавиатуры} Procedure Vvod_Kl(var n: byte; var a: mas); Var i: byte; Begin Write(‘Количество элементов?’); Readln(n); For i := 1 to n do Begin write(i, ‘-й элемент’); readln(a[i]) End End; {Заполнение массива случайными данными} Procedure Vvod_Sl(var n: byte; var a: mas); Var i: byte; Begin Write(‘Количество элементов?’); Readln(n); For i := 1 to n do a[i] := -1000+random(2001) End; {Вывод массива} Procedure Print(n: byte; const a: mas); Var i: byte; Begin For i := 1 to n do write(a[i] :8); writeln End; Задача 5. Дан линейный массив. Найти: сумму минимального и максимального элементов; количество отрицательных элементов, стоящих на чётных местах. Изменить массив, вычеркнув из него нечетные элементы. Function Min(n: byte; const a: mas): byte; Var i, m : byte; Begin m:= 1; for i:=2 to n do if a[i] < a[m] then m:=i; Min := m End; Function Max(n: byte; const a: mas): byte; Var i, m : byte; Begin m:= 1; for i:=2 to n do if a[i] > a[m] then m:=i; Max := m End; 42 Function K(n: byte; const a: mas): byte; Var i, kol : byte; Begin kol:= 0; for i:=1 to n do if (a[i]<0) and not odd(i) then kol:=kol+1; K:= kol End; Procedure V(var n: byte; var a:mas); Var i, j: byte; Begin i:=1; while i <= n do if odd(a[i]) then begin for j:=i+1 to n do a[j-1]:=a[j]; n:=n-1 end else i:= i+1 End; {procedure vvod_sl - сюда подставить приведенные выше процедуры} {procedure print} Var a:mas; n:byte; Begin randomize; vvod_sl(n, a); print(n, a); writeln('Сумма минимального и максимального элементов', a[Min(n, a)] + a[Max(n, a)]); writeln('Количество отрицательных элементов, стоящих на чётных местах: ', K(n, a)); V(n, a); print(n, a) End. При решении практических задач часто приходится иметь дело с различными таблицами данных, математическим эквивалентом которых служат матрицы. Такой способ организации данных, при котором каждый элемент определяется номером строки и номером столбца, на пересечении которых он расположен, называется двумерным массивом или таблицей. Например, данные о планетах Солнечной системы представлены следующей таблицей: Планета Расст. до Солнца Относ. обьем Относ. масса Меркурий 57.9 0.06 0.05 Венера 108.2 0.92 0.81 Земля 149.6 1.00 1.00 Марс 227.9 0.15 0.11 Юпитер 978.3 1345.00 318.40 Сатурн 1429.3 767.00 95.20 43 Их можно занести в память компьютера, используя понятие двумерного массива. Положение элемента в массиве определяется двумя индексами. Они показывают номер строки и номер столбца. Индексы разделяются запятой. Например: A[7, 6], D[56, 47]. Заполняется двумерный массив аналогично одномерному: с клавиатуры, с помощью оператора присваивания. Например, в результате выполнения программы: Program Vvod2; Var I, J : Integer; A : Array [1..20, 1..20] Of Integer; Begin FOR I := 1 TO 3 DO FOR J := 1 TO 2 DO A[I, J] := 456 + I End. элементы массива примут значения A[1, 1] = 457; A[1, 2] = 457; A[2, 1] = 458; A[2, 2] = 458; A[3, 1] = 459; A[3, 2] = 459. При описании массива задается требуемый объем памяти под двумерный массив, указываются имя массива и в квадратных скобках диапазоны изменения индексов. При выполнении инженерных и математических расчетов часто используются переменные более чем с двумя индексами. При решении задач на ЭВМ такие переменные представляются как компоненты соответственно трех-, четырехмерных массивов и т.д. Однако описание массива в виде многомерной структуры делается лишь из соображений удобства программирования как результат стремления наиболее точно воспроизвести в программе объективно существующие связи между элементами данных решаемой задачи. Что же касается образа массива в памяти ЭВМ, то как одномерные, так и многомерные массивы хранятся в виде линейной последовательности своих компонент, и принципиальной разницы между одномерными и многомерными массивами в памяти ЭВМ нет. Однако порядок, в котором запоминаются элементы многомерных массивов, важно себе представлять. В большинстве алгоритмических языков реализуется общее правило, устанавливающее порядок хранения в памяти элементов массивов: элементы многомерных массивов хранятся в памяти в последовательности, соответствующей более частому изменению младших индексов. Задача 6. Заполнить матрицу порядка n по следующему образцу: 1 2 3 ... n-2 n-1 n 2 1 2 ... n-3 n-2 n-1 3 2 1 ... n-4 n-3 n-2 ... ... ... ... ... ... ... n-1 n-2 n-3 ... 2 1 2 n n-1 n-2 ... 3 2 1 Program Massiv12; Var I, J, K, N : Integer; A : Array [1..10, 1..10] Of Integer; Begin Write('Введите порядок матрицы: '); ReadLn(N); For I := 1 To N Do For J := I To N Do Begin A[I, J] := J - I + 1; A[J, I] := A[I, J]; End; For I := 1 To N Do 44 Begin WriteLn; For J := 1 To N Do Write(A[I, J]:4); End End. Задача 7. Дана целочисленная квадратная матрица. Найти в каждой строке наибольший элемент и поменять его местами с элементом главной диагонали. Program Obmen; Var N, I, J, Max,Ind, Vsp : Integer;A : Array [1..15, 1..15] Of Integer; Begin WRITE('Введите количество элементов в массиве: '); READLN(N); FOR I := 1 TO N DO FOR J := 1 TO N DO Begin WRITE('A[', I, ',', J, '] '); READLN(A[I, J]) End; FOR I := 1 TO N DO Begin Max := A[I, 1]; Ind := 1; FOR J := 2 TO N DO IF A[I, J] > Max THEN Begin Max := A[I, J]; Ind := J End; Vsp := A[I, I]; A[I, I] := A[I, Ind]; A[I, Ind] := Vsp End; FOR I := 1 TO N DO Begin WriteLn; FOR J := 1 TO N Do Write(A[I, J] : 3); End; WriteLn End. Задача 8. Задана прямоугольная целочисленная таблица размером m×n. Указать столбец (назвать его номер), где минимальное количество элементов, кратных сумме индексов. type mas2=array[1..20, 1..20] of integer; {Формирование массива заданного размера с помощью датчика псевдослучайных чисел} procedure create(var m, n: byte; var a: mas2); var i, j: byte; begin write('Введите размеры таблицы: '); readln(m, n); for i:=1 to m do for j:= 1 to n do a[i, j]:= -20+random(41); end; {вывод таблицы на экран} procedure print(const m, n: byte; const a: mas2); 45 var i, j: byte; begin for i:=1 to m do begin writeln; for j:=1 to n do write(a[i, j]:5); end; writeln end; {Поиск нужного столбца; результат - в переменной k и, далее, - значение функции} function solution(const m, n: byte; const a: mas2): byte; var i, j, k, ke, mke: byte; begin k:=1; mke:=m; for j:=1 to n do begin ke:=0; for i:=1 to m do if abs(a[i, j]) mod (i+j) = 0 then ke:=ke+1; if ke<mke then begin mke:=ke; k:=j end end; solution := k; end; var m, n: byte; a: mas2; begin randomize; create(m, n, a); print(m, n, a); writeln('Ответ: ', solution(m, n, a)) end. Лекция №5. Поиск и сортировка в одномерных массивах Поиск Задачи поиска элемента, обладающего заданными свойствами, достаточно часто приходится решать применительно к массивам (см. материал "Массивы"). Следует заметить, что если массив не отсортирован, то поиск неэффективен. Вообще, с развитием информационных и информационно-поисковых систем рационализация алгоритмов поиска является актуальной задачей. Рассмотрим простейший алгоритм поиска для отсортированного массива (для определенности пусть элементы массива расположены по возрастанию). Алгоритм носит название двоичного (бинарного) поиска, т.к. на каждом шаге область поиска уменьшается вдвое. Пусть в отсортированном массиве требуется найти элемент со значением x, или указать, что такого элемента там нет. Выберем средний элемент. Для этого элемента относительно значения x возможны три случая: элемент равен x (поиск завершен); 46 элемент больше x (поиск необходимо продолжить в левой части массива); элемент меньше x (поиск необходимо продолжить в правой части массива). В случаях 2-3 поиск (если это ещё возможно) продолжается. Для этого в выделенной части массива вновь выбирается средний элемент и проводятся аналогичные рассуждения. И т.д., до тех пор, пока поиск не будет завершен. Поиск завершается в одном из двух случаев: 1. элемент найден; 2. элемент не найден (это констатируется в том случае, когда длина области поиска уменьшилась до нуля, т.е. левая и правая границы области поиска сомкнулись). Рассмотрим программную реализацию алгоритма. {Алгоритм двоичного поиска} type mas= array[1..100] of integer; {Процедура вывода массива} Procedure Print(n: byte; const a: mas); Var i: byte; Begin For i := 1 to n do write(a[i] :8); writeln End; {Процедура формирования массива из случайных элементов, расположенных по возрастанию} Procedure Vvod_Sl(var n: byte; var a: mas); Var i: byte; Begin Write(‘Количество элементов?’); Readln(n); a[1]:= -1000+random(2001); For i := 2 to n do a[i] := a[i-1]+random(20)+1; End; function find(a: mas; n: byte; x: integer): byte; var L, R, c: byte; Begin L := 1; R := n; {изначально область поиска - весь массив} repeat c := (L + R) div 2; {индекс среднего элемента} if a[c] > x then R := c - 1; {случай 2} if a[c] < x then L := c + 1; {случай 3} until (a[c]=x) or (L > R); if a[c]=x then find:=c else find := 0; {возвращем индекс элемента или нуль, если элемент не найден} end; var a: mas; n, r: byte; k: integer; begin randomize; Vvod_Sl(n, a); print(n, a); 47 write('Искомый элемент? '); readln(k); r := find(a, n, k); if r = 0 then writeln('Элемент не найден') else writeln(r) end. Сортировка Обычно сортировку подразделяют на два класса: внутреннюю и внешнюю. При внутренней сортировке все элементы хранятся в оперативной памяти, таким образом, как правило, это сортировка массивов. При внешней сортировке — элементы хранятся на внешнем запоминающем устройстве, это сортировка файлов. Одно из Основных требований к методам сортировки — экономное использование памяти. Это означает, что переупорядочение нужно выполнять «на том же месте», то есть методы пересылки элементов из одного массива в другой не представляют интереса. Удобной мерой эффективности является подсчет числа С — сравнений элементов и М — присваиваний элементов. Достаточно хороший алгоритм затрачивает на сортировку N элементов время порядка N×log2(N). Простейшие алгоритмы сортировки, которые мы рассмотрим в этом разделе, обладают характеристикой порядка N2. Если N достаточно мало, то простые алгоритмы выгодно использовать в силу простоты их реализации. Сортировка выбором Алгоритм сортировки. Пусть часть массива до i-1 элемента включительно отсортирована. Выбираем в не сортированной части минимальный элемент и меняем его местами с i-м. Изначально отсортированная часть состоит из нулевого количества элементов. procedure sort(var a : mas; n : byte); var i, j, min: byte; vsp : integer; begin for i := 1 to n - 1 do begin min:=i; for j := i+1 to n do if a[j]<a[min] then min := j; vsp:=a[i]; a[i]:=a[min]; a[min]:=vsp; end; end; Сортировка обменом (пузырьковая) Массив просматривается N-1 раз. При каждом просмотре сравниваются каждые два соседних элемента. Если элемент с меньшим индексом оказывается больше, производится их обмен. procedure obmen(var a : mas; n : byte); var i, j: byte; vsp : integer; begin for i := 1 to n - 1 do for j := 1 to n - i do if a[j]>a[j+1] then begin vsp:=a[j]; a[j]:=a[j+1]; a[j+1]:=vsp; end 48 end; Сортировка вставками Пусть часть массива отсортирована. Выбираем в неотсортированной части очередной элемент и вставляем его в отсортированную так, чтобы упорядоченность элементов сохранилась. При поиске места вставки осуществляем сдвиг элементов в сторону возрастания номеров. procedure vstavka(var a : mas; n : byte); var i, j: byte; vsp : integer; begin for i := 2 to n do begin vsp:=a[i]; j:= i-1; while (a[j]>vsp) and (j>1) do begin a[j+1]:=a[j]; j:=j-1 end; if (a[j]>vsp) and (j=1) then begin a[2]:=a[1]; a[1]:= vsp end else a[j+1]:=vsp; end end; Лекция №6. Подпрограммы (процедуры и функции). Рекурсия При решении новых задач можно попытаться воспользоваться ранее написанными программами. Алгоритм, ранее разработанный и целиком используемый в составе других алгоритмов, называется вспомогательным. Применение вспомогательных алгоритмов позволяет разбить задачу на части, структурировать ее. Вся программа условно может быть разделена на две части: основную и вспомогательную. В основной части производится простейшая обработка информации, организуется обращение к разным вспомогательным модулям (подпрограммам). Вспомогательный алгоритм тоже может вызывать другие вспомогательные, длина такой цепочки вызовов теоретически не ограничена. Здесь и далее следующие пары слов используются как синонимы: алгоритм и программа, вспомогательный алгоритм и подпрограмма, команда и оператор, программа и модуль. Вспомогательными и основными алгоритмы являются не сами по себе, а по отношению друг к другу. При использовании вспомогательных алгоритмов необходимо учитывать способ передачи значений исходных данных для них и получения результата от них. Аргументы вспомогательного алгоритма — это переменные, в которых должны быть помещены исходные данные для решения соответствующей подзадачи. Результаты вспомогательного алгоритма — это также переменные, где содержаться результаты решения этих подзадач, а также результатом может быть конкретное действие, которое совершает компьютер под действием подпрограммы. Подпрограммы могут быть двух видов: подпрограмма без параметров и подпрограмма с параметрами. Обращение к подпрограмме может быть организовано из 49 любого места основной программы или другой подпрограммы сколько угодно раз. При работе с подпрограммами важными являются понятия формальных и фактических параметров. Формальные параметры — это идентификаторы входных данных для подпрограммы. Если формальные параметры получают конкретные значения, то они называются фактическими. Формальные параметры могут получить конкретные значения только в той программе, где производится обращение к данному модулюподпрограмме. Тип и порядок записи фактических параметров должны быть такими же, как и формальных параметров. В противном случае результат работы программы будет непредсказуемым. Из этого следует, что фактические параметры используются при обращении к подпрограмме из основной, а формальные параметры — только в самом модуле. Подпрограмма с параметрами используется для записи многократно повторяющихся действий при разных исходных данных. Подпрограммы с параметрами можно разделить на два типа: подпрограммы-функции и просто подпрограммы с параметрами (их называют процедурами). При составлении подпрограмм с параметрами надо соблюдать следующие правила: 1) каждая подпрограмма имеет свое имя и список формальных параметров; 2) процедура из основной программы вызывается командой вызова, которая по форме ничем не отличается от вызова команды исполнителя. Результат присваивается одной или нескольким переменным, которые находятся в списке формальных параметров. Но результатом могут быть, конечно, не только значения переменных, но какое либо действие, выполненное ЭВМ. Пример 1. Используем алгоритм нахождения наибольшего общего делителя двух натуральных чисел в качестве вспомогательного при решении задачи: составить программу вычитания дробей (a, b, c, d — натуральные числа). Результат представить в виде обыкновенной несократимой дроби. Подпрограмма. 1) Ввести натуральные числа M, N. 2) Если M=N, перейти к п. 5, иначе к следующему пункту. 3) Если M>N, то M:=M-N, иначе N:=N-M. 4) Перейти к п. 2. 5) Передать значение M в основную программу. 6) Конец подпрограммы. Основная программа. 1) Ввести значения A, B, C, D. 2) E:=A*D - B*C. 3) F:= B*D. 4) Если E=0, вывести значение E и перейти к п. 9, иначе перейти к следующему пункту. 5) M:=|E|, N:=F, перейти к подпрограмме вычисления НОД. 6) G := M. 7) E и F нацело разделить на G. 8) Вывести значения E и F на печать. 9) Конец программы. Program Sub; Var A, B, C, D, G, E, F : Integer; 50 Procedure Nod(M, N : Integer; Var K : Integer); Begin While M <> N Do If M > N Then M := M - N Else N := N - M; K := M End; Begin Write('Введите числители и знаменатели дробей:'); ReadLn(A, B, C, D); E := A * D - B * C; F := B * D; If E = 0 Then WriteLn(E) Else Begin Nod(Abs(E), F, G); E := E Div G; F := F Div G; WriteLn('Ответ: ', E, '/', F) End End. Как видно из примера, объявление и тело подпрограмм находится в разделе описаний. В заголовке подпрограммы содержится список формальных параметров с указанием их типа, которые условно можно разделить на входные и выходные (перед ними стоит служебное Var). При обращении к процедуре указывается ее имя и список фактических параметров. Формальные и фактические параметры должны соответствовать по количеству и по типу. Вызов процедуры осуществляется следующим образом: <Идентификатор (имя) процедуры>(<список параметров>); фактических Например, Nod(Abs(E), F, G); По способу передачи фактических значений в подпрограмму в Turbo Pascal 7.0 выделяют параметры-переменные, параметры-значения, параметры-константы и массивы открытого типа, строки открытого типа, параметры-процедуры, параметры-функции (подробности — в литературе). Функция (в отличие от процедуры) всегда возвращает единственное значение. Покажем, как изменится подпрограмма из примера, если ее записать в виде функции. Function Nod(M, N : Integer) : Integer; Begin While M <> N Do If M > N Then M := M - N Else N := N - M; Nod := M End; Итак, после списка параметров указывается тип значения функции, а в теле функции хотя бы один раз встречается присваивание переменной, имя которой совпадает с именем функции, соответствующего значения. 51 Вызов функции будет следующим: G := Nod(Abs(E), F); Вообще, вызов функции может присутствовать в выражении, стоящем: в правой части оператора присваивания, в процедуре вывода, в качестве фактического параметра в вызове другой подпрограммы и т.д. При решении задач целесообразно проанализировать условие, записать решение в крупных блоках (не являющихся операторами Pascal), детализировать каждый из блоков (записав в виде блоков, возможно, по-прежнему не операторов Pascal), и т.д., продолжать до тех пор, пока каждый из блоков не будет реализован с помощью операторов языка. Пример 2. Дано натуральное число n. Переставить местами первую и последнюю цифры этого числа. Program Integ; Var N : Integer; Begin Write('Введите натуральное число: '); ReadLn(N); If Impossible(N) Then WriteLn('Невозможно переставить цифры, возникнет переполнение') Else Begin Change(N); WriteLn('Ответ: ', N) End; End. Можно заметить, что необходимо детализировать логическую функцию Impossible, которая диагностирует, возможна ли перестановка, и процедуру Change, которая эту перестановку (в случае, если она возможна) выполняет. Function Impossible(N : Integer) : Boolean; Begin If Number(N) < 5 Then Impossible := False Else Impossible := (N Mod 10 > 3) Or (N Mod 10 = 3) And (N Mod 10000 Div 10 * 10 + N Div 10000 > MaxInt Mod 10000) End; Здесь необходимо детализировать функцию Number, возвращающую количество цифр в записи натурального числа (т.к. функция Impossible содержит ее вызов, то в разделе описаний функция Number должна ей предшествовать). Function Number(N : Integer) : Integer; Var Vsp : Integer; Begin Vsp := 0; While N > 0 Do Begin Vsp := Vsp + 1; N := N Div 10 End; Number := Vsp End; 52 Наконец, последняя процедура. Procedure Change(Var N : Integer); Var Kol, P, S, R : Integer; Begin Kol := Number(N); P := N Mod 10; {последняя цифра} If Kol > 1 Then S := N Div Round(Exp((Kol - 1) * Ln(10))) Else S := 0; {первая цифра} R := N Mod Round(Exp((Kol - 1) * Ln(10))) Div 10; N := P * Round(Exp((Kol - 1) * Ln(10))) + R * 10 + S End; Возможны также подпрограммы, которые вызывают сами себя. Они называются рекурсивными. Создание таких подпрограмм является красивым приемом программирования, но не всегда целесообразно из-за чрезмерного расхода памяти ЭВМ. Пример 3. Найти максимальную цифру в записи данного натурального числа. Program MaxDigit; Type NaturLong = 1..(High(LongInt)); Digit = 0..9; Var A : LongInt; Function Maximum(N : LongInt) : Digit; Begin If N < 10 Then Maximum := N Else If N Mod 10 > Maximum(N Div 10) Then Maximum := N mod 10 Else Maximum := Maximum(N Div 10) End; Begin Write('Введите натуральное число: '); ReadLn(A); WriteLn('Максимальная цифра равна ', Maximum(A)) End. При создании функции Maximum было использовано следующее соображение: если число состоит из одной цифры, то она является максимальной, иначе если последняя цифра не является максимальной, то ее следует искать среди других цифр числа. При написании рекурсивного алгоритма следует позаботиться о граничном условии, когда цепочка рекурсивных вызовов обрывается и начинается ее обратное «раскручивание». В нашем примере это условие N < 10. Контрольные вопросы и задания 1. Какие алгоритмы называют вспомогательными? 2. какое количество вспомогательных алгоритмов может присутствовать в основном алгоритме? 3. Можно ли вспомогательные алгоритмы, написанные для решения данной задачи, использовать при решении других задач, где их применение было бы целесообразно? 53 4. Какие параметры называют формальными? фактическими? 5. Какое соответствие должно соблюдаться между формальными и фактическими параметрами? 6. Может ли фактических параметров процедуры (функции) быть больше, чем формальных? А меньше? 7. Существуют ли подпрограммы без параметров? 8. Существуют ли ограничения на число параметров подпрограмм? Если нет, то чем же всё-таки ограничивается это количество в Turbo Pascal? 9. В каком разделе объявляются и реализуются подпрограммы в Turbo Pascal? 10. Какие виды формальных параметров существуют? Чем они отличаются друг от друга? 11. В чём состоит отличие процедур и функций? 12. В каких случаях целесообразно использовать функции? 13. Почему, если в функции используются параметры-переменные, необходимо преобразовать её в процедуру? 14. Какого типа может быть значение функции? 15. Расскажите о методе последовательной детализации при разработке программ. 16. Какие подпрограммы называют рекурсивными? 17. Что такое граничное условие при организации рекурсивной подпрограммы? Лекция №7. Рекурсия Рекурсия — это такой способ организации вспомогательного алгоритма (подпрограммы), при котором эта подпрограмма (процедура или функция) в ходе выполнения ее операторов обращается сама к себе. Вообще, рекурсивным называется любой объект, который частично определяется через себя. Например, приведенное ниже определение двоичного кода является рекурсивным: <двоичный код> ::= <двоичная цифра> | <двоичный код><двоичная цифра> <двоичная цифра> ::= 0 | 1 Здесь для описания понятия были использованы, так называемые, металингвистический формулы Бэкуса-Наура (язык БНФ); знак "::=" обозначает "по определению есть", знак "|" — "или". Вообще, в рекурсивном определении должно присутствовать ограничение, граничное условие, при выходе на которое дальнейшая инициация рекурсивных обращений прекращается. Приведём другие примеры рекурсивных определений. Пример 1. Классический пример, без которого не обходятся ни в одном рассказе о рекурсии, — определение факториала. С одной стороны, факториал определяется так: n!=1*2*3*...*n. С другой стороны, Граничным условием в данном случае является n<=1. Пример 2. Определим функцию K(n), которая возвращает количество цифр в заданном натуральном числе n: Задание. По аналогии определите функцию S(n), вычисляющую сумму цифр заданного натурального числа. 54 Пример 3. Функция C(m, n), где 0 <= m <= n, для вычисления биномиального коэффициента по следующей формуле является рекурсивной. Ниже будут приведены программные реализации всех этих (и не только) примеров. Обращение к рекурсивной подпрограмме ничем не отличается от вызова любой другой подпрограммы. При этом при каждом новом рекурсивном обращении в памяти создаётся новая копия подпрограммы со всеми локальными переменными. Такие копии будут порождаться до выхода на граничное условие. Очевидно, в случае отсутствия граничного условия, неограниченный рост числа таких копий приведёт к аварийному завершению программы за счёт переполнения стека. Порождение все новых копий рекурсивной подпрограммы до выхода на граничное условие называется рекурсивным спуском. Максимальное количество копий рекурсивной подпрограммы, которое одновременно может находиться в памяти компьютера, называется глубиной рекурсии. Завершение работы рекурсивных подпрограмм, вплоть до самой первой, инициировавшей рекурсивные вызовы, называется рекурсивным подъёмом. Выполнение действий в рекурсивной подпрограмме может быть организовано одним из вариантов: Begin Begin Begin P; операторы; операторы; операторы; P P; End; End; операторы End; рекурсивный подъём рекурсивный спуск подъём и рекурсивный спуск, и рекурсивный Здесь P — рекурсивная подпрограмма. Как видно из рисунка, действия могут выполняться либо на одном из этапов рекурсивного обращения, либо на обоих сразу. Способ организации действий диктуется логикой разрабатываемого алгоритма. Реализуем приведённые выше рекурсивные определения в виде функций и процедур на языке Pascal и в виде функций на языке C. Пример 1. {Функция на Pascal} Function Factorial(N:integer):Extended; Begin If N<=1 Then Factorial:=1 Else Factorial:=Factorial(N-1)*N End; {Процедура на Pascal} Procedure Factorial(N:integer; Var F:Extended); Begin If N<=1 Then F:=1 Else Begin Factorial(N-1, F); F:=F*N End End; /* Функция на C */ double Factorial(int N) { double F; if (N<=1) F=1.; else F=Factorial(N-1)*N; return F; 55 } Пример 2. {Функция на Pascal} Function K(N:Longint):Byte; Begin If N<10 Then K:=1 Else K:=K(N div 10)+1 End; {Процедура на Pascal} Procedure K(N:Longint; Var Kol:Byte) Begin If N<10 Then Kol:=1 Else Begin K(N Div 10, Kol); Kol:=Kol+1 End; End; /* Функция на C */ int K(int N) { int Kol; if (N<10) Kol=1; else Kol=K(N/10)+1; return Kol; } Пример 3. {Функция на Pascal} function C(m, n :Byte):Longint; Begin If (m=0) or (m=n) Then C:=1 Else C:=C(m, n-1)+C(m-1, n-1) End; {Процедура на Pascal} Procedure C(m, n: Byte; Var R: Longint); Var R1, R2 : Longint; Begin If (m=0) or (m=n) Then R:=1 Else Begin C(m, n-1, R1); C(m-1, n-1, R2); R:=R1+R2 End; End; /* Функция на C */ int C(int m, int n) { int f; if (m==0||m==n) f=1; else f=C(m, n-1)+C(m-1, n-1); return f; } Пример 4. Вычислить сумму элементов линейного массива. При решении задачи используем следующее соображение: сумма равна нулю, если 56 количество элементов равно нулю, и сумме всех предыдущих элементов плюс последний, если количество элементов не равно нулю.{Программа на языке Pascal} Program Rec2; Type LinMas = Array[1..100] Of Integer; Var A : LinMas; I, N : Byte; {Рекурсивная функция} Function Summa(N : Byte; A: LinMas) : Integer; Begin If N = 0 Then Summa := 0 Else Summa := A[N] + Summa(N - 1, A) End; {Основная программа} Begin Write('Количество элементов массива? '); ReadLn(N); Randomize; For I := 1 To N Do Begin A[I] := -10 + Random(21); Write(A[I] : 4) End; WriteLn; WriteLn('Сумма: ', Summa(N, A)) End. /* Программа на языке C */ #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <time.h> int summa(int N, int a[100]); int i,n, a[100]; void main() { clrscr(); printf("\nКоличество элементов массива? "); scanf("%d", &n); printf("\nВ сформированном массиве %d чисел:\n", n); randomize(); for (i=0; i<n; i++) {a[i]= -10+random(21); printf("%d ", a[i]);} printf("Сумма: %d", summa(n-1, a)); } int summa(int N, int a[100]) { if (N==0) return a[0]; else return a[N]+summa(N-1, a); } Пример 5. Определить, является ли заданная строка палиндромом, т.е. читается одинаково слева направо и справа налево. Идея решения заключается в просмотре строки одновременно слева направо и справа налево и сравнении соответствующих символов. Если в какой-то момент символы не совпадают, делается вывод о том, что строка не является палиндромом, если же удается достичь середины строки и при этом все соответствующие символы совпали, то строка является палиндромом. Граничное условие — строка является палиндромом, если она пустая или состоит из одного символа. {программа на языке Pascal} Program Palindrom; 57 {Рекурсивная функция} Function Pal(S: String) : Boolean; Begin If Length(S)<=1 Then Pal:=True Else Pal:= (S[1]=S[Length(S)]) and Pal(Copy(S, 2, Length(S) - 2)); End; Var S : String; {Основная программа} Begin Write('Введите строку: '); ReadLn(S); If Pal(S) Then WriteLn('Строка является палиндромом') Else WriteLn('Строка не является палиндромом') End. /* программа на языке C */ #include <stdio.h> #include <conio.h> #include <string.h> char s[100]; int pal(char s[100]); void main() { clrscr(); printf("\nВведите строку: "); gets(s); if (pal(s)) printf("Строка является палиндромом"); else printf("Строка не является палиндромом"); } int pal(char s[100]) { int l; char s1[100]; if (strlen(s)<=1) return 1; else {l=s[0]==s[strlen(s)-1]; strncpy(s1, s+1, strlen(s)-2); s1[strlen(s)-2]='\0'; return l&&pal(s1);} } Задание. Используя аналогичный подход, определите, является ли заданное натуральное число палиндромом. Подводя итог, заметим, что использование рекурсии является красивым приёмом программирования. В то же время в большинстве практических задач этот приём неэффективен с точки зрения расходования таких ресурсов ЭВМ, как память и время исполнения программы. Использование рекурсии увеличивает время исполнения программы и зачастую требует значительного объёма памяти для хранения копий подпрограммы на рекурсивном спуске. Поэтому на практике разумно заменять рекурсивные алгоритмы на итеративные. Контрольные вопросы и задания 1. Какое определение называется рекурсивным? Приведите собственные примеры рекурсивных определений. 2. Какой вспомогательный алгоритм (подпрограмма) называются 58 рекурсивными? Приведите собственные примеры содержательных задач, где для решения может быть использован рекурсивный вспомогательный алгоритм. 3. Что такое граничное условие, и каково его назначение в рекурсивной подпрограмме? 4. Что такое рекурсивный спуск? 5. Что такое рекурсивный подъём? 6. Что такое глубина рекурсии? Чему равна глубина рекурсии в приведённых выше примерах? 7. На каком этапе выполнения рекурсивной подпрограммы могут выполняться её операторы? 8. Почему приведённый ниже алгоритм посимвольного формирования строки завершится аварийно? Function Stroka : String; Var C : Char; Begin Write('Введите очередной символ: '); ReadLn(C); Stroka:=Stroka+C End; 9 На каком этапе выполняются действия в этом алгоритме? Лекция №8. Строковый тип данных в языке Pascal Познакомимся с типом данных, который относится к числу структурированных. Это строковый тип данных (строка). Строка — это последовательность символов. Каждый символ занимает 1 байт памяти (код ASCII). Количество символов в строке называется ее длиной. Длина строки может находиться в диапазоне от 0 до 255. Строковые величины могут быть константами и переменными. Особенностью строки в Turbo Pascal является то, что с ней можно работать как с массивом символов, с одной стороны, и как с единым объектом, — с другой. За счет этого обработка строк достаточно гибка и удобна. Строковая константа есть последовательность символов, заключенная в апострофы. Например: 'это строковая константа', ‘272’. Строковая переменная описывается в разделе описания переменных следующим образом: Var <идентификатор> : string[<максимальная длина строки>]; Например: Var Name : string[20]; Параметр длины может и не указываться в описании. В таком случае подразумевается, что он равен максимальной величине — 255. Например: Var slovo : string. Строковая переменная занимает в памяти на 1 байт больше, чем указанная в описании длина. Дело в том, что один (нулевой) байт содержит значение текущей длины строки. Если строковой переменной не присвоено никакого значения, то ее текущая длина равна нулю. По мере заполнения строки символами ее текущая длина возрастает, но она не должна превышать максимальной по описанию величины. Символы внутри строки индексируются (нумеруются) от единицы. Каждый отдельный символ идентифицируется именем строки с индексом, заключенным в квадратные скобки. Например: N[5], S[i], slovo[k+l]. Индекс может быть положительной константой, переменной, выражением целого типа. Значение индекса не должно выходить за границы описания. 59 Тип string и стандартный тип char совместимы. Строки и символы могут употребляться в одних и тех же выражениях. Строковые выражения строятся из строковых констант, переменных, функций и знаков операций. Над строковыми данными допустимы операции сцепления и операции отношения. Операция сцепления (конкатенации) (+) применяется для соединения нескольких строк в одну результирующую строку. Сцеплять можно как строковые константы, так и переменные. Пример: 'Мама ' + 'мыла ' + 'раму'. В результате получится строка: 'Мама мыла раму'. Длина результирующей строки не должна превышать 255. Операции отношения: =, <, >, <=, >=, <>. Позволяют произвести сравнение двух строк, в результате чего получается логическое значение (true или false). Операция отношения имеет приоритет более низкий, чем операция сцепления. Сравнение строк производится слева направо до первого несовпадающего символа, и та строка считается больше, в которой первый несовпадающий символ имеет больший номер в таблице символьной кодировки. Если строки имеют различную длину, но в общей части символы совпадают, считается, что более короткая строка меньше, чем более длинная. Строки равны, если они полностью совпадают по длине и содержат одни и те же символы. Пример:Выражение Результат ‘True1’<’True2’ True ‘Mother’>’MOTHER’ True ‘Мама ‘ <> ‘Мама’ True ‘Cat’=’Cat’ True Функция Copy(S, Pozition, N) выделяет из строки S подстроку длиной N символов, начиная с позиции Pozition. Здесь N и Pozition — целочисленные выражения. Пример: Значение S Выражение Результат ‘Мама мыла раму’ Copy(S, 6, 4) ‘мыла’ ‘Маша ела кашу’ Copy(S, 1, 8) ‘Маша ела’ Функция Concat(S1, S2, …, SN) выполняет сцепление (конкатенацию) строк S1, S2, …, SN в одну строку. Пример: Выражение Concat('Маша ', 'ела ', 'кашу') Результат 'Маша ела кашу' Функция Length(S) — определяет текущую длину строки S. Результат — значение целого типа. Пример: Значение S est-5' '(A+B)*C' Выражение Результат Length(S) 6 Length(S) 7 Функция Pos(S1, S2) — обнаруживает первое появление в строке S2 подстроки S1. Результат — целое число, равное номеру позиции, где находится первый символ подстроки S1. Если в S2 подстроки S1 не обнаружено, то результат равен 0. Пример: Значение S2 'abcdef' Выражение Результат Pos('cd', S2) 3 60 'abcdcdef' 'abcdef' Pos('cd', S2) 3 Pos('k', S2) 0 Процедура Delete(S, Poz, N) — удаление N символов из строки S, начиная с позиции Poz. Пример: Исходное значение S Оператор Конечное значение S 'abcdefg' Delete(S, 3, 2) 'abefg' 'abcdefg' Delete(S, 2, 6) 'a' В результате выполнения процедуры уменьшается текущая длина строки в переменной S. Процедура Insert(S1, S2, Poz) — вставка строки S1 в строку S2, начиная с позиции Poz. Пример: Исходное значение S2 Оператор 'ЭВМ РС' Insert('IBM-', S2, 5) 'Рис. 2' Insert('N', S2, 6) Конечное значение S2 'ЭВМ IBM-PC' 'Рис. N 2' Контрольные вопросы и задания Как можно объявить величину строкового типа? К каким типам данных относятся строки? Какова максимально возможная длина строки? С величиной какого типа данных совместим по присваиванию отдельный символ строки? 5. Расскажите об операциях, которые можно выполнять над строковыми величинами. 6. Расскажите о функциях, определенных для величин строкового типа. 7. Расскажите о процедурах, определенных для величин строкового типа. 8. Как осуществляется доступ к отдельному символу строки? 9. Почему значение отношения 'Мама'<>'мама' равно TRUE? 10. Дан фрагмент программы: 11. S:= ''; 12. For I:= '0' to '9' do S:=S+I; 13. Какое значение получит переменная S после его исполнения? 14. Дан фрагмент программы. Type String40 = String[40]; M = Array[1..100] Of String40; Var A : M; I, J, Min, N : Byte; Vsp : String40; Begin Write('N? '); ReadLn(N); For I := 1 To N Do ReadLn(A[I]); For I := 1 To N-1 Do Begin Min := I; For J := I + 1 To N Do If A[J] < A[Min] Then Min := J; Vsp := A[I]; A[I] := A[Min]; A[Min] := Vsp; End; WriteLn('Ответ: '); For I := 1 To N Do WriteLn(A[I]) End. 1. 2. 3. 4. 61 Точно и однозначно сформулировать условие задачи, решение которой приведено в данном фрагменте. Лекция №9. Множества в языке Pascal Множество — это структурированный тип данных, представляющий собой набор взаимосвязанных по какому-либо признаку или группе признаков объектов, которые можно рассматривать как единое целое. Каждый объект в множестве называется элементом множества. Все элементы множества должны принадлежать одному из порядковых типов, содержащему не более 256 значений. Этот тип называется базовым типом множества. Базовый тип задается диапазоном или перечислением. Область значений типа множество — набор всевозможных подмножеств, составленных из элементов базового типа. В выражениях на языке Паскаль значения элементов множества указываются в квадратных скобках: [1,2,3,4], ['а',‘b','с'], ['a'..'z']. Если множество не имеет элементов, оно называется пустым и обозначается как []. Количество элементов множества называется его мощностью. Множество может принимать все значения базового типа. Базовый тип не должен превышать 256 возможных значений. Поэтому базовым типом множества могут быть byte, char, boolean и производные от них типы. Множество в памяти хранится как массив битов, в котором каждый бит указывает является ли элемент принадлежащим объявленному множеству или нет. Максимальное число элементов множества 256, а данные типа множество могут занимать не более 32 байт. Число байтов, выделяемых для данных типа множество, вычисляется по формуле: ByteSize = (max div 8) - (min div 8) + 1, где max и min — верхняя и нижняя границы базового типа данного множества. Номер байта для конкретного элемента Е вычисляется по формуле: ByteNumber = (E div 8) - (min div 8), номер бита внутри этого байта по формуле: BitNumber = E mod 8 Не имеет значения порядок записи элементов множества внутри конструктора. Например, [1, 2, 3] и [3, 2, 1] — это эквивалентные множества. Каждый элемент в множестве учитывается только один раз. Поэтому множество [1, 2, 3, 4, 2, 3, 4, 5] эквивалентно [1..5]. Переменные множественного типа описываются так: Var <идентификатор> : set of <базовый тип>; Например: Var A, D : Set Of Byte; B : Set Of 'a'..'z'; C : Set Of Boolean; Нельзя вводить значения во множественную переменную процедурой ввода и выводить процедурой вывода. Множественная переменная может получить конкретное значение только в результате выполнения оператора присваивания: <множественная переменная> := <множественное выражение>; Например: A : = [50, 100, 150, 200]; B : = ['m', 'n', 'k']; C : = [True, False]; 62 D : = A; Кроме того, выражения могут включать в себя операции над множествами. Операции над множествами Объединением двух множеств A и B называется множество, состоящее из элементов, входящих хотя бы в одно из множеств A или B. Знак операции объединения в Паскале «+». Примеры: 1) [1, 2, 3, 4] + [3, 4, 5, 6] => [1, 2, 3, 4, 5, 6] 2) []+[‘a’..’z’]+[‘A’..’E’, ‘k’] => [‘A’..’E’, ‘a’..’z’] 3) [5<4, true and false] + [true] => [false, true] Пересечением двух множеств A и B называется множество, состоящее из элементов, одновременно входящих во множество A и во множество B. Знак операции пересечения в Паскале «*» Примеры: 1) [1, 2, 3, 4] * [3, 4, 5, 6] => [3, 4] 2) [‘a’..’z’]*[‘A’..’E’, ‘k’] => [‘k’] 3) [5<4, true and false] * [true] => [] Разностью двух множеств A и B называется множество, состоящее из элементов множества A, не входящих во множество B. 63 Примеры: 1a) [1, 2, 3, 4] - [3, 4, 5, 6] => [1, 2] 1b) [3, 4, 5, 6] - [1, 2, 3, 4] => [5, 6] 2a) [‘a’..’z’]-[‘A’..’E’, ‘k’] => [‘a’..’j’, ‘i’..’z’] 2b) [‘A’..’E’, ‘k’] - [‘a’..’z’] => [‘A’..’E’] 3a) [5<4, true and false] - [true] => [false] 3b) [true] - [5<4, true and false] => [true] Операция вхождения. Это операция, устанавливающая связь между множеством и скалярной величиной, тип которой совпадает с базовым типом множества. Если x — такая скалярная величина, а M — множество, то операция вхождения записывается так: x in M. Результат — логическая величина true, если значение x входит в множество M, и false — в противном случае. Например, 4 in [3, 4, 7, 9] –– true, 5 in [3, 4, 7, 9] –– false. Используя данную операцию, можно не только работать с элементами множества, но и, даже если в решении задачи явно не используются множества, некоторые логические выражения можно записать более лаконично. 1) Натуральное число n является двухзначным. Вместо выражения (n >= 10) and (n <=99) можно записать n in [10..99]. 2) Символ c является русской буквой. Вместо выражения (c >= ‘А’) and (c <= ‘Я’) or (c>=‘а’) and (c<=‘п’) or (c>=‘р’) and (c<=‘я’) пишем c in [‘А’.. ‘Я’, ‘а’.. ‘п’, ‘р’.. ‘я’] и т.д. Добавить новый элемент в множество можно с использованием операции объединения. Например, a:= a+[5] Для этих же целей в Turbo Pascal 7.0 предназначена процедура Include: include (M, A) M – множество, A – переменная того же типа, что и элементы множества M. Тот же пример можно записать так: Include (a, 5) Исключить элемент из множества можно с помощью операции «разность множеств». Например, a:= a-[5] Для этих же целей в Turbo Pascal 7.0 предназначена процедура Exclude: exclude (M, A) M – множество, A – переменная того же типа, что и элементы множества M. Тот же пример можно записать так: Exclude (a, 5) Рассмотрим несколько примеров использования множеств при решении задач. Задача 1. В городе имеется n высших учебных заведений, которые производят закупку компьютерной техники. Есть шесть компьютерных фирм: «Диалог», «Avicom», «Нэта», «Сервер», «Декада», «Dega.ru». Ответить на следующие вопросы: 1) в каких фирмах закупка производилась каждым из вузов? 2) в каких фирмах закупка производилась хотя бы одним из вузов? 3) в каких фирмах ни один из вузов не закупал компьютеры? Решим задачу с использованием множеств. Для удобства дальнейших манипуляций в порядке следования занумеруем компьютерные фирмы, начиная с единицы. Занесём информации о месте закупок компьютеров каждым из вузов в отдельное множество. Ответ на первый вопрос можно получить, выполнив пересечение всех таких множеств. Ответ на второй вопрос – результат объединения множеств. И, наконец, на последний – разность множества всех фирм и множества фирм, где хотя бы один вуз делал покупки. program ex_set_1; type firma = set of 1..6; 64 v = array[0..20] of firma; const f: array [1..6] of string[10] = ('Диалог', 'Avicom', 'Нэта', 'Сервер', 'Декада', 'Dega.ru'); {процедура ввода информации о закупке компьютеров в очередной фирме} procedure vvod(var a: firma); var i: byte; ans: 0..1; begin a:= []; for i := 1 to 6 do begin Write('Вуз покупал компьютеры в фирме ', f[i], ' (1 - да, 0 - нет)? '); ReadLn(ans); if ans = 1 then a:=a+[i] end; end; {процедура вывода элементов массива, номера которых содержатся в множестве} procedure Print(a : firma); var i: byte; begin for i := 1 to 6 do if i in a then write(f[i]:10); writeln end; {Процедура, дающая ответ на первый вопрос} procedure Rez1(a: v; n : byte; var b : firma); var i : byte; begin b := [1..6]; for i := 0 to n-1 do b := b * a[i]; end; {Процедура, дающая ответ на второй вопрос} procedure Rez2(a: v; n : byte; var b : firma); var i : byte; begin b := []; for i := 0 to n-1 do b := b + a[i]; end; var a: v; n, i : byte; c : firma; begin write('Сколько вузов делали закупку? '); readln(n); for i := 0 to n-1 do vvod(a[i]); Rez1(a, n, c); writeln('Каждый из вузов закупил компьютеры в фирмах: '); Print(c); Rez2(a, n, c); writeln('Хотя бы один из вузов закупил компьютеры в фирмах: '); Print(c); writeln('Ни один из вузов не закупил компьютеры в фирмах: '); Print([1..6]-c); end. 65 Задача 2. Сгенерировать n множеств (нумерацию начать с 1). Вывести элементы, которые входят во все множества с номерами, кратными трём, но не входят в первое множество. program ex_set_2; type mn = set of byte; v = array[1..30] of mn; {процедура ввода информации в очередное множество} procedure vvod(var a: mn); var i, n, vsp: byte; begin a:= []; n := 1 +random(200); for i := 1 to n do begin vsp:= random(256); a:=a+[vsp] end; end; {процедура вывода элементов множества} procedure Print(a : mn); var i: byte; begin for i := 0 to 255 do if i in a then write(i:4); writeln end; {Процедура, дающая ответ на вопрос} procedure Rez(a: v; n : byte; var b : mn); var i : byte; begin b := [0..255]; i:= 3; while i <= n do begin b := b * a[i]; i := i + 3 end; b := b - a[1] end; var a: v; n, i : byte; c : mn; begin randomize; write('Сколько множеств? '); for i := 1 to n do begin vvod(a[i]); print (a[i]) end; Rez(a, n, c); Print(c); end. readln(n); 66 Задача 3. Дана строка. Сохранить в ней только первые вхождения символов, удалив все остальные. program ex_set_3; var m : set of char; s : string; i : byte; begin write('Введите строку: '); readln(s); m :=[]; i := 1; while i <= length(s) do if s[i] in m then delete(s, i, 1) else begin m:=m+[s[i]]; i := i + 1 end; writeln(s) end. Контрольные вопросы и задания 1. Что такое множество? 2. Почему множество является структурированным типом данных? 3. Как хранится множество в памяти ЭВМ? Какой максимальный объем оперативной памяти может быть отведен под хранение одного множества? 4. Какие операции можно выполнять над множествами? 5. Как добавить элемент в множество? 6. Как исключить элемент из множества? 7. Как вывести элементы множества? Как подсчистать количество элементов в множестве? 8. Как может быть использована операция вхождения? Лекция №10. Записи в языке Pascal Запись — структурированный тип данных. Записи являются неоднородными неупорядоченными структурами с прямым доступом к компонентам. Компоненты записи называют полями записи. Обычно запись содержит совокупность разнотипных атрибутов, относящихся к одному объекту. Например, информация о рекордах по бегу у мужчин для открытых стадионов может содержать следующие атрибуты рекордсмен страна статус соревнований (Олимпиада, чемпионат мира) длина дистанции год рекорда время Структурная диаграмма для записи Запись может быть объявлена в разделе type: идентификатор типа = Record 67 поле1: тип; поле2: тип; … полеN: тип end; Например, type beg = record fam: string[25]; srtana: string[20]; status: boolean; {Олимпиада - true; чемпионат мира - false} dlina: longint; god: 1900..2009; vr: string[15]; end; Var a: beg; К каждому элементу записи можно обратиться, используя составное имя, которое имеет следующую структуру: <имя переменной>.<имя поля> Например, a.fam; a.dlina и т.п. Если, например, требуется полю "status" присвоить значение «true», то это делается так: a.status := true; Поля записи могут иметь любой тип, кроме файла, в частности, сами могут быть записями. Например, type beg1 = record fam: string[25]; srtana: string[20]; status: boolean; {Олимпиада - true; чемпионат мира - false} dlina: longint; god: 1900..2009; vr: record min: longint; sec: real end end; Var a: beg1; Поля такой записи, находящиеся на третьем уровне, идентифицируются тройным составным именем. Например, a.vr.min a.vr.sec В программе могут использоваться массивы записей. 68 Любая обработка записей, в том числе ввод и вывод, производится поэлементно. Например, var b: array[1..200] of beg1; ... write('Число рекордсменов? '); readln(n); for i:=1 to n do with b[i] do begin write('Фамилия спортсмена? '); readln(fam); write('Гражданин какой страны спортсмен? '); readln(strana); write('Статус соревнования? '); readln(status); write('Длина дистанции? '); readln(dlina); write('Год проведения соревнования? '); readln(god); write('Время, за которое спортсмен пробежал дистанцию (мин, сек)? '); readln(vr.min, vr.sec); end; ... В примере был использован оператор присоединения, который имеет следующий вид: with <переменная типа запись> do <оператор>; Он позволяет, один раз указав имя переменной типа "запись" после слова with, работать в пределах одного оператора (простого или составного) с именами полей как с обычными переменными, т.е. не писать громоздких составных имен. Объём памяти, занимаемый записью, определяется суммарным объёмом, занимаемым её полями: type beg1 = record fam: string[25]; {26 байт} srtana: string[20]; {21 байт} status: boolean; {1 байт} dlina: longint; {4 байта} god: 1900..2009; {2 байта} vr: record min: longint; {4 байта} sec: real {6 байт} end end; Всего получаем: 26+21+1+4+2+4+6=65 байт Массив, описанный выше, занимает 65 байт × 200 элементов = 13000 байт оперативной памяти. Записи с вариантами При определении типа записи в нее можно включать вариантную часть. Это означает, что разные переменные, хотя они относятся к одному типу, могут иметь различные структуры. Вариантная часть записи начинается выбором case и следует за общей частью; после ее окончания в записи не могут появляться никакие другие поля, поэтому case закрывается служебным словом end. 69 Любая запись может иметь только одну вариантную часть, которая должна размещаться в конце записи (после фиксированной части). Однако, внутри какого-либо варианта, в свою очередь, может присутствовать другая вариантная часть, вложенная в первую. При записи варианта (списков элементов) обязательно наличие круглых скобок, даже если в них ничего не заключается. Структурная диаграмма для записи с вариантами Например, пусть необходимо задать информацию о некотором человеке, указав фамилию и год рождения, а также, если это мужчина, то сообщить, военнообязанный ли он и какую имеет специальность, а если это женщина, то указать, замужем ли она и сколько имеет детей. Type pol=(m,w); people=record fam:string[20]; godro:1900..2007; case mw:pol of m: ( voen: boolean; spec: string[15]); w: ( merry: boolean; child: byte) end; var p1, p2: people; Все имена элементов должны быть различными, даже если они встречаются в разных вариантах. К элементам вариантной части можно обращаться так же, как к элементам фиксированной части записи. P1.mw:=m; p1.voen:=true; p2.child:=2; В процессе выполнения программы в структуру записи включается тот вариант, элементам которого в данный момент было присвоено значение. Как только какому-либо элементу другого варианта присваивается некоторое значение, в структуру записи включается этот вариант, а элементы предыдущего варианта становятся неопределенными. Данное обстоятельство учитывается и при подсчете объема памяти, отводимого под запись с вариантами. Статические поля учитываются обычным образом, а среди вариативной части отыскивается тот случай, где суммарный объем памяти для полей максимальный. Type pol=(m,w); people=record fam:string[20]; {21} godro:1900..2007; {2} case mw:pol of {1} m: ( voen: boolean; spec: string[15]); {1+16=17} w: ( merry: boolean; child: byte) {1+1=2} end; 21 + 2 + 1 + max(17, 2) = 41 (байт) 70 Рассмотрим пример работы с записями без вариантов. Задача. Сведения о деталях, хранящихся на складе, содержат следующие атрибуты: название, количество, стоимость одной детали. Вывести информацию о детали, суммарная стоимость для которой максимальна. program ex_zap; type detal = record a: string[30]; kol, st: integer; end; var a: array [0..99] of detal; n, i, max: integer; begin write('Количество деталей? '); readln(n); for i:=0 to n-1 do With a[i] do begin write('Информация об ', i, ' детали: '); readln(a); readln(kol); readln(st); end; max := 0; for i:= 1 to n-1 do if a[max].kol*a[max].st < a[i].kol*a[i].st then max:=i; writeln('Искомая деталь: ', a[max].a, ' стоимостью ', a[max].st, ' в количестве ', a[max].kol); end. Контрольные вопросы и задания Чем отличается тип "запись" от других структурированных типов? Могут ли поля записи быть одного и того же типа? Как обратиться к отдельному полю записи? Что такое "оператор присоединения"? В каких целях он используется? Что такое "запись с вариантами"? Как определить объем памяти под статическую запись? запись с вариантами? 7. Как заполнить массив записей? 1. 2. 3. 4. 5. 6. Лекция №11. Создание библиотек подпрограмм в Turbo Pascal Стандартный язык Pascal не располагает средствами разработки и поддержки библиотек программиста (в отличие, скажем, от языка Fortran и других языков программирования высокого уровня), которые компилируются отдельно и в дальнейшем могут быть использованы как самим разработчиком, так и другими. Если программист имеет достаточно большие наработки, и те или иные подпрограммы могут быть использованы при написании новых приложений, то приходится эти подпрограммы целиком включать в новый текст. В Turbo Pascal это ограничение преодолевается за счет, во-первых, введения внешних процедур, во-вторых, разработки и использования модулей. В настоящей публикации на примерах рассмотрим работу с теми и другими программными единицами. Начнем с внешних подпрограмм. 71 Такой механизм предусматривает, что исходный текст каждой процедуры или функции хранится в отдельном файле и при необходимости с помощью специальной директивы компилятора включается в текст создаваемой программы. Покажем это на примере задач целочисленной арифметики, где аргументы, результаты и промежуточные величины являются целыми (Integer, Word, LongInt и т.д.). Вот несколько таких задач. 1. Дано натуральное число n. Найти сумму первой и последней цифры этого числа. 2. Дано натуральное число n. Переставить местами первую и последнюю цифры этого числа. 3. Дано натуральное число n. Дописать к нему цифру k в конец и в начало (если это возможно, т.е. результат не выйдет за диапазон допустимых значений), или сообщить о невозможности выполнения операции. 4. Найти наибольшую цифру в записи данного натурального числа. 5. Дано натуральное число n. Переставить его цифры так, чтобы образовалось максимальное число, записанное теми же цифрами. При решении каждой из этих задач может быть использована функция, возвращающая количество цифр в записи натурального числа. Вот возможный вариант такой функции: Function Digits(N : LongInt) : Byte; Var Kol : Byte; Begin Kol := 0; While N <> 0 Do Begin Kol := Kol + 1; N := N Div 10 End; Digits := Kol End; Сохраним этот текст в файле с расширением .inc (это расширение внешних подпрограмм в Turbo Pascal), например, digits.inc. Еще необходима функция возведения натурального числа в натуральную степень. Function Power(A, N : LongInt) : LongInt; {файл power.inc} Var I, St : LongInt; Begin St := 1; For I := 1 To N Do St := St * A; Power := St End; Попробуем использовать функции при решении задачи номер один. Program Example1; Var N, S : LongInt; {$I digits.inc} {подключаем внешнюю функцию digits.inc, возвращающую количество цифр в записи числа} {$I power.inc} {внешняя функция, выполняющая возведение числа A в степень N} Begin 72 Write('Введите натуральное число: '); ReadLn(N); {для определения последней цифры числа N берем остаток от деления этого числа на 10, а для определения первой делим N на 10 в степени на единицу меньшую, чем количество цифр в записи числа (нумерация разрядов начинается с 0)} S := N Mod 10 + N Div Power(10, Digits(N) - 1); WriteLn('Искомая сумма: ', S) End. Внешние процедуры создаются и внедряются в использующие их программы аналогично функциям, и мы не будем подробно на этом останавливаться. Далее речь пойдет о модулях: их структуре, разработке, компиляции и использовании. Модуль — это набор ресурсов (функций, процедур, констант, переменных, типов и т.д.), разрабатываемых и хранимых независимо от использующих их программ. В отличие от внешних подпрограмм модуль может содержать достаточно большой набор процедур и функций, а также других ресурсов для разработки программ. Обычно каждый модуль содержит логически связанные между собой программные ресурсы. В основе идеи модульности лежат принципы структурного программирования. Существуют стандартные модули Turbo Pascal, которые обычно описываются в литературе по данному языку. Модуль имеет следующую структуру: Unit <имя модуля>; {заголовок модуля} Interface {интерфейсная часть} Implementation {раздел реализации} Begin {раздел инициализации модуля} End. После служебного слова Unit записывается имя модуля, которое (для удобства дальнейших действий) должно совпадать с именем файла, содержащего данный модуль. Поэтому (как принято в MS DOS) имя не должно содержать более 8 символов. В разделе Interface объявляются все ресурсы, которые будут в дальнейшем доступны программисту при подключении модуля. Для подпрограмм здесь указывается лишь полный заголовок. В разделе Implementation реализуются все подпрограммы, которые были ранее объявлены. Кроме того, здесь могут содержаться свои константы, переменные, типы, подпрограммы и т.д., которые носят вспомогательный характер и используются для написания основных подпрограмм. В отличие от ресурсов, объявленных в разделе Interface, все, что дополнительно объявляется в Implementation, уже не будет доступно при подключении модуля. При написании основных подпрограмм достаточно указать их имя (т.е. не нужно полностью переписывать весь заголовок), а затем записать тело подпрограммы. Наконец, раздел инициализации (который часто отсутствует) содержит операторы, которые должны быть выполнены сразу же после запуска программы, использующей модуль. Приведем пример разработки и использования модуля. Поскольку рассмотренная ниже задача достаточно элементарна, ограничимся листингом программы с подробными комментариями. 73 Задача. Реализовать в виде модуля набор подпрограмм для выполнения следующих операций над обыкновенными дробями вида P/Q (P — целое, Q — натуральное): 1) сложение; 2) вычитание; 3) умножение; 4) деление; 5) сокращение дроби; 6) возведение дроби в степень N (N — натуральное); 7) функции, реализующие операции отношения (равно, не равно, больше или равно, меньше или равно, больше, меньше). Дробь представить следующим типом: Type Frac = Record P : Integer; Q : 1.. High(LongInt) End; Используя этот модуль, решить задачи: 1. Дан массив A — массив обыкновенных дробей. Найти сумму всех дробей, ответ представить в виде несократимой дроби. Вычислить среднее арифметическое всех дробей, ответ представить в виде несократимой дроби. 2. Дан массив A — массив обыкновенных дробей. Отсортировать его в порядке возрастания. Unit Droby; Interface Type Natur = 1..High(LongInt); Frac = Record P : LongInt; {Числитель дроби} Q : Natur {Знаменатель дроби} End; Procedure Sokr(Var A : Frac); Procedure Summa(A, B : Frac; Var C : Frac); Procedure Raznost(A, B : Frac; Var C : Frac); Procedure Proizvedenie(A, B : Frac; Var C : Frac); Procedure Chastnoe(A, B : Frac; Var C : Frac); Procedure Stepen(A : Frac; N : Natur; Var C : Frac); Function Menshe(A, B : Frac) : Boolean; Function Bolshe(A, B : Frac) : Boolean; Function Ravno(A, B : Frac) : Boolean; Function MensheRavno(A, B : Frac) : Boolean; Function BolsheRavno(A, B : Frac) : Boolean; Function NeRavno(A, B : Frac) : Boolean; {Раздел реализации модуля} Implementation {Наибольший общий делитель двух чисел - вспомогательная функция, ранее не объявленная} Function NodEvklid(A, B : Natur) : Natur; Begin While A <> B Do If A > B Then If A Mod B <> 0 Then A := A Mod B Else A := B Else If B Mod A <> 0 Then B := B Mod A Else B := A; NodEvklid := A 74 End; Procedure Sokr; {Сокращение дроби} Var M, N : Natur; Begin If A.P <> 0 Then Begin If A.P < 0 Then M := Abs(A.P) Else M := A.P; {Совмещение типов, т.к. A.P - LongInt} N := NodEvklid(M, A.Q); A.P := A.P Div N; A.Q := A.Q Div N End End; Procedure Summa; {Сумма дробей} Begin {Знаменатель дроби} C.Q := (A.Q * B.Q) Div NodEvklid(A.Q, B.Q); {Числитель дроби} C.P := A.P * C.Q Div A.Q + B.P * C.Q Div B.Q; Sokr(C) End; Procedure Raznost; {Разность дробей} Begin {Знаменатель дроби} C.Q := (A.Q * B.Q) Div NodEvklid(A.Q, B.Q); {Числитель дроби} C.P := A.P * C.Q Div A.Q - B.P * C.Q Div B.Q; Sokr(C) End; Procedure Proizvedenie; Begin {Знаменатель дроби} C.Q := A.Q * B.Q; {Числитель дроби} C.P := A.P * B.P; Sokr(C) End; Procedure Chastnoe; Begin {Знаменатель дроби} C.Q := A.Q * B.P; {Числитель дроби} C.P := A.P * B.Q; Sokr(C) End; Procedure Stepen; {Степень} Var I : Natur; Begin C.Q := 1; C.P := 1; Sokr(A); For I := 1 To N Do Proizvedenie(A, C, C) End; Function Menshe; Begin Menshe := A.P * B.Q < A.Q * B.P End; Function Bolshe; Begin Bolshe := A.P * B.Q > A.Q * B.P End; Function Ravno; Begin Ravno := A.P * B.Q = A.Q * B.P End; Function BolsheRavno; Begin BolsheRavno := Bolshe(A, B) Or Ravno(A, B) End; Function MensheRavno; Begin MensheRavno := Menshe(A, B) Or Ravno(A, B) End; Function NeRavno; 75 Begin NeRavno := Not Ravno(A, B) End; {Раздел инициализации модуля} Begin End. Дадим некоторые рекомендации по разработке модулей: 1) спроектировать модуль, подпрограммы, другие ресурсы; т.е. выделить основные и вспомогательные 2) каждую подпрограмму целесообразно отладить отдельно, после чего «вклеить» в текст модуля. Сохраним текст разработанной программы в файле DROBY.PAS и откомпилируем наш модуль. Для этого можно воспользоваться внешним компилятором, поставляемым вместе с Turbo Pascal. Команда будет выглядеть так: TPC DROBY.PAS. Если в тексте нет синтаксических ошибок, получим файл DROBY.TPU, иначе будет соответствующее сообщение с указанием строки, содержащей ошибку. Другой способ компиляции модуля — в среде программирования Turbo Pascal выбрать в пункте меню Run подпункты Make или Build (при этом должна быть включена компиляция на диск). Теперь можно подключить модуль к программе, где планируется его использование. Для примера решим задачу суммирования массива дробей. Program Sum; Uses Droby; Var A : Array[1..100] Of Frac; I, N : Integer; S : Frac; Begin Write('Введите количество элементов массива: '); ReadLn(N); S.P := 0; S.Q := 1; {Первоначально сумма равна нулю} For I := 1 To N Do {Вводим и суммируем дроби} Begin Write('Введите числитель ', I, '-й дроби: '); ReadLn(A[I].P); Write('Введите знаменатель ', I, '-й дроби: '); ReadLn(A[I].Q); Summa(A[I], S, S); End; WriteLn('Ответ: ', S.P, '/', S.Q) End. Вторую задачу предлагаем решить читателю самостоятельно. Как видно из примера, для подключения модуля используется служебное слово USES, после чего указывается имя модуля и происходит это сразу же после заголовка программы. Если необходимо подключить несколько модулей, они перечисляются через запятую. При использовании ресурсов модуля совсем не нужно знать, как работают его подпрограммы. Достаточно обладать информацией, как выглядят их заголовки и какое действие эти подпрограммы выполняют. По такому принципу осуществляется работа со всеми стандартными модулями. Поэтому, если программист разрабатывает модули не только для личного пользования, ему необходимо сделать полное описание всех 76 доступных при подключении ресурсов. В таком случае возможна полноценная работа с таким продуктом. Ещё несколько слов о видимости объектов модуля. Если в программе, использующей модуль, имеются идентификаторы, совпадающие с точностью до символа с идентификаторами модуля, то они «перекрывают» соответствующие ресурсы модуля. Тем не менее, даже в такой ситуации доступ к этим ресурсам модуля может быть получен таким образом: <имя модуля>.<имя ресурса>. Лекция №12. Динамические структуры данных: списки Введение В предыдущих обзорах мы рассматривали программирование, связанное с обработкой только статических данных. Статическими величинами называются такие, память под которые выделяется во время компиляции и сохраняется в течение всей работы программы. В языках программирования (Pascal, C, др.) существует и другой способ выделения памяти под данные, который называется динамическим. В этом случае память под величины отводится во время выполнения программы. Такие величины будем называть динамическими. Раздел оперативной памяти, распределяемый статически, называется статической памятью; динамически распределяемый раздел памяти называется динамической памятью (динамически распределяемой памятью). Использование динамических величин предоставляет программисту ряд дополнительных возможностей. Во-первых, подключение динамической памяти позволяет увеличить объем обрабатываемых данных. Во-вторых, если потребность в каких-то данных отпала до окончания программы, то занятую ими память можно освободить для другой информации. В-третьих, использование динамической памяти позволяет создавать структуры данных переменного размера. Работа с динамическими величинами связана с использованием еще одного типа данных — ссылочного типа. Величины, имеющие ссылочный тип, называют указателями. Указатель содержит адрес поля в динамической памяти, хранящего величину определенного типа. Сам указатель располагается в статической памяти. Адрес величины — это номер первого байта поля памяти, в котором располагается величина. Размер поля однозначно определяется типом. Далее будем более подробно обсуждать указатели и действия с ними в языке Pascal, примеры будем приводить на Pascal и C. Величина ссылочного типа (указатель) описывается в разделе описания переменных следующим образом: Var <идентификатор> : ^<имя типа>; Вот примеры описания указателей: Type Mas1 = Array[1..100] Of Integer; Var P1 : ^Integer; P2 : ^String; Pm : ^Mas1; Здесь P1 — указатель на динамическую величину целого типа; P2 — указатель на динамическую величину строкового типа; Pm — указатель на динамический массив, тип которого задан в разделе Type. Сами динамические величины не требуют описания в программе, поскольку во время компиляции память под них не выделяется. Во время компиляции память выделяется только под статические величины. Указатели — это статические величины, 77 поэтому они требуют описания. Каким же образом происходит выделение памяти под динамическую величину? Память под динамическую величину, связанную с указателем, выделяется в результате выполнения стандартной процедуры NEW. Формат обращения к этой процедуре: NEW(<указатель>); Считается, что после выполнения этого оператора создана динамическая величина, имя которой имеет следующий вид: <имя динамической величины> := <указатель>^ Пусть в программе, в которой имеется приведенное выше описание, присутствуют следующие операторы: NEW(P1); NEW(P2); NEW(Pm); После их выполнения в динамической памяти оказывается выделенным место под три величины (две скалярные и один массив), которые имеют идентификаторы: P1^, P2^, Pm^ Например, обозначение P1^ можно расшифровать так: динамическая переменная, на которую ссылается указатель P1. Дальнейшая работа с динамическими переменными происходит точно так же, как со статическими переменными соответствующих типов. Им можно присваивать значения, их можно использовать в качестве операндов в выражениях, параметров подпрограмм и пр. Например, если переменной P1^ нужно присвоить число 25, переменной P2^ присвоить значение символа "Write", а массив Pm^ заполнить по порядку целыми числами от 1 до 100, то это делается так: P1^ := 25; P2^ := 'Write'; For I := 1 To 100 Do Pm^[I] := I; Кроме процедуры NEW значение указателя может определяться оператором присваивания: <указатель> := <ссылочное выражение>; В качестве ссылочного выражения можно использовать указатель; ссылочную функцию (т.е. функцию, значением которой является указатель); константу Nil. Nil — это зарезервированная константа, обозначающая пустую ссылку, т.е. ссылку, которая ни на что не указывает. При присваивании базовые типы указателя и ссылочного выражения должны быть одинаковы. Константу Nil можно присваивать указателю с любым базовым типом. До присваивания значения ссылочной переменной (с помощью оператора присваивания или процедуры NEW) она является неопределенной. Ввод и вывод указателей не допускается. Рассмотрим пример. Пусть в программе описаны следующие указатели: Var D, P : ^Integer; K : ^Boolean; 78 Тогда допустимыми являются операторы присваивания D := P; K := Nil; поскольку соблюдается принцип соответствия типов. Оператор K := D ошибочен, т.к. базовые типы у правой и левой части разные. Если динамическая величина теряет свой указатель, то она становится "мусором". В программировании под этим словом понимают информацию, которая занимает память, но уже не нужна. Представьте себе, что в программе, в которой присутствуют описанные выше указатели, в разделе операторов записано следующее: NEW(D); NEW(P); {Выделено место в динамической памяти под две целые переменные. Указатели получили соответствующие значения} D^ := 3; P^ := 5; {Динамическим переменным присвоены значения} P := D; {Указатели P и D стали ссылаться на одну и ту же величину, равную 3} WriteLn(P^, D^); {Дважды напечатается число 3} Таким образом, динамическая величина, равная 5, потеряла свой указатель и стала недоступной. Однако место в памяти она занимает. Это и есть пример возникновения "мусора". На схеме показано, что произошло в результате выполнения оператора P := D. В Паскале имеется стандартная процедура, позволяющая освобождать память от данных, потребность в которых отпала. Ее формат: DISPOSE(<указатель>); Например, если динамическая переменная P^ больше не нужна, то оператор DISPOSE(P) удалит ее из памяти. После этого значение указателя P становится неопределенным. Особенно существенным становится эффект экономии памяти при удалении больших массивов. В версиях Турбо-Паскаля, работающих под операционной системой MS DOS, под данные одной программы выделяется 64 килобайта памяти (или, если быть точнее, 65520 байт). Это и есть статическая область памяти. При необходимости работать с большими массивами информации этого может оказаться мало. Размер динамической памяти — много больше (сотни килобайт). Поэтому использование динамической памяти позволяет существенно увеличить объем обрабатываемой информации. Следует отчетливо понимать, что работа с динамическими данными замедляет выполнение программы, поскольку доступ к величине происходит в два шага: сначала ищется указатель, затем по нему — величина. Как это часто бывает, действует "закон сохранения неприятностей": выигрыш в памяти компенсируется проигрышем во времени. Пример. Дан текстовый файл размером не более 64 Кб, содержащий действительные числа, по одному в каждой строке. Переписать содержимое файла в массив, разместив его в динамически распределяемой памяти. Вычислить среднее значение элементов массива. Очистить динамическую память. Создать целый массив размером 10000, заполнить его случайными целыми числами в диапазоне от –100 до 100 и вычислить его среднее значение. {Язык Turbo Pascal} Program Srednee; Const NMax = 10000; Type Diapazon = 1..NMax; MasInt = Array[Diapazon] Of Integer; MasReal = Array[Diapazon] Of Real; 79 Var PIint : ^MasInt; PReal : ^MasReal; I, Midint : longInt; MidReal : Real; T : Text; S : string; Begin Write('Введите имя файла: '); ReadLn(S); Assign(T, S); Reset(T); MidReal := 0; MidInt := 0; Randomize; NEW(PReal); {Выделение памяти под вещественный массив} {Ввод и суммирование вещественного массива} While Not Eof (T) Do Begin ReadLn(T, PReal^[I]); MidReal := MidReal + PReal^[I] End; DISPOSE(PReal); {Удаление вещественного массива} NEW(PInt); {Выделение памяти под целый массив} {Вычисление и суммирование целого массива} For I := 1 To NMax Do Begin PInt^[I] := -100 + Random(201); MidInt := MidInt + PInt^[I] End; {Вывод средних значений} WriteLn('среднее целое равно: ', MidInt Div NMax); WriteLn('среднее вещественное равно: ', (MidReal / NMax) : 10 : 6) End. // Язык C++ #include < stdio.h > #include < time.h > #include < stdlib.h > #include < iostream.h > #define NMax 10000 typedef int MasInt; typedef float MasReal; MasInt *PInt; MasReal *PReal; int I, n, MidInt; float MidReal; char S[255]; FILE *t; char *endptr; void main() { cout << "Введите имя файла: "; cin >> S; t=fopen(S, "r"); MidReal = 0; MidInt = 0; randomize(); I=0; /*Выделение памяти под вещественный массив*/ PReal = (MasReal*) malloc (sizeof(MasReal)); /*Ввод и суммирование вещественного массива*/ while (!feof(t)) {fgets(S, 255, t); // вводим из файла строку PReal[I] = strtod(S, &endptr); // преобразуем введенную строку в вещественное число MidReal += PReal[I]; I++;} n=I+1; free (PReal); /*Удаление вещественного массива*/ PInt = (MasInt*) malloc(sizeof(MasInt)); /*Выделение памяти под целый массив*/ /* Вычисление и суммирование целого массива */ for (I=0; I < NMax; I++) { PInt[I] = -100 + random(201); 80 MidInt += PInt[I];} /*Вывод средних значений*/ cout << "\nсреднее целое равно " << MidInt / double(NMax) << "\n"; cout << "среднее вещественное равно: " << MidReal / n << "\n"; fclose(t); } Списки Обсудим вопрос о том, как в динамической памяти можно создать структуру данных переменного размера. Разберем следующий пример. В процессе физического эксперимента многократно снимаются показания прибора (допустим, термометра) и записываются в компьютерную память для дальнейшей обработки. Заранее неизвестно, сколько будет произведено измерений. Если для обработки таких данных не использовать внешнюю память (файлы), то разумно расположить их в динамической памяти. Во-первых, динамическая память позволяет хранить больший объем информации, чем статическая. А во-вторых, в динамической памяти эти числа можно организовать в связанный список, который не требует предварительного указания количества чисел, подобно массиву. Что же такое "связанный список"? Схематически он выглядит так: Здесь Inf — информационная часть звена списка (величина любого простого или структурированного типа, кроме файлового), Next — указатель на следующее звено списка; First — указатель на заглавное звено списка. Согласно определению, список располагается в динамически распределяемой памяти, в статической памяти хранится лишь указатель на заглавное звено. Структура, в отличие от массива, является действительно динамической: звенья создаются и удаляются по мере необходимости, в процессе выполнения программы. Для объявления списка сделано исключение: указатель на звено списка объявляется раньше, чем само звено. В общем виде объявление выглядит так. Type U = ^Zveno; Zveno = Record Inf : BT; Next: U End; Здесь BT — некоторый базовый тип элементов списка. Если указатель ссылается только на следующее звено списка (как показано на рисунке и в объявленной выше структуре), то такой список называют однонаправленным, если на следующее и предыдущее звенья — двунаправленным списком. Если указатель в последнем звене установлен не в Nil, а ссылается на заглавное звено списка, то такой список называется кольцевым. Кольцевыми могут быть и однонаправленные, и двунаправленные списки. Более подробно рассмотрим работу со связанными списками на примере однонаправленного некольцевого списка. Выделим типовые операции над списками: добавление звена в начало списка; удаление звена из начала списка; добавление звена в произвольное место списка, отличное от начала (например, после звена, указатель на которое задан); удаление звена из произвольного места списка, отличного от начала (например, после звена, указатель на которое задан); проверка, пуст ли список; очистка списка; печать списка. 81 Реализуем выделенный набор операций в виде модуля. Подключив этот модуль, можно решить большинство типовых задач на обработку списка. Пусть список объявлен так, как было описано выше. Первые четыре действия сначала реализуем отдельно, снабдив их иллюстрациями. 1. Добавление звена в начало списка {Процедура добавления звена в начало списка; в x содержится добавляемая информация} Procedure V_Nachalo(Var First : U; X : BT); Var Vsp : U; Begin New(Vsp); Vsp^.Inf := X; Vsp^.Next := First; {То звено, что было заглавным, становится вторым по счёту} First := Vsp; {Новое звено становится заглавным} End; 2. Удаление звена из начала списка {Процедура удаления звена из начала списка; в x содержится информация из удалённого звена} Procedure Iz_Nachala(Var First : U; Var X : BT); Var Vsp : U; Begin Vsp := First; {Забираем ссылку на текущее заглавное звено} First := First^.Next; {То звено, что было вторым по счёту, становится заглавным} X := Vsp^.Inf; {Забираем информацию из удаляемого звена} Dispose(Vsp); {Уничтожаем звено} End; 3. Добавление звена в произвольное место списка, отличное от начала (после звена, указатель на которое задан) {Процедура добавления звена в список после звена, на которое ссылается указатель Pred; в x содержится информация для добавления} Procedure V_Spisok(Pred : U; X : BT); Var Vsp : U; Begin New(Vsp); {Создаем пустое звено} Vsp^.Inf := X; {Заносим информацию} Vsp^.Next := Pred^.Next; {Теперь это звено ссылается на то, что было следом за звеном Pred} Pred^.Next := Vsp; {Теперь новое звено встало вслед за звеном Pred} End; 4. Удаление звена из произвольного места списка, отличного от начала (после звена, указатель на которое задан) {Процедура удаления звена из списка после звена, на которое ссылается указатель Pred; в x содержится информация из удалённого звена} Procedure Iz_Spiska(Pred : U; Var X : BT); 82 Var Vsp : U; Begin Vsp := Pred^.Next; {Забираем ссылку на удаляемое звено} {Удаляем звено из списка, перенаправив ссылку на следующее за ним звено} Pred^.Next := Pred^.Next^.Next; X := Vsp^.Inf; {Забираем информацию из удаляемого звена} Dispose(Vsp); {Уничтожаем звено} End; Приведём полный текст модуля.{Язык Pascal} Unit Spisok; Interface Type BT = LongInt; U = ^Zveno; Zveno = Record Inf : BT; Next: U End; Procedure V_Nachalo(Var First : U; X : BT); Procedure Iz_Nachala(Var First : U; Var X : BT); Procedure V_Spisok(Pred : U; X : BT); Procedure Iz_Spiska(Pred : U; Var X : BT); Procedure Ochistka(Var First: U); Function Pust(First : U) : Boolean; Procedure Print(First : U); Implementation Procedure V_Nachalo; Var Vsp : U; Begin New(Vsp); Vsp^.Inf := X; Vsp^.Next := First; First := Vsp; End; Procedure Iz_Nachala; Var Vsp : U; Begin Vsp := First; First := First^.Next; X := Vsp^.Inf; Dispose(Vsp); End; Procedure V_Spisok; Var Vsp : U; Begin New(Vsp); Vsp^.Inf := X; Vsp^.Next := Pred^.Next; 83 Pred^.Next := Vsp; End; Procedure Iz_Spiska; Var Vsp : U; Begin Vsp := Pred^.Next; Pred^.Next := Pred^.Next^.Next; X := Vsp^.Inf; Dispose(Vsp); End; Procedure Ochistka; Var Vsp : BT; Begin While Not Pust(First) Do Iz_Nachala(First, Vsp) End; Function Pust; Begin Pust := First = Nil End; Procedure Print; Var Vsp : U; Begin Vsp := First; While Vsp <> Nil Do Begin Write(Vsp^.Inf : 6); Vsp := Vsp^.Next End; WriteLn End; Begin End. // Язык С++ #include < iostream.h > #include < conio.h > #include < stdlib.h > #include < time.h > typedef long BT; struct Zveno{ BT Inf; Zveno *Next; }; Zveno *V_Nachalo(Zveno *First, BT X) { Zveno *Vsp; Vsp = (Zveno *) malloc(sizeof(Zveno)); Vsp->Inf=X; Vsp->Next=First; First=Vsp; return First; } 84 Zveno *Iz_Nachala(Zveno *First) { Zveno *Vsp; Vsp=First->Next; free(First); return Vsp; } Zveno *V_Spisok(Zveno *Pred, BT X) { Zveno *Vsp; Vsp = (Zveno *) malloc(sizeof(Zveno)); Vsp->Inf=X; Vsp->Next=Pred->Next; Pred->Next=Vsp; return Vsp; } BT Iz_Spiska(Zveno *Pred) { BT X; Zveno *Vsp; Vsp=Pred->Next; Pred->Next=Pred->Next->Next; X=Vsp->Inf; free(Vsp); return X; } void Print(Zveno *First) { Zveno *Vsp; Vsp=First; while (Vsp) {cout << Vsp->Inf << ' '; Vsp=Vsp->Next;} cout << "\n"; } int Pust(Zveno *First) { return !First; } Zveno *Ochistka(Zveno *First) { while (!Pust(First)) First=Iz_Nachala(First); return First; } Пример. Составить программу, которая на основе заданного списка формирует два других, помещая в первый из них положительные, а во второй — отрицательные элементы исходного списка. При реализации алгоритма будем использовать подпрограммы разработанного модуля. Это существенно облегчает решение задачи. 85 {Программа на Turbo Pascal} Program Ex_sp_1; Uses Spisok; Var S1, S2, S3, V1, V2, V3 : U; A : BT; I, N : Byte; Begin Randomize; N := 1 + Random(20); S1 := Nil; A := -100 + Random(201); V_Nachalo(S1, A); V1 := S1; For I := 2 To N Do Begin A := -100 + Random(201); V_Spisok(V1, A); V1 := V1^.Next End; WriteLn('Исходный список: '); Print(S1); V1 := s1; S2 := Nil; S3 := Nil; While V1 <> Nil Do Begin If V1^.Inf > 0 Then If S2 = Nil Then Begin V_Nachalo(S2, V1^.Inf); V2 := S2 End Else Begin V_Spisok(V2, V1^.Inf); V2 := V2^.Next End; If V1^.Inf < 0 Then If S3 = Nil Then Begin V_Nachalo(s3, V1^.Inf); V3 := S3 End Else Begin V_Spisok(V3, V1^.Inf); V3 := V3^.Next End; V1:= V1^.Next End; WriteLn('Результирующий список из положительных элементов: '); Print(S2); WriteLn('Результирующий список из отрицательных элементов: '); Print(S3); Ochistka(S1); Ochistka(S2); Ochistka(S3); End. // Программа на C++ #include "SPIS.CPP" void main() {Zveno *S1, *S2, *S3, *V1, *V2, *V3; BT a; int i, n; clrscr(); randomize(); S1=NULL; // создаём первый элемент a=-100+random(201); S1=V_Nachalo(S1, a); n=1+random(20); // формируем список произвольной длины и выводим на печать V1=S1; for (i=2; i<=n; i++) { a=-100+random(201); V1=V_Spisok(V1, a); } Print(S1); V1 = S1; S2 = NULL; S3 = NULL; while (V1) {if (V1->Inf > 0) 86 if (!S2) {S2=V_Nachalo(S2, V1->Inf); V2 = S2;} else {V_Spisok(V2, V1->Inf); V2 = V2->Next;}; if (V1->Inf < 0) if (!S3) {S3=V_Nachalo(S3, V1->Inf); V3 = S3;} else {V_Spisok(V3, V1->Inf); V3 = V3->Next;}; V1= V1->Next;} cout << "Результирующий список из положительных элементов: \n"; Print(S2); cout << "Результирующий список из отрицательных элементов: \n"; Print(S3); S1=Ochistka(S1); S2=Ochistka(S2); S3=Ochistka(S3); } Контрольные вопросы и задания Чем отличаются статические и динамические величины? Какая память называется динамически распределяемой? Что такое указатель? Какие виды указателей вам известны? Как определяется адрес переменной? Приведите примеры объявления указателей. Как выделить память под динамическую переменную? Как освободить память от динамической переменной? 8. Что такое "разыменование"? 9. Что в языке Pascal обозначает константа Nil (в языке C константа NULL)? 10. В каком случае возможно присваивание указателей? 11. Какие ситуации приводят к возникновению в динамически распределяемой памяти "мусора"? 12. Что понимают под "связанным списком"? 13. Как классифицируют связанные списки? 14. Какие основные действия над списками и компонентами списков обычно реализуют? 15. Как описывается список? 16. Двунаправленный список объявлен следующим образом: Type BT = Byte; U = ^Zveno; Zveno = Record Inf : BT; Pred, Next: U End; Здесь Pred, Next — соответственно указатели на предыдущее и последующее звенья списка. Разработать основные подпрограммы для обслуживания такого списка. 1. 2. 3. 4. 5. 6. 7. Лекция №13. Динамические структуры данных: стеки Стек — динамическая структура данных, представляющая из себя упорядоченный набор элементов, в которой добавление новых элементов и удаление существующих производится с одного конца, называемого вершиной стека. По определению, элементы извлекаются из стека в порядке, обратном их 87 добавлению в эту структуру, т.е. действует принцип "последний пришёл — первый ушёл". Наиболее наглядным примером организации стека служит детская пирамидка, где добавление и снятие колец осуществляется как раз согласно определению стека. Стек можно организовать на базе любой структуры данных, где возможно хранение нескольких однотипных элементов и где можно реализовать определение стека: линейный массив, типизированный файл, однонаправленный или двунаправленный список. В нашем случае наиболее подходящим для реализации стека является однонаправленный список, причём в качестве вершины стека выберем начало этого списка. Выделим типовые операции над стеком и его элементами: добавление элемента в стек; удаление элемента из стека; проверка, пуст ли стек; просмотр элемента в вершине стека без удаления; очистка стека. Реализуем эти операции, используя разработанный ранее модуль для однонаправленных списков (см. материал "Динамические структуры данных: списки"). { Turbo Pascal, файл STACK.PAS } Unit Stack; Interface Uses Spisok; Procedure V_Stack(Var Versh : U; X : BT); Procedure Iz_Stack(Var Versh : U; Var X : BT); Function Pust(Versh : U) : Boolean; Function V_Vershine(Versh : U) : BT; Procedure Ochistka(Var Versh : U); Implementation Procedure V_Stack; Begin V_Nachalo(Versh, X) End; Procedure Iz_Stack; Begin Iz_Nachala(Versh, X) End; Function Pust; Begin Pust := Versh = Nil End; Function V_Vershine; Begin V_Vershine := Versh^.Inf End; Procedure Ochistka; Begin Spisok.Ochistka(Versh) End; Begin End. /* C++, файл STACK.CPP */ 88 #include "SPIS.CPP" Zveno *V_Stack(Zveno *Versh, BT X) { return V_Nachalo(Versh, X); } Zveno *Iz_Stack(Zveno *Versh) { return Iz_Nachala(Versh); } int SPust(Zveno *Versh) { return !Versh; } BT V_Vershine(Zveno *Versh) { return Versh->Inf; } Zveno *Chistka(Zveno *Versh) { while (!Pust(Versh)) Versh=Iz_Stack(Versh); return Versh; } Используя разработанные здесь библиотеки, решим задачу. Пример. Написать программу, которая вычисляет как целое число значение выражений (без переменных), записанных (без ошибок) в постфиксной форме в текстовом файле. Каждая строка файла содержит ровно одно выражение. Алгоритм решения. Выражение просматривается слева направо. Если встречается число, то его значение (как целое) заносится в стек, а если встречается знак операции, то из стека извлекаются два последних элемента (это операнды данной операции), над ними выполняется операция и ее результат записывается в стек. В конце в стеке остается только одно число — значение всего выражения. { Turbo Pascal, файл ST2.PAS } Program St2; Uses Spisok, Stack; Const Znak = ['+', '-', '*', '/']; Var S, S1 : String; T : Text; I, N : Byte; X, Y : BT; Code : Integer; NS : U; Begin Write('Введите имя файла: '); ReadLn(S1); Assign(T, S1); ReSet(T); NS := Nil; While Not Eof(T) Do Begin ReadLn(T, S); I := 1; While I <= Length(S) Do 89 Begin If S[I] In ['0'..'9'] Then Begin N := I; While S[I] In ['0'..'9'] Do I := I + 1; Val(Copy(S, N, I - N), X, Code); V_Stack(NS, X); End Else If S[I] In Znak Then Begin Iz_Stack(NS, X); Iz_Stack(NS, Y); Case S[I] Of '+' : X := X + Y; '-' : X := Y - X; '*' : X := X * Y; '/' : X := Y Div X End; V_Stack(NS, X) End; I := I + 1 End; Iz_Stack(NS, X); WriteLn(S, ' = ', X); End End. /* C++, файл ST2.CPP */ #include "STACK.CPP" #include < string.h > #include < stdio.h > void main(void) { char S[255]; FILE *T; int I; BT X, Y; Zveno *NS; clrscr(); cout << "Введите имя файла: "; cin >> S; T=fopen(S, "r"); NS = NULL; while (!feof(T)) { fgets(S, 255, T); I = 0; while (I <= strlen(S)-1) { if (S[I]>='0'&&S[I]<='9') { X=0; 90 while(S[I]>='0'&&S[I]<='9') {X=X*10+(S[I]-'0'); I++;} NS=V_Stack(NS, X); } else if (S[I]=='+'||S[I]=='-'||S[I]=='/'||S[I]=='*') { X=V_Vershine(NS); NS=Iz_Stack(NS); Y=V_Vershine(NS); NS=Iz_Stack(NS); switch (S[I]) { case '+' : X += Y; break; case '-' : X = Y - X; break; case '*' : X *= Y; break; case '/' : X = Y / X; break;} NS=V_Stack(NS, X); } I++; } X=V_Vershine(NS); NS=Iz_Stack(NS); cout << S << " => " << X << "\n";} } Очередь — это информационная структура, в которой для добавления элементов доступен только один конец, называемый хвостом, а для удаления — другой, называемый головой. В англоязычной литературе для обозначения очередей довольно часто используется аббревиатура FIFO (first-in-first-out — первый вошёл — первым вышел). Очередь разумнее всего моделировать, отобразив её на двунаправленный кольцевой список. В этом случае в заглавном звене будет присутствовать информация как об указателе на голову, так и на хвост очереди. Выделим типовые операции над очередями: добавление элемента в очередь (помещение в хвост); удаление элемента из очереди (удаление из головы); проверка, пуста ли очередь; очистка очереди. Вот модуль, содержание которого составляют реализованные типовые операции над очередями. {Язык Pascal} Unit Spisok2; Interface Type BT = LongInt; U = ^Zveno; Zveno = Record Inf : BT; N, P: U End; Procedure V_Och(Var First : U; X : BT); Procedure Iz_Och(Var First : U; Var X : BT); Procedure Ochistka(Var First: U); Function Pust(First : U) : Boolean; Implementation Procedure V_Och; Var Vsp : U; 91 Begin New(Vsp); Vsp^.Inf := X; If First = Nil then begin Vsp^.N := Vsp; Vsp^.P := Vsp; First := Vsp end else begin Vsp^.N := First; Vsp^.P := First^.P; First^.P^.N := Vsp; First^.P := Vsp; end; End; Procedure Iz_Och; Var Vsp : U; Begin x:=first^.inf; if First^.p=first then begin dispose(first); first:= nil end else begin Vsp := First; First := First^.N; First^.P := Vsp^.P; Dispose(Vsp) end End; Procedure Ochistka; Var Vsp : BT; Begin While Not Pust(First) Do Iz_Och(First, Vsp) End; Function Pust; Begin Pust := First = Nil End; Begin End. // Язык С++ #include <iostream.h> #include <conio.h> #include <stdlib.h> #include <time.h> typedef long BT; struct U{ BT Inf; U *N, *P;}; U *V_Och(U *First, BT X) { U *Vsp; Vsp = (U*) malloc (sizeof(U)); Vsp->Inf=X; if (!First) {Vsp->N=Vsp; Vsp->P=Vsp; First=Vsp;} else {Vsp->N=First; Vsp->P=First->P; First->P->N=Vsp; First->P=Vsp;} return First;} 92 U *Iz_Och(U *First, BT &X) { U *Vsp; X=First->Inf; if (First->P==First) {free(First); First=NULL;} else {Vsp=First; First=First->N; First->P=Vsp->P; free(Vsp);} return First;} int Pust(U *First) { return !First;} U *Ochistka(U *First) { BT Vsp; while (!Pust(First)) First=Iz_Och(First, Vsp); return First; } Пример. Напечатать в порядке возрастания первые n натуральных чисел, в разложение которых на простые множители входят только числа 2, 3, 5. Алгоритм решения. Введем три очереди x2, x3, x5, в которых будем хранить элементы, которые соответственно в 2, 3, 5 раз больше напечатанных, но еще не напечатаны. Рассмотрим наименьший из ненапечатанных элементов; пусть это x. Тогда он делится нацело на одно из чисел 2, 3, 5. x находится в одной из очередей и, следовательно, является в ней первым (меньшие напечатаны, а элементы очередей не напечатаны). Напечатав x, нужно его изъять и добавить его кратные. Длины очередей не превосходят числа напечатанных элементов. {Язык Pascal} Program Example; Uses Spisok2; Var X2, X3, X5 : U; X : BT; I, N : Word; Procedure PrintAndAdd(T : BT); Begin If T <> 1 Then Write(T : 6); V_Och(X2, T * 2); V_Och(X3, T * 3); V_Och(X5, T * 5); End; Function Min(A, B, C : BT) : BT; Var Vsp : BT; Begin If A < B Then Vsp := A Else Vsp := B; If C < Vsp Then Vsp := C; Min := Vsp End; Begin X2 := Nil; X3 := Nil; X5 := Nil; Write('Сколько чисел напечатать? '); ReadLn(N); PrintAndAdd(1); For I := 1 To N Do Begin X := Min(X2^.Inf, X3^.Inf, X5^.Inf); PrintAndAdd(X); If X = X2^.Inf Then Iz_Och(X2, X); If X = X3^.Inf Then Iz_Och(X3, X); 93 If X = X5^.Inf Then Iz_Och(X5, X); End; Ochistka(X2); Ochistka(X3); Ochistka(X5); WriteLn End. // Язык С++ #include "spis2.cpp" void PrintAndAdd(BT T); BT Min (BT A, BT B, BT C); U * X2, *X3, *X5; void main () { BT X; long I, N; X2 = NULL; X3 = NULL; X5 = NULL; cout << "Сколько чисел напечатать? "; cin >> N; PrintAndAdd(1); for (I=1;I<=N; I++) { X = Min(X2->Inf, X3->Inf, X5->Inf); PrintAndAdd(X); if (X==X2->Inf) X2=Iz_Och(X2, X); if (X==X3->Inf) X3=Iz_Och(X3, X); if (X==X5->Inf) X5=Iz_Och(X5, X); } X2=Ochistka(X2); X3=Ochistka(X3); X5=Ochistka(X5); cout << endl; } void PrintAndAdd(BT T) { if (T!=1) {cout.width(6); cout << T;} X2=V_Och(X2, T*2); X3=V_Och(X3, T*3); X5=V_Och(X5, T*5); } BT Min (BT A, BT B, BT C) { BT vsp; if (A < B) vsp=A; else vsp=B; if (C < vsp) vsp=C; return vsp; } Контрольные вопросы и задания 1. Какую структуру данных называют очередью? Что такое хвост и голова очереди? 2. На базе каких структур данных может быть организована очередь? 3. Приведите из жизни примеры организации чего-либо по принципу очереди. 4. Используя очередь, напечатайте сначала русские символы данной строки, а затем все остальные, сохранив их порядок следования. 5. Составьте и решите задачу с использованием абстрактного типа данных "очередь". 94 4.2. Содержание практических занятий Практическое занятие № 1 Условный оператор Задание: Используя разветвляющуюся структуру, составить блок-схему вычисления значения составной функции, имеющей различный вид на разных участках аргумента, затем составить программу, реализующую данный алгоритм (значение аргумента функции вводится с клавиатуры). Теоретический материал Алгоритм – это любая конечная последовательность основных математических и логических действий, однозначно определяющих процесс преобразования исходных данных в искомые результаты решения задачи. Для представления алгоритмов используются несколько способов: • словесный (описание на естественном человеческом языке); • с помощью одного из алгоритмических языков программирования; • графический (на языке блок-схем); • с использованием диаграмм; • с использованием таблиц решений; и др. Рассмотрим способ составления алгоритма на языке блок-схем. Блок-схема – это графическая интерпретация алгоритма, представляющая набор геометрических фигур, каждая из которых изображает какую-либо операцию или действие. Правила построения алгоритмов на языке блок-схем 1. Блок-схема строится сверху вниз. 2. В любой блок-схеме имеется только один элемент, соответствующий началу алгоритма, и один элемент, соответствующий концу алгоритма. 3. Должен быть ходя бы один путь их начала блок-схемы любому элементу. 4. Должен быть хотя бы один путь от каждого элемента блок-схемы в конец блок-схемы. Практическое занятие № 2 Условный оператор if Иногда требуется, чтобы часть программы выполнялась не всегда, а лишь при выполнении некоторого условия (а при невыполнении этого условия выполнялась другая часть программы). В этом случае пользуются оператором уловного выполнения, который записывается в следующем виде: if <условие> then <оператор1> else <оператор2>; Под оператором понимается либо одиночный оператор (например, присваивания, вызова процедуры), либо так называемый составной оператор, состоящий из нескольких простых операторов, помещенных между словами begin и end. Важно заметить, что перед else не ставится точка с запятой. Часть else может и отсутствовать. Пример выполнения лабораторной работы В качестве примера рассмотрим составную функцию вида. Как видно из задания, функция вычисляется на трех диапазонах аргумента x по трем различным формулам. Составим блок-схему алгоритма решения данной задачи. Теперь по данному алгоритму составим программу (а не наоборот) на языке Pascal. 95 Любую программу рекомендуется (но не обязательно) начинать с заголовка. Program Lab1_varX; x > 2.5 0 ≤ x ≤ 2.5 x<0 7 Далее следует раздел описания переменных. В нашем примере понадобятся две переменные для хранения аргумента x и значения функции y. Так как и аргумент, и сама функция могут принимать дробные значения, то их необходимо описывать вещественным типом данных. Var x,y: real; Затем описываем основное тело программы. Как следует из блок-схемы алгоритма, вначале необходимо вывести информационный блок (автор, номер варианта, назначение программы), после чего вывести текстовую строку, которая подскажет пользователю, что нужно вводить значение аргумента. А затем считать с клавиатуры введенное пользователем число. Не забываем, что тело программы начинается с зарезервированного слова begin. Begin writeln; writeln(’ Автор – Иванов И.П., студент гр. ИСЭд-11’); writeln(’ Вариант № 100’); writeln(’Программа вводит значение аргумента X и вычисляет значение функции Y’); writeln(’ |2*x x>2.5’); writeln(’Y= |x^3-x 0<=x<=2.5’); writeln(’ |x*(sin(x)) x<0’); writeln; writeln(’введите x= ’); readln(x); В данном фрагменте используется два вида оператора вывода: с параметром и без параметра. В первом случае на экран выводится текст, который указан в качестве параметра, а во втором случае – пустая строка (для того чтобы сделать отступ между строками). После того, как значение X введено оператором ввода readln(x), нужно определить, по какой формуле должна вычисляться функция. Для этого в алгоритме предусмотрены проверки значения аргумента. If x>2.5 then Y:=2*x Else If x>=0 then y:=x*x*x-x Else Y:=x* sin(x); Как видно, в данном фрагменте программы признак конца оператора (точка с запятой) ставится только один раз в самом конце, т. к. условный оператор заканчивается именно на последней строке, перед Else точку с запятой не ставят. В конце программы нужно вывести результаты вычислений и завершить тело программы служебным словом end с точкой. writeln(’Y= ’, y:7:3); readln end. Последний оператор вывода отличается от всех предыдущих. В данном случае стандартная процедура вывода writeln содержит два параметра, перечисленных через запятую. Первый параметр – это текстовая строка (’Y= ’), которую надо вывести на экран. 96 Второй параметр – переменная Y, значение которой требуется вывести в определенном формате, о чем говорят два числа, написанные через двоеточие. Первое число – количество позиций, отводимых под вывод всего числа (включая знак, целую часть числа, точку и дробную часть числа), а второе – число разрядов после запятой. В нашем случае под вывод всего числа запланировано 7 позиций, из них 3 позиции – под дробную часть, одна позиция – под точку, остается 3 позиции под целую часть и знак. Следует заметить, что если программист указал недостаточное количество позиций под вывод всего числа, то это число будет автоматически увеличено до требуемого для вывода значения. Для того, чтобы результаты работы программы оставались на экране после выполнения программы, используем оператор ввода без параметров readln. В этом случае программа выполнит все необходимые действия и будет ожидать от пользователя нажатия клавиши Enter. Итак, программа написана, ниже приведен ее полный текст. Program Lab1_variant100; Var x,y: real; Begin writeln; writeln(’ Автор – Иванов И.П., студент гр. ИСЭд-11’); writeln(’ Вариант № 100’); writeln(’Программа вводит значение аргумента X и вычисляет значение функции Y’); writeln(’ |2*x x>2.5’); writeln(’Y= |x^3-x 0<=x<=2.5’); writeln(’ |x*(sin(x))) x<0’); writeln; writeln(’введите x= ’); readln(x); If x>2.5 then Y:=2*x Else If x>=0 then y:=x*x*x-x Else Y:=x*sin(x); writeln(’Y= ’, y:7:3); readln end.__ Практическое занятие № 3 Определить количество элементов массива, значение которых больше соседних элементов Пользователь вводит n элементов массива. Требуется определить количество элементов, значение которых больше, чем у соседних элементов массива. Описание переменных: m - количество реальных элементов массива; qty - количество элементов массива, значение которых больше, чем у соседей. Алгоритм решения задачи: 1. Считываем числа с помощью процедуры read() и помещаем их в массив. 2. Переменной qty присваиваем значение 0. 97 3. Переменной i, которая обозначает индекс массива, присваиваем значение 2, т.к проверять первый элемент массива не имеет смысла (у него нет левого соседа). 4. В условии цикла выражение i < m говорит о том, что последний элемент массива также не проверяется, т.к. у него нет правого соседа. 5. Если очередной элемент массива (arr[i]) больше предшествующего (arr[i-1]) и очередной элемент массива (arr[i]) больше последующего (arr[i+1]), то ... 6. ... увеличить значение qty на единицу и ... 7. ... увеличить значение i на 2, т.к проверять следующий элемент нет смысла, т.к. уже известно, что он меньше левого соседа. 8. Иначе (если условие не выполнилось) перейти к следующему элементу массива (i := i + 1). 9. Вывести значение qty на экран. Это не оптимальный алгоритм, но короткий. Программа на языке Паскаль: const n = 100; var arr: array[1..n] of integer; i, m, qty: byte; begin write('Количество элементов массива: '); readln(m); write('Элементы массива через пробел: '); for i:=1 to m do read(arr[i]); readln; qty := 0; i := 2; while i < m do if (arr[i] > arr[i-1]) and (arr[i] > arr[i+1]) then begin qty := qty + 1; i := i + 2 end else i := i + 1; writeln('Кол-во элементов, которые больше соседей: ', qty); readln; end. Примечания: Пример выполнения программы: Количество элементов массива: 6 Элементы массива через пробел: 3 4 2 3 7 6 Количество элементов, которые больше соседей: 2 Получается 2, т.к. только четверка и семерка больше обоих своих соседей. Практическое занятие №4 Определение различных цифр, входящих в число Какие различные цифры входят в целое число. Описание переменных: n – анализируемое число; digits – массив различных цифр, из которых состоит число n; q_digits – количество цифр в массиве; digit – извлеченная из числа цифра; 98 – проверка наличия цифры в массиве (если цифра в массиве уже есть, то она не будет в него добавлена. flag Алгоритм решения задачи: 1. Избавляемся от знака числа с помощью функции abs. 2. Обнуляем количество цифр в массиве (изначально массив пуст). 3. После извлечения очередной цифры числа с конца (n mod 10) предполагаем, что ее еще нет в массиве (flag:= false). 4. Перебираются элементы массива цифр. Если текущая извлеченная цифра уже есть в нем, то меняем значение флага. Для первой извлеченной цифры данный цикл не работает, что правильно. 5. Если флаг остался в значении false, то увеличиваем количество цифр в массиве на единицу и записываем очередную цифру в массив. 6. Избавляемся от последней цифры числа (n div 10). 7. Цифры в массив записаны в обратном порядке. Для их вывода на экран в порядке следования в числе следует использовать вариант цикла for с downto. Программа на языке Паскаль: var n: longint; digits: array[1..10] of byte; q_digits: byte; digit: byte; i: byte; flag: boolean; begin write('Input number: '); readln(n); if n < 0 then n:= abs(n); q_digits:= 0; repeat digit:= n mod 10; flag:= false; for i:=1 to q_digits do if digit = digits[i] then begin flag:= true; break end; if flag = false then begin q_digits:= q_digits + 1; digits[q_digits]:= digit; end; n:= n div 10 until n = 0; for i:=q_digits downto 1 do write(digits[i]:3); readln end. Практическое занятие №5 Использование динамических массивов Десять раз генерировать ряд случайных чисел от 0 до 99. Каждый ряд также должен иметь произвольную длину. В каждом ряде следует найти максимальное число. Сохранить только максимальные числа и вывести их на экран. Описание переменных: 99 arrmax – обычный массив, где будут сохраняться максимальные значения рядов; row – динамический массив для текущего ряда чисел; n – длина текущего ряда чисел. Алгоритм решения задачи: Переменной j будет играть роль индекса элементов массива arrmax. В конце цикла for, который сделает 10 итераций, j будет увеличиваться на единицу, что будет означать переход к следующей ячейке массива. Длина очередного ряда чисел случайна. Получив значение n, выделяем память под динамический массив с помощью процедуры getmem. Заполняем динамический массив случайными числами. Записываем в очередную ячейку массива arrmax первое значение динамического массива. Если какое-либо последующее значение массива, на который указывает row, будет больше arrmax[j], то производится перезапись значения. После нахождения максимума память можно освободить (freemem). Программа на языке Паскаль: type arrdinamic = array[1..50] of integer; var arrmax: array[1..10] of integer; row: ^arrdinamic; n, i, j, k: integer; begin randomize; j := 1; for i := 1 to 10 do begin n := random(50) + 1; getmem(row,n*sizeof(integer)); for k := 1 to n do row^[k] := random(100); arrmax[j] := row^[1]; for k := 1 to n do if row^[k] > arrmax[j] then arrmax[j] := row^[k]; freemem(row,n*sizeof(integer)); j := j + 1 end; for i := 1 to 10 do write(arrmax[i], ' '); readln end. Практическое занятие №6 Форма и компоненты Создать приложение, в котором при вводе в текстовое поле нового заголовка и при последующем щелчке по кнопке мыши меняется заголовок окна. 1. Запустите Visual Basic с помощью меню Window/ Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 100 Для создания приложения необходимо использовать следующие объекты управления: a) Label1 - метка; b) Command1 - командная кнопка; c) Text1- текстовое окно. Перенести на пустую форму кнопку, текстовое окно и метку и уменьшить размеры формы до допустимо возможных, так как других компонент нет. 2. В работе следует использовать следующие свойства Properties. Label1 Caption = 'Введите новый заголовок' Command1 Caption = 'Сменить заголовок окна' Default = 'True' Text1 Text = 'Информатика и программирование' Form1 Caption = 'Пример 1' 3. Набрать программный код. Private SubCommand1_Click() Form1.Caption = Text1.Text End Sub 4. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы курсор появится в текстовом окне. Чтобы сменить заголовок формы, следует набрать новый текст в текстовом окне и нажать на кнопку «Сменить заголовок окна». 5. Для завершения выполнения программы необходимо нажать значок «закрыть» на форме или выполнить команду Run/End. 6. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 7. Задание 1. Выполнить манипуляции с формой и компонентами на ней, изменяя размеры, цвет и шрифт, свертывание и развертывание формы. 8. Задание 2. Добавить на форму еще две командные кнопки Command2 Command3 Caption = 'Сменить цвет формы' Caption = 'Выход из приложения' 101 9. Составим программный код для новых командных кнопок (Command2, Command3). Private Sub Command2_Click() Form1.BackColor = Rnd * 65 000 End Sub Private Sub Command3_Click() End End Sub 10. Запустите программу на выполнение. После запуска программы нажать кнопку «Сменить цвет формы»; можно это сделать многократно. Цвет формы будет меняться каждый раз при нажатии на эту кнопку, при этом будет работать и кнопка 1 каждый раз при соответствующем нажатии на нее. 11. Для завершения выполнения программы необходимо нажать на кнопку 3- «Выход из приложения», если она не работает, то нажать значок «закрыть» на форме или выполнить команду Run/End и проверить программный код. 12. Сохранить файл программы и файл формы. Практическое занятие №7 Ввод и вывод данных Использование случайных чисел для ввода данных при создании приложений. Задание. Даны два случайных числа а и b, которые выводятся в соответствующие текстовые поля на форме. Пользователь с клавиатуры задает название арифметической операции, которую надо выполнить над числами: сложение, вычитание, умножение, деление. Необходимо создать приложение, в котором в определенное текстовое поле указывается символ операции, в соответствующее текстовое поле выдается результат вычисления. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Label1 - метка; Label2 - метка; Labe3 - метка; Label4 — метка; Text 1 - текстовое окно; Text 2 - текстовое окно; 102 Text 3 - текстовое окно; Text 4 - текстовое окно; Command1 - командная кнопка; Command2 - командная кнопка. Перенести на пустую форму эти элементы и уменьшить размеры формы до допустимо возможных, так как других компонент нет. 3. В работе следует использовать следующие свойства Properties: Label1 Caption = 'Случайное число А' LabeI 2 Caption = 'Случайное число В' Label 3 Caption = ' Результат' Label 4 Caption = ' Знак операции' Text1, Text2, Text3, Text4 Text = ' 0 ' Command1 Caption = 'Старт' Command 2 Caption = 'Финиш' Form1 Caption = 'Практическая работа № 2' 4. Набрать программный код в соответствии с приведенным ниже текстом Private Sub «Старт»’ Command1_Click() ' Для Dim а, b, с As Single, oper As String a = Rnd * 300: b = Rnd * 200 Text1.Text = Format(а, "###.#") Text2.Text = Format(b, "###.#") oper = InputBox ("Введите название операции", "Пример 2") If oper = "сложение" Then с=a+b Text4.Text = Format("+") End If If oper = "вычитание" Then с=a-b Text4.Text = Format("-") End If If oper = "умножение" Then с=a*b Text4.Text = Format("*") End If 103 командной кнопки If oper = "деление" And b <> 0 Then с=a/b Text4.Text = Format("/") End If Text3.Text = Format(c, "######.####") 'Вывод результата End Sub Private Sub Command2_Click() ‘Для командной кнопки «Финиш»’ End End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы следует нажать кнопку «Старт», на экране появится окно с заголовком «Пример 2». Необходимо в текстовом поле набрать название операции и нажать кнопку «Старт». Повторите все сначала, нажав кнопку «Старт»: измените данные, введите название новой операции и т. д. 6. Для завершения выполнения программы необходимо нажать кнопку на форме «Финиш» (или значок «закрыть» на форме) или выполнить команду Run/End. 7. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 8. Задание. Выполнить манипуляции с формой и компонентами на ней, изменяя свойства формы: WindowState, MaxButton, MinButton, ControlBox, BorderStyle. Практическое занятие №8 Вывод данных на форму Вывод результатов вычислений на форму при создании приложений. Оператор цикла. Преобразование данных строкового типа в числовые. Задание. Составить программу для построения таблицы значений а и b, если а = х+4.3, b = ах + 1.23 при изменении х в диапазоне [0, 5] с шагом h =0.5. Результат вычислений вывести в виде таблицы. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Label1 - метка; Command1 –командная кнопка; Command2 - командная кнопка. Перенести на пустую форму эти элементы и уменьшить размеры формы до допустимо возможных, так как других компонент нет. 104 3. В работе следует использовать следующие свойства Properties: Label1 Caption = ‘Группа, Ф.И.О. студента' Command1 Caption = 'Старт' Command2 Caption = 'Финиш' Form1 Caption = 'Практическая работа №3' 4. Набрать программный код в соответствии с приведенным ниже текстом Private Sub Command1_Click() Dim x 0 , x k , x, a, b, h As Single Di m i , n As Int e ger Rem Ввод данных в текстовое окно и преобразование их в числа x0, xk, h x0 = Val(InputBox("Ввести начальное значение х " , "Пример 3 " ) ) x k = Val (InputBox ("Ввести конечное зн ачение xk", "При мер 3 " ) ) h = Val(InputBox("Ввести h - шаг изменения х " , "Пример 3 " ) ) х = х0 Form1.Cls ' - Очистка формы Print: :Print " "; " х0="; x0, "xk="; xk, "h ="; h Print: :Print " "; " a", " b", " x": Print Rem - Выделение целой части числа n = Int((xk – x0) / h) Rem Определение количества данных, выводимых на форму For i=1 To n A=x+4.4:b=a*x+1.23 Print “ “;a,b,x X=x+h Next End Sub Private Sub Command2_Click() End End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска 105 6. 7. 8. 9. программы следует нажать кнопку «Старт», на экране по очереди откроются окна с заголовком «Пример 2» (см. в программе оператор InputBox). Пользователь должен ввести в текстовом поле соответствующего окна значения: начальное значение х0 конечное значение xk, h — шаг изменения х ( см.условие задачи в начале). Повторите все сначала, нажав кнопку «Старт»: измените данные и т. д. Для завершения выполнения программы необходимо нажать кнопку на форме «Финиш» (или значок «закрыть» на форме) или выполнить команду Run/End. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. Задание. Выполнить манипуляции с формой и компонентами на ней, изменяя свойства формы: WindowState, MaxButton, MinButton, ControlBox, BorderStyle. Практическое занятие №9 Вывод табличных данных Вывод результатов вычислений на нестандартные управляющие элементы создании приложений. Оператор цикла. при Задание. Составить программу для заполнения массива Тab(5,5) случайными, целыми числами в интервале [0÷150]. Результат вычислений вывести в виде таблицы Microsoft FlexGrid. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Label1 - метка; Command1 - командная кнопка; MS FlexGrid - таблица. Чтобы поместить элемент Microsoft Flex Grid на панель инструментов, необходимо в меню Project выбрать команду Components, в одноименном диалоге найти и отметить флажком элемент Microsoft Flex Grid Control 6.0 и нажать кнопку «Применить». Выбранный элемент появится на панели компонентов в виде значка. Перенести на пустую форму эти элементы и уменьшить размеры формы до ДОПУСТИМО возможных, так как дрУГИХ компонент нет. 3. В работе следует использовать следующие свойства Properties: Label1 Caption = Труппа, Ф.И.О. студентов' Command1 Caption = 'Заполнить таблицу' Form1 Caption = 'Практическая работа № 4' MS FlexGrid Rows = 6, Cols = 6 4. Набрать программный код в соответствии с приведенным ниже текстом 106 ption Explicit Private Sub Commandl_Click() Dim Tabl(5, 5), c, w As Integer ' w - столбцы, с - строки For w = 0 To 4 ‘ начало цикла по‘ строкам 0 То 4 начало цикла по столбцам с) = Int(Rnd * 150) ' заполнение таблицы целыми ' случайными числами Next c Next w Rem назначение количества строк и столбцов на нестандартном элементе MSFlexGridl.Row = 6: MSFlexGrid1.Cols = 6 MSFlexGridl.Row =0 ‘нумерация столбцов на MSFlexGridl For с= 0 To 5 MSFlexGridl. Col = с MSFlexGridl.ColWidth(c) = 500 ‘ширина столбцов на MSFlexGridl MSFlexGridl.Text = с Next с MSFlexGridl.Col = 0 ‘нумерация строк на MSFlexGridl For w = 1 To 5 MSFlexGridl.Row = w MSFlexGridl.Text = w Next w Rem заполнение ячеек на MSFlexGridl элементами массива Tabl(5,5) For w = 1 To 5 For с = 1 To 5 MSFlexGridl.Col = с MSFlexGridl.Row = w MSFlexGridl.Text = Tabl(w - 1, с - 1) Next с Next w End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы следует нажать кнопку «Заполнить таблицу». Таблица в окне элемента MSFlexGridl заполнится числами. 6. Результат работы программы показан на рис. 4.7. Повторите все сначала: нажимая на кнопку «Заполнить таблицу». 7. Для завершения выполнения программы необходимо нажать значок «закрыть» на форме или выполнить команду Run/End. 8. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 107 Рис. 4.7. Результат работы программы 9. Задание. Измените данные, количество строк и столбцов, тип чисел, размеры ячеек и другие свойства элемента MSFlexGridl и повторите запуск программы. Практическое занятие №10 График функции Получение навыков программирования графических объектов и построения графиков функций. Задание. Составить программу для построения таблицы значений и графика функции у=1. 2 s i n x при изменении х в диапазоне [-10, 10]. Результат вычислений вывести на форму в виде таблицы. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Lab el1 — метка; Command1 — командная кнопка; Command2 командная кнопка; PictureBox1 - окно рисунка. Перенести на пустую форму эти элементы и уменьшить размеры формы до допустимо возможных, как показано на рис. 4.8. 3. В работе следует использовать следующие свойства Properties: Label1 Caption = Труппа, Ф.И.О. студентов' Command1 Caption = Трафик' Command2 Caption = 'Таблица' Form1 Caption = 'Практическая работа № 5" Рис. 4.8. Заготовка приложения 4. Набрать программный код в 108 соответствии с приведенным ниже текстом Option Explicit Private Sub Command1_Click() Dim x As Single Form1.Cls Pict u re 1. Vi s i ble = T r ue Picture1.DrawWidth = 1: Picture1.Scale (-12, Picture1.Line (-11, Picture1.Line (0, Picture1.DrawStyle = 2)-(12, -2) 0)-(ll, 1.5)-(0, 0 0) -1.5) For x = -10 To 10 Step 0 . 2 Picture1.DrawWidth = 4: Picture1.PSet (x, Picture1.DrawStyle = 2 1.2 * Sin(x) - x ), RGB(0,0, 256) Next x End Sub Private Sub Coinmand2_Click () Dim x As Single Form1.Cls Picture1.Visible = False Print: Print " ", " x ", " Функция": Print For x = -10 To 10 Step 2.5 Print " ", x, 1.2 * Sin(x)-x Next x End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы пользователь, поочередно нажимая кнопки «График» 1 и «Таблица», получит соответственно в окне рисунка график функции или таблицу функции на форме. 6. Результат работы программы показан на рис. 109 7. Для завершения выполнения программы необходимо нажать значок «закрыть» на форме или выполнить команду Run/End. 8. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 9. Задание. Измените функцию, выберите шаг, измените свойства линии (цвет и толщину) и повторите запуск приложения. Практическое занятие №11 Флажки. Таймер Получение навыков работы со стандартным диалоговым окном, таймером и флажками. Задание. Составить программу, которая выводит в текстовое окно время при установке флажка или показывает изображение — картинку, которую пользователь и процессе работы может изменить, если щелкнет по изображению указателем мыши. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Text1- текстовое окно; Check1- флажок; Check2 - флажок; Image1 - рисунок. Перенести на пустую форму эти элементы и уменьшить размеры формы до допустимо возможных. 3. В работе следует использовать следующие свойства Properties: Check1 Caption = 'Время' Oheck2 Caption = 'Рисунок' Form1 Caption = 'Часы' Image1 Stretch (растяжка) = ‘True' BorderStyle (стиль рамки) = 1 — Fixed Single (тонкая фиксированная) Picture (изображение) - выбрать изображение. Чтобы выбрать изображение, следует нажать кнопку с многоточием в диало говом окне с заголовком Load Picture (загрузить рисунок). Найти «исходный рисунок», который будет загружаться автоматически при запуске программы. Полное название этого файла (например, С:\Мои документы\DBWlZ\cfd.gif) необходимо запомнить. Для облегчения просмотра графических файлов при работе программы можно использовать стандартное диалоговое окно выбора файлов - Microsoft Common Dialog. Чтобы поместить, элемент Microsoft Common Dialog на панель инструментов, необходимо в меню Project выбрать команду Components, в одноименном диалоге найти 110 и отметить флажком элемент Microsoft Common Dialog Control 6.0 и нажать кнопку «Применить». Выбранный элемент появится на панели компонентов в виде значка. Для Common Dialog установить свойства: Custom = (дополнительно) в диалоговом окне Property Pages (страницы свойств) открыть вкладку Open/Save As и установить все свойства в соответствии с рис. 4.11. В поле FileName указать имя выбранного выше файла. 4. Набрать программный код в соответствии с приведенным ниже текстом. Option Еxplicit Private Sub Check1_Click() Image1.Visible = Fals Text1.Text = Format(Now, "hh: mm: ss") End Sub Private Sub Check2_Click() Image1.Visible = True Text1.Text = " " End Sub Private Sub Image1_Click() CommonDialog1.ShowOpen Text1.Text = " " Image1.Picture=LoadPicture(CommonDialog1.FileName) End Sub 111 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы пользователь, поочередно активизируя флажки для «Время» и «Рисунок», получит результат работы программы в соответствии с рис. Щелчок мышью по рисунку откроет окно обзора для выбора графического файла (с расширением, указанным выше в окне Property Pages) для его замены. 6. Для завершения выполнения программы необходимо нажать значок «закрыть» на форме или выполнить команду Run/End. 7. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 8. Задание. Измените расположение объектов на форме (например, переместите текстовое окно на изображение) или замените исходный рисунок, его тип, и повторите запуск приложения. Практическое занятие №12 Переключатели Получение навыков программирования графических объектов и изучение функций переключателя. Задание. Составить программу, которая позволяет изменить начертание и цвет геометрической фигуры, типа квадрат, овал, круг, используя стандартный элемент управления «фигура» - Shape. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Shape1 - фигура; Label1 метка; Command1 - командная кнопка; Frame1 - рамка; Option1 - переключатель; Option2 - переключатель; Option3 — переключатель; Option4 — переключатель; Option5 — переключатель; Option6 - переключатель. Перенести на пустую форму эти элементы и уменьшить размеры формы до допустимо возможных. Причем, прежде чем перенести переключатели, входящие в группу «Цвет», необходимо перенести на форму рамку. Далее, выбрав на панели инструментов очередной переключатель, нарисовать его внутри рамки. 3. В работе следует использовать следующие свойства Properties: Label1 Caption = 'ФИГУРА' Command1 Caption = 'OK' Option1 Caption = 'Квадрат'; Value = True 112 Option2 Option3 Option4 Option5 Option6 Frame1 Form1 Caption = 'Овал'; Value = True Caption ='Круг'; Value = True Caption = 'Красный'; Value = True Caption = 'Желтый'; Value = True Caption = 'Синий'; Value = True Caption = 'Цвет'; Value = True Caption = 'Переключатели' Shape1 BackStyle= 1 - Opacue BackColor= ButtonFace В orderStyle= 0 Transparent 4. Набрать программный код в соответствии с приведенным ниже текстом Option Explicit Private Sub Option1_Click() . Shape1.Shape = 1 End Sub Private Sub 0ption2_Click() Shape1.Shape = 2 End Sub Private Sub Option3_Click() Shape1.Shape = 3 End Sub Private Sub Option4_Click() Shape1.BackColor = vbRed End Sub Private Sub 0ption5_Click() Shape1.BackColor = vbYellow End Sub Private Sub 0ption6_Click() 113 Shape1.BackColor = vbBlue End Sub Private Sub Coimand1_Click () End End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы пользователь, поочередно активизируя переключатели для цвета и фигуры, получит разные варианты изменения фигуры. 6. Для завершения выполнения программы необходимо нажать кнопку «ОК» (или значок «закрыть» на форме) или выполнить команду Run/End. 7. Сохранить файл программы и файл формы с соответствующими расширениями .bas и .frm. 8. Задание. Добавьте для фигуры другие варианты, измените цвета, измените свойства заполнения фигуры (например, тип начертания) и повторите запуск приложения. Практическое занятие №13 Списки Процедуры и функции для работы со списками в Visual Basic. Заполнение простого списка и комбинированного списка, дополнение и удаление информации из списка. Составить программу для выбора информации о шрифте. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения можно использовать следующие объекты управления: Label1- метка; Label2 - метка; Label3 — метка; Label4 — метка; Command1 - командная кнопка с рисунком; Command2 — командная кнопка; Command3 - командная кнопка; Command4 - командная кнопка; Combo1 - комбинированный список; Combo2 - комбинированный список; List1- список; List2 - список. Необходимые объекты перенести на форму (в соответствии с рис.) и изменить размеры формы до допустимо возможных. В работе использованы следующие свойства Properties Label1 Caption = 'Выбрать шрифт' Label2 Caption = 'Выбрать начертание' 114 Label3 Label4 Command1 Command2 Command3 Command4 Form1 Combo1 Combo2 List1 List2 Caption = 'Выбрать цвет' Caption = 'Выбранный шрифт' Caption = 'Выход' Caption = 'Выбрать шрифт' Caption = 'Добавить шрифт' Caption = 'Удалить шрифт' Caption = 'Списки' List = в соответствии с таблицей Text='' List = в соответствии с таблицей Text = ' ' List = в соответствии с таблицей List = ' ' При заполнении строк списков необходимо использовать клавиши Ctrl+Enter. В соответствии с используемыми командными кнопками необходимо активизировать событие Click для Command1, Command2, Command3, Command4. 3. Полный текст программного кода, который составляется пользователем: Private Sub Command1_Click() If Combo1.Listlndex >= 0 Then List2.Addltem "Шрифт: " & Combo1.Text End If If Combo2.Listlndex >= 0 Then List2.Addltem Combo2.Text End If If List1.Listlndex >= 0 Then List2.Addltem List1.Text End If End Sub Private Sub Command2_Click() Dim ns As String ns = Trim(Combo1.Text) If ns <> " " Then Combo1.Addltem ns End Sub Private Sub Command3_Click() Combo1.RemoveItem Combo1.Listlndex 115 End Sub Private Sub Command4_Click() End End Sub Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы на экране появится приложение в соответствии с рис. Выбирая элементы шрифта, заполните список. 4. Для завершения выполнения программы выполняется команда «Выход»или выполнить команду Run/End. 5. Сохранить файл программы и файл формы с соответствующими расширениями. Практическое занятие №14 Создание меню Процедуры и функции для создания меню в Visual Basic. Заполнение команд меню для выполнения вычислений. Составить программу для вычисления функции с использованием команд меню. 1. Запустите Visual Basic с помощью меню Window/Пуск/Программы. Если программа уже работает, то открыть новый проект Project. 2. Для создания приложения необходимо использовать следующие объекты управления: Label1 - метка; LabeI2 - метка; Label3 - метка; Label4 - метка; Label5 - метка; Text1 - текстовое окно; Text2 - текстовое окно; Text3 - текстовое окно List1 - простой список. Необходимые объекты перенести на форму (в соответствии с рис.) и изменить размеры формы до допустимо возможных. 116 В работе использованы следующие свойства Properties. Label1 Caption = 'Функция' Label2 Caption = 'Вычисляемая функция' Label3 Caption = 'Левая граница' LabeI4 Caption = 'Правая граница' Label5 Caption ='Шаг' Text1 Text = 0 Text2 Text = 4 Text3 Text = 0.5 Form1 Caption = 'Создание меню' List1 List = ' ' 3. В соответствии с используемыми командами меню необходимо активизировать событие Click для следующих команд меню: func -> Вычислить функцию; ism_int -> Изменить интервал; c!s_List -> Очистить окно вывода; end -> Выход. 4. Полный текст программного кода, который составляется пользователем: Private Sub func_click() Rem Вычисление функции 117 Dim x0, xk, x, a, b, a1, b1, h As Single Dim i, n As Integer X0 = Text1.Text xk = Text2.Text h = Text3.Text n = Int((xk – x0) / h) List1.Addltem "a b " For i = 1 To n a = x + 4.4: b = a * x + 1.23 a1= Format(a, "###..##") b1= Format(b, "####..##") List1.Addltem " " & CStr(a1) & " " & CStr(b1) x=x+h Next End Sub Private Sub ism_int_Click() Rem Изменить интервал - очистить текстовые окна ввода Text1.Text = Clear Text2.Text = Clear Text3.Text = Clear End Sub Private Sub cls_List_Click() Rem Очистить список List1.Clear End Sub Private Sub end_Click() Rem Закрыть приложение - Выход End End Sub 5. Запустите программу на выполнение: из меню Run/Start или клавишей F5. После запуска программы на экране появится форма в соответствии с рис. Запуская команды меню, исследуйте работу отдельных процедур. 6. Для завершения выполнения программы выполняется команда «Выход», или выполнить команду Run/End. 7. Сохранить файл программы и файл формы с соответствующими расширениями. 118 4.3. Методические рекомендации преподавателю Согласно существующему государственному образовательному стандарту специальности и других нормативных документов целесообразно разработать матрицу наиболее предпочтительных методов обучения и форм самостоятельной работы студентов, адекватных видам лекционных и лабораторных занятий. Необходимо предусмотреть развитие форм самостоятельной работы, выводя студентов к завершению изучения учебной дисциплины на её высший уровень. Пакет заданий для самостоятельной работы следует выдавать в начале семестра, определив предельные сроки их выполнения и сдачи. Организуя самостоятельную работу, необходимо постоянно обучать студентов методам такой работы. Вузовская лекция — главное звено дидактического цикла обучения. Её цель — формирование у студентов ориентировочной основы для последующего усвоения материала методом самостоятельной работы. Содержание лекции должно отвечать следующим дидактическим требованиям: изложение материала от простого к сложному, от известного к неизвестному; логичность, четкость и ясность в изложении материала; возможность проблемного изложения, дискуссии, диалога с целью активизации деятельности студентов; опора смысловой части лекции на подлинные факты, события, явления, статистические данные; тесная связь теоретических положений и выводов с практикой и будущей профессиональной деятельностью студентов. Преподаватель, читающий лекционные курсы в вузе, должен знать существующие в педагогической науке и используемые на практике варианты лекций, их дидактические и воспитывающие возможности, а также их методическое место в структуре процесса обучения. Лабораторные работы сопровождают и поддерживают лекционный курс. При проведении промежуточной и итоговой аттестации студентов важно всегда помнить, что систематичность, объективность, аргументированность — главные принципы, на которых основаны контроль и оценка знаний студентов. Проверка, контроль и оценка знаний студента, требуют учета его индивидуального стиля в осуществлении учебной деятельности. Знание критериев оценки знаний обязательно для преподавателя и студента. 4.3. Методические указания студентам Изучение программы курса. На лекциях преподаватель рассматривает вопросы программы курса, составленной в соответствии с государственным образовательным стандартом. Из-за недостаточного количества аудиторных часов некоторые темы не удается осветить в полном объеме, поэтому преподаватель, по своему усмотрению, некоторые вопросы выносит на самостоятельную работу студентов, рекомендуя ту или иную литературу. Кроме этого, для лучшего освоения материала и систематизации знаний по дисциплине, необходимо постоянно разбирать материалы лекций по конспектам и учебным пособиям. В случае необходимости обращаться к преподавателю за консультацией. Полный список литературы по дисциплине приведен в пункте 8.1. «Учебно-методическое обеспечение дисциплины». В целом, на один час аудиторных занятий отводится один час самостоятельной работы. 119 Контрольные работы. После изучения некоторых разделов практической части курса «Программирование» проводятся контрольные аудиторные работы. Для успешного их написания необходима определенная подготовка. Готовиться к контрольным работам нужно по материалам лекций и рекомендованной литературы. Обычно, контрольная работа имеет 4-6 вариантов. Лабораторные работы. При изучении курса «Программирование» необходимо выполнять и вовремя сдавать преподавателю индивидуальные лабораторные работы. Коллоквиум — это устный теоретический опрос. Он проводится в середине семестра с целью проверки понимания и усвоения теоретического и практического материала курса, а также для проверки самостоятельной работы студентов по вопросам программы курса. При подготовке к коллоквиуму ориентируйтесь на лекции и рекомендованную основную литературу. Дополнительная литература также может помочь при подготовке к теоретическому опросу. В каждом семестре предполагается проведение трех коллоквиумов. 120 4.5. Глоссарий Алгоритм - Точное предписание,определяющие вычислительный процесс,ведущий от варьируемых начальных данных к искомому результату. Алгоритм линейной структуры(Следование) - Алгоритм, в котором все действия выполняются последовательно друг за другом. Алгоритмизация - Техника составления алгоритмов и программ для решения задач на ЭВМ. Алфавит языка программирования - Набор символов, с помощью которого могут быть образованы величины,выражения и операторы данного языка. Ветвление - Схема, в которой предусмотрено разветвление указанной последовательности действий на два направления в зависимости от итога проверки заданного условия. Вещественный Тип(REAL) - Элементы подмножеств вещественного типа. Выражение - Совокупность операций и операндов. Графический способ записи Алгоритмов - Описание алгоритма с помощью графических символов. Дискретность - Свойство алгоритма разчленять предопределённый алгоритмом вычислительный процесс, на отдельные этапы, элементарные операции. Идентификатор - Последовательность символов, начинающаяся с буквы, для наименования объектов. Интерпретатор - Програмный продукт, выполняющий предъявленную программу путём одновременного её онализа и реализации предписанных ею действий. Итерация - Повторение последовательности операторов, включающим проверку условия в начале каждого прохода цикла. Компилятор - Разновидность транслятора, обеспечивающая перевод программ с языка высокого уровня на язык более низкого уровня или машинозависимый язык. Константа - Элемент данных, сохраняющий неизменное значение в течении всего времени выполнения программы. Логический тип(BOOLEAN) - Одно из двух истиностных значений, обозначаемых предопределёнными именами false и true. Массовость - Свойство алгоритма, позволяющее решать однотипичные задачи с различными исходными дынными по одному алгоритму. Метка - Произвольный идентифакатор, позволяющий именовать некоторый оператор программы и таким образом ссылаться на него. Оператор выбора - Оператор для программирования алгоритмов с множиственным выбором (CASE-OF-ELSE-END) Оператор перехода - Оператор передачи управления соответствующиму меченому оператору(GOTO) Оператор повторений WHILE-DO - Оператор для программирования алгоритмов циклической структуры с предпроверкой условия. Оператор присваивания - Оператор, присваивающий переменной или имени функции значение выражения, стоящего справа от знака присваивания. Определённость - Свойство алгоритма исключать произвольность толкования любого из предписаний и заданного порядка исполнения. Основные структуры алгоритмов - Ограниченный набор блоков и стандартных способов их соединения для выполнения типичных последовательностей действий. Переменная - Объект, имеющий фиксированное имя, фиксированный тип и изменяющееся в зависимости от применяемых действий значение. Программирование - Процесс определения последовательности инструкций, которые должен выполнить компьютер для решения определённой задачи. 121 Программный способ записи алгоритмов - запись алгоритма на языке программирования. Результативность - Свойство алгоритма через определённое число шагов приводит к выдаче результатов или сообщения о невозможности решения задачи. Символьный(литерный) тип - Элементы конечного и упорядоченного множества символов. Синтаксическая диаграмма - Направленый граф для описания синтаксиса языка, в соответствии с которым строится синтаксически правильная программа. Система программирования - Совокупность языка программирования и виртуальной машины, обеспечивающей выполнение реальной машиной программ, составленных на этом языке. Скалярные переменные - Переменные, имеющие в качестве текущего значения только одну величену. Словесный способ записи алгоритмов - Запись Последовательности действийв произвольном изложении на естественном язке Составной оператор - Последовательность произвольных операторов программы, заключённая в операторные скобки(BEGIN-END) Стандартные типы данных - Изначало определённые типы данных, встроенные в ЭВМ Структурированная переменная - Переменная, состоящая из нескольких элементов или компонент, на которую можно ссылаться как на единный объект. Структурно-стилизованный способ записи алгоритмов - Запись алгоритма путём использования ограниченного набора типовых синтаксических конструкций. Тип данных - Множество значений, которые может принимать переменная и совокупность операций, выполняемых с этими данными. Транслятор - Программа, осуществяющая перевод текстов с одного языка на другой. Уровень языка программирования - Смысловая ёмкость его конструкции и его ориентация на программиста-человека. Условный оператор - Оператор с ключевыми словами IF-THEN-ELSE для программирования алгоритмов разветвляющейся структуры. Целый тип(INTEGER) - Элементы подмножества целых чисел. Язык программирования - Система обозначений для точного описания алгоритмов для ЭВМ. 122