415 157 2MB
Russian Pages [96]
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
ЮЖНО-УРАЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
004(07) Ч-343
С.С. Чеботарёв, Д.И. Абдрахимова
ПРОГРАММИРОВАНИЕ НА MICROSOFT VISUAL C# Часть 1 ОСНОВЫ АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ
Учебное пособие
Челябинск 2019
Министерство науки и высшего образования Российской Федерации Южно-Уральский государственный университет Институт естественных и точных наук Кафедра «Прикладная математика и программирование»
004(07) Ч-343
С.С. Чеботарёв, Д.И. Абдрахимова
ПРОГРАММИРОВАНИЕ НА MICROSOFT VISUAL C# Часть 1 ОСНОВЫ АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ Учебное пособие
Челябинск Издательский центр ЮУрГУ 2019
УДК 004.056(075.8) Ч-343 Одобрено учебно-методической комиссией Института естественных и точных наук
Рецензенты: Овсяницкая Л.Ю., Рузаков А.А.
Чеботарёв, С.С. Ч-343 Программирование на Microsoft Visual C#. Ч. 1: Основы алгоритмизации и программирования: учебное пособие / С.С. Чеботарев, Д.И. Абдрахимова. – Челябинск: Издательский центр ЮУрГУ, 2019. – 94 с. Учебное пособие адресовано как школьникам, так и учащимся среднего профессионального или высшего образования и всем желающим изучать программирование. Систематическая подача материалов в виде последовательных уроков, содержащих технологии решения конкретных задач, позволяет быстро изучить программирование, как начинающим программистам, так и тем, кто имеет определенный опыт программирования, но не знаком с C#. УДК 004.056(075.8)
© Издательский центр ЮУрГУ, 2019
Оглавление Предисловие......................................................................................................... 5 Введение ............................................................................................................... 6 Знакомство со средой разработки...................................................................... 9 Консольный проект ........................................................................................... 12 Основы языка программирования Переменные ................................................................................................... 15 Типы данных .................................................................................................. 17 Объявление переменных .............................................................................. 20 Операции ........................................................................................................ 20 Выражения Арифметические выражения........................................................................ 24 Логические выражения ................................................................................. 25 Комментарии.................................................................................................. 27 Вывод и ввод данных .................................................................................... 27 Форматирование строк ................................................................................. 29 Математические функции ............................................................................ 34 Начинаем программировать Создаём первый проект ................................................................................ 36 Приведение и преобразование типов .......................................................... 42 Задачи для самостоятельного решения «Простейшие программы». ....... 45 Структурное программирование Понятие “Алгоритм” ..................................................................................... 47 Понятие о структурном подходе ................................................................. 48 Оператор блок ................................................................................................ 49 Основные управляющие конструкции ............................................................ 51 Следование ..................................................................................................... 51 Ветвление Оператор if ..................................................................................................... 52 3
Задача «Квадратное уравнение» .................................................................. 56 Задача «Три числа по возрастанию» .......................................................... 58 Тернарный оператор «?:» ............................................................................. 63 Каскадное ветвление ..................................................................................... 63 Задача «Знаки зодиака» ................................................................................ 64 Оператор выбора (switch) ............................................................................. 67 Задача «Китайский календарь» .................................................................... 71 Задачи для самостоятельного решения ....................................................... 76 Циклы Назначение и виды циклов ........................................................................... 79 Задача «Простые числа» ............................................................................... 86 Задача «Квадратный корень» ....................................................................... 87 Организация диалога с пользователем ....................................................... 89 Задача «Долгожитель» .................................................................................. 92 Задачи для самостоятельного решения ....................................................... 93
4
Предисловие Данное пособие содержит изложение основ программирования и алгоритмизации в среде Microsoft Visual Studio на языке C#. Материал изложен максимально простым и понятным языком, в виде последовательных занятий, содержащих методы решения конкретных задач. Задачи подобраны интересные, позволяющие в увлекательной форме раскрывать изучаемую тему. Разбирается алгоритм каждой задачи, описываются особенности программного кода, рассматриваются несколько вариантов решения. Предлагаются задачи для самостоятельного решения. Систематическая подача материалов в виде последовательных уроков, содержащих технологии решения конкретных задач, позволяет быстро изучить программирование, как начинающим программистам, так и тем, кто имеет определенный опыт программирования, но не знаком с C#. Учебное пособие адресовано как школьникам, так и учащимся среднего профессионального или высшего образования и всем желающим изучать программирование.
5
Введение В качестве первого языка для освоения программирования отлично подходит Java или C#, – это современные высокоуровневые языки программирования (ЯП) – на ранних этапах освоения ничуть не уступающие по простоте классическим академическим языкам таким как Паскаль (Pascal) или Бейсик (Basic). Однако, при своей первоначальной простоте, языки C# и Java занимают ведущие позиции в списке вакансий на российском и международном рынке труда и входят в пятёрку самых популярных ЯП согласно индексу TIOBE [1]. Рейтинг языков программирования TIOBE за февраль 2018 представлен в таблице 1. Таблица 1 Февраль 2018
Февраль 2017
ЯП
Рейтинг
Изменения %
1
1
Java
14.988%
-1.69%
2
2
C
11.857%
+3.41%
3
3
C++
5.726%
+0.30%
4
5
Python
5.168%
+1.12%
5
4
C#
4.453%
-0.45%
6
8
Visual Basic .NET
4.072%
+1.25%
7
6
PHP
3.420%
+0.35%
8
7
JavaScript
3.165%
+0.29%
9
9
Delphi/Object Pascal
2.589%
+0.11%
10
11
Ruby
2.534%
+0.38%
Изменения
По нашему убеждению, крайне пагубно влияет на формирование правильного мышления программиста выбор в качестве первого любого скриптового языка программирования с динамической слабой типизацией таких как: JavaScript, PHP, Visual Basic. Упомянутые языки могут быть достаточно хороши и удобны в своей нише для решения конкретных
6
прикладных задач, но не позволяют обучать некоторым важнейшим концепциям, правилам и дисциплине программирования. Также нам кажется крайне важным начинать изучение основ только со структурного (процедурного) программирования, не смешивая его с Объектно-ориентированным (ООП). В нашем случае, основным языком программирования мы выбираем Microsoft Visual C#. C# (произносится как "си шарп") – простой, современный объектноориентированный и типобезопасный язык программирования. Вот лишь несколько функций языка C#, обеспечивающих надежность и устойчивость приложений: сборка мусора автоматически освобождает память, занятую уничтоженными и неиспользуемыми объектами; обработка исключений дает структурированный и расширяемый способ выявлять и обрабатывать ошибки; строгая типизация языка не позволяет обращаться к неинициализированным переменным, выходить за пределы массива или выполнять неконтролируемое приведение типов. Как средство обучения программированию C# обладает рядом несомненных достоинств. Он хорошо организован, строг, большинство его конструкций логичны и удобны. Развитые средства диагностики и редактирования кода делают процесс программирования приятным и эффективным, а мощная библиотека классов платформы .NET берёт на себя массу рутинных операций, что даёт возможность решать сложные задачи, используя готовые "строительные блоки". Всё это позволяет расценивать C# в качестве перспективной замены языков Pasсal, Basic и C++ при обучении программированию. В то же время, C# является профессиональным языком, предназначенным для решения широкого спектра задач, в первую очередь, в быстро развивающейся области создания распределённых приложений. C# является объектно-ориентированным языком, но поддерживает также и компонентно-ориентированное программирование. Разработка современных приложений все больше тяготеет к созданию программных компонентов в форме автономных и самоописательных пакетов, реализующих отдельные функциональные возможности. Важная особенность таких компонентов – это модель программирования на основе свойств, методов и событий. Каждый компонент имеет атрибуты, предоставляющие декларативные сведения о компоненте, а также встроенные элементы документации. C# предоставляет языковые конструкции, непосредственно поддерживающие такую концепцию работы. Благодаря этому C# отлично подходит для создания и применения программных компонентов. Этот язык программирования является одним из самых молодых и перспективных на сегодняшний день: простой для освоения, строго типизированный и абсолютно безопасный (благодаря отсутствию адресной 7
арифметики и наличию системы автоматической сборки мусора) – вобрал в себя всё самое лучшее из всех прочих существующих языков программирования. C# довольно близок к языку Java, который возглавляет мировой рейтинг популярности языков программирования, но его объектно-ориентированные возможности и особенности (такие как, например: свойства, делегаты, события, лямбда-выражения и интегрированный язык запросов LINQ) дают ему преимущества в яркой простоте и выразительности языковых средств. Что касается инструментов разработки и многоцелевого использования, то и здесь язык может похвастаться богатым набором возможностей: на операционной системе Microsoft Windows для разработки бизнес приложений есть замечательная среда Visual Studio – один из самых известных инструментов профессиональных разработчиков ПО; на других системах (Linux, MacOS) – среды разработки MonoDevelop и Xamarin Studio. Популярные игровые движки Unreal, CryEngine и Unity поддерживают разработку игр на языке C#. Платформа Microsoft ASP .NET позволяет разрабатывать на C# самые сложные веб сайты и веб сервисы. Платформа Xamarin предоставляет возможности разработки мобильных приложений для популярных мобильных операционных систем Google Android и Apple iOS. Таким образом, по нашему мнению, язык C# идеально подходит для обучения. Базовый курс программирования, построенный на основе C#, позволит быстрее достичь уровня востребованного специалистапрофессионала. На начальных этапах, C# предоставляет для изучения простые элементы структурного программирования, такие же понятные и строгие как в Pascal, но гораздо более удобные и лаконичные. На следующем этапе – при освоении принципов объектно-ориентированного программирования C# демонстрирует все необходимые базовые элементы, не уступающие C++ или Java, но превосходящие их по удобству за счёт элементов «синтаксического сахара» – свойств, делегатов, индексаторов, событий, лямбда-выражений, встроенного языка запросов (LINQ), механизма рефлексий (отражений) и многого другого. Для типовых задач, при разработке многоцелевых проектов, C# опирается на огромную, продуманную библиотеку классов платформы .NET Framework. И наконец, о типе проектов – по нашему убеждению, начинать изучение программирования нужно только с разработки в консольном режиме – ни в коем случае не стоит начинать сразу с Windows Forms, Unity или графических фреймворков!
8
Знакомство со средой разработки На рис. 1 представлено окно среды разработки Microsoft Visual Studio 2017 в редакция Enterprise, но аналогичным будет и версия Community.
Рис. 1. Начальный вид окна среды разработки Для создания нового проекта (рис. 2) нужно выбрать пункт меню «Файл», затем «Создать» и «Проект» (Файл => Создать => Проект).
9
Рис. 2. Меню создания проекта Изучение основ программирования начинаем с помощью разработки консольных приложений для ОС Windows. При запуске консольного приложения операционная система создает так называемое консольное окно, через которое идет весь ввод-вывод программы. Внешне это напоминает работу в операционной системе в режиме командной строки, когда ввод-вывод представляет собой поток символов. Консольные приложения наилучшим образом подходят для изучения языка, так как в них не используется множество стандартных объектов, необходимых для создания графического интерфейса. В появившемся окне «Создание проекта» (рис. 3), в левой его части выбираем среди списка «Шаблоны» раздел «Visual C#» => «Windows» => «Классическое приложение» и в средней части окна находим в списке «Консольное приложение».
10
Рис. 3. Окно выбора шаблона проекта
11
Консольный проект После создания проекта мы можем видеть в среде разработки окно «Program.cs», в котором представлен шаблон исходного кода простого консольного приложения. Это окно может быть закрыто, и в этом случае вернуться к нему (и любому другому файлу с исходным кодом проекта) можно с помощью окна «Обозреватель решений», которое отображает полную структуру нашего проекта, включая все файлы с исходным кодом. Если в рабочем пространстве среды разработки отсутствует «Обозреватель решений» или любое другое необходимое вам окно (панель), его можно найти с помощью основного меню «Вид» среды Visual Studio. Теперь вернёмся к окну «Program» исходного кода проекта (рис. 4). Верхнюю часть, в которой присутствуют слова «using», мы рассмотрим подробно гораздо позднее (там описываются подключаемые «разделы» платформы Microsoft .NET Framework. Ниже, под текстом с «using», находится предложение «namespace ConsoleApplication1». Слово «namespace» обозначает «пространство имён», а за ним следует наименование нашего проекта. С помощью таких namespace’ов разработчики могут разделять свой проект на независимые модульные части. Далее в тексте встречаются символы, которые сразу примечает наш взгляд – это фигурные скобки { и }. Это один из самых часто используемых операторов языка программирования C#. Называется он – «оператор блока», одна из скобок { обозначает начало блока, другая – завершение (рис. 5).
Рис. 4. Готовый консольный проект 12
Рис. 5. Меню «Вид» для включения инструментальных окон (панелей) среды разработки
Рис. 6. Окно «Program» содержит исходный код шаблона «Консольное приложение»
13
Все ближайшие проекты мы с вами будем писать программный код внутри блока ‘Main” (рис. 7).
Рис. 7. Программный код для вывода приветствия в консоли
14
Основы языка программирования Переменные Любая задача, решаемая на ЭВМ, проходит через три этапа: 1) ввод данных, 2) обработка данных, 3) вывод результатов. Таким образом, данные являются важнейшим элементом при выполнении любой программы. Например, когда вы запускаете на своём вычислительном устройстве (компьютере, смартфоне или планшете) просмотр фотографии, - операционная система инициирует загрузку данных из постоянной памяти (получается, «ввод») затем полученная информация особым образом «обрабатывается» (выполняется декомпрессия и декодирование) и через видеокарту эти данные «выводятся» на дисплей. При программировании на любом этапе вы будете иметь дело с данными, придётся выполнять с ними различные действия, преобразования и вычисления. Поскольку важнейшим компонентом при выполнении любой программы являются данные, начинать знакомство с языком программирования стоит с рассмотрения понятия «переменные» (по англ. variables). Переменная – это именованная область памяти для хранения данных. С помощью переменных программы способны решать любые задачи, которые мы сможем сформулировать. Переменные позволяют хранить различные значения (данные), разрешают изменять их и передавать в подпрограммы. Без использования переменных процесс программирования сложно себе представить (это как готовить обед без продуктов!) Переменную можно представить, как сосуд с индивидуальной подписью для хранения варенья или компота. Обращение к переменной осуществляется по имени. В переменной может одновременно храниться одно значение. При занесении в переменную нового значения старое значение пропадает (вместо варенья переливаем в банку компот). Физически переменные представляют собой одну ячейку (или чаще группу ячеек) в оперативной памяти вычислительной машины, а имя переменной при выполнении программного кода, превращается в её фактический (числовой) адрес в линейном адресном пространстве оперативной памяти.
15
Поскольку переменная выступает основным кирпичиком при составлении программ, она является очень простым элементом и обладает всего двумя ключевыми характеристиками, это: 1) имя переменной 2) тип данных (значений, которые она может хранить) Имена позволяют нам обращаться к переменным в индивидуальном порядке и это совершенно логично и обоснованно, как и в реальной жизни у каждого человека есть собственное имя, фамилия и отчество, для того чтобы его можно было найти и обратиться лично. Следствием этой концепции также является то, что в пределах своей области действия переменные должны иметь собственные УНИКАЛЬНЫЕ имена. В большинстве языков программирования существуют единые правила именования переменных. Для задания имени можно использовать слова, составленные из латинских букв (верхнего и нижнего регистра), цифр и символа нижнего подчёркивания (все другие символы недопустимы), но запрещается начинать это слово с цифры. Дополнительной особенностью именования переменных в C# является возможность использовать буквы любого алфавита (в том числе и русского) в верхнем и нижнем регистре. Также язык C#, как и некоторые другие родственные языки (C, C++, Java, Php, JavaScript и др.), является регистрочувствительным (case sensitive) – это означает, что два и более одинаковых по звучанию имени, будучи записаны буквами, отличающимися регистром, будут считаться разными именами (например: counter, Counter, coUnteR – три имени для различных переменных). Процесс составления имени для переменной является достаточно значимым в программировании (как и в известном мультфильме – «Как вы яхту назовёте, так она и поплывёт»), поскольку правильно составленное имя в будущем может увеличивать время, затрачиваемое на составление алгоритмов и решение задач или, наоборот, сокращать его. Мы рекомендуем давать переменным «говорящие» имена – такие, благодаря которым сразу становится ясным их смысл и назначение. Несмотря на возможность использования русского языка, хорошей практикой всё-таки будет выбор в пользу правильных английских слов и для этого есть несколько причин: во-первых, улучшение знаний английского языка будет особенно полезным для программиста – так как вся актуальная техническая документация по современным информационным технологиям представлена именно на нём; во-вторых, так сложилось, что английский является языком международного общения и скорее всего в профессиональной деятельности вам придётся читать программный код, написанный разработчиками из Европы, Азии, Америки и, с большой долей вероятности, переменные и комментарии там будут написаны с использованием английского языка. 16
В профессиональной среде есть рекомендации и даже особые стили для именования переменных. Для C# актуальным является так называемый “camel casing style”, который рекомендует начинать имена переменных с маленькой буквы, составлять имена из нескольких слов и каждое следующее слово начинать с большой буквы. Например, переменную, предназначенную для подсчёта количества гостей, в этом стиле можно назвать следующим образом guestsCounter (счётчик гостей). Типы данных Вторая важнейшая характеристика переменной – это тип данных (type). Тип данных влияет на несколько важных вещей для переменной (а точнее, для значений, которые она может хранить): 1) объём занимаемой памяти (измеряется в байтах); 2) диапазон принимаемых значений; 3) виды допустимых операций над данными. Рассмотрим простейшие типы данных, с помощью которых мы продолжим знакомство с основами программирования. Первая группа – целочисленные типы данных, которые позволяют хранить в памяти целые числа и выполнять с ними математические операции. Таблица 2 Целочисленные типы данных в C# Название типа в C#
Название типа в (.NET Framework)
Размер (байт)
Знаковый/ Беззнаковый
sbyte
System.Sbyte
1
знаковый
short
System.Int16
2
знаковый
int
System.Int32
4
знаковый
long
System.Int64
8
знаковый
byte
System.Byte
1
беззнаковый
ushort
System.UInt16
2
беззнаковый
uint
System.UInt32
4
беззнаковый
ulong
System.UInt64
8
беззнаковый
Как можно видеть в таблице, в C# восемь целочисленных типов, отличаются они объёмом занимаемой памяти (1, 2, 4 и 8 байт) и 17
диапазоном принимаемых значений (это знаковые и беззнаковые типы). Если с памятью всё понятно, то иногда возникают вопросы по поводу знака. Например, тип byte занимает в памяти точно 1 байт (или 8 бит) и при этом является беззнаковым – это значит, что мы можем хранить в такой переменной только неотрицательные числа (всего 256 чисел). Поскольку в байте есть восемь бит, каждый из которых может принимать всего 2 значения (0 или 1), количество всех возможных состояний определяется по формуле N = 28 = 256. Таким образом, диапазон возможных значений для этого типа будет от 0 до 255 (всего 256 вариантов). В таблице есть родственный для byte тип – sbyte, где приставка -s является частью слова signed (т.е. «знаковый»). Этот тип также занимает один байт памяти, но его старший бит (один из восьми) выполняет особую функцию – он хранит пометку, является ли число положительным (бит равен нулю) или отрицательным (бит хранит единицу). Таким образом, в этом типе для хранения модуля числа остаётся всего семь бит и количество возможных состояний вычисляем N = 27 = 128. Диапазон возможных значений для типа sbyte будет от -128 .. 0 .. 127 (остаётся по-прежнему 256 значений, но включаются положительные и отрицательные числа). Аналогично представлены типы (short и ushort) – занимают по два байта памяти, но short – включая знак (+/-), а ushort – без знака, где приставка -u является частью слова unsigned (т.е. «безнаковый»); пара (int, uint) и (long, ulong). Для работы с вещественными (дробными) числами в языке C# есть типы: float (4 байта) и double (8 байт) и decimal (16 байт). Вещественные числа в памяти представляются следующим образом. Сначала число приводится к нормализованному виду, когда целая часть числа равна 0, первая цифра после запятой является значащей, а положение запятой в числе определяется значением показателя степени 10. Например, число 0,086 в нормализованной форме имеет вид 0.86·10-1, число 123,45 – 0.12345·103. При этом цифры, расположенные в нормализованной записи после точки, называются мантиссой, а показатель степени 10 – порядок. В памяти отдельно представляется мантисса и отдельно порядок. При этом количество бит, предназначенных для мантиссы, определяет точность представления, а количество бит, предназначенных для порядка, определяет диапазон представляемых чисел. Если количество цифр в двоичном представлении мантиссы превышает количество отведенных под нее разрядов, то последние двоичные цифры теряются и число оказывается представленным в памяти приближенно. Кроме того, при выполнении арифметических операций ошибки могут накапливаться. Таким образом, в общем случае вещественные числа в памяти представляются приближенно и их сравнение на точное равенство невозможно. Обычно вещественные 18
переменные используются для обозначения величин, полученных в результате измерений, которые всегда имеют некоторую погрешность, либо полученных в результате вычислений. Для выполнения логических операций и вычислений используется особый тип данных – bool (от англ. boolean – логический), занимает в памяти один байт и принимает одно из двух возможных значений true (истина) или false (ложь). Манипуляции с символами выполняются с помощью типа char (от англ. character – символ). И ещё один важный тип (значительно отличающийся от всех рассмотренных выше, но этот момент мы обсудим позже) – string (англ., строка) позволяет производить действия с текстовой информацией и может содержать произвольное количество символов. Таблица 3 Встроенные типы данных C# Тип Определение данных bool Логический тип данных, данные этого типа могут принимать значения true и false. sbyte Знаковый целый тип byte Беззнаковый целый тип short Знаковый целый тип ushort Беззнаковый целый тип int Знаковый целый тип uint Беззнаковый целый тип long Знаковый целый тип ulong Беззнаковый целый тип char Символьный тип, Unicodeсимвол float Вещественный тип double Вещественный тип decimal string
1 байт
Диапазон значений true, false
1 байт 1 байт 2 байта 2 байта 4 байта 4 байта 8 байт 8 байт 1 байт
-128 . . 127 0 . . 255 -32768 . . 32767 0 . . 65535 -2*109 . . 2*109 0 . . 4*109 -9*1018 . . 9*1018 0 . . 18*1018 U+0000 . . U+ffff
4 байта 8 байт
1.5*10-45. . 3.4*1038 5.0*10-324. .1.7*10308 1.0*10-28. . 7.9*1028
Размер
Финансовый тип для денежных вычислений Строковый тип, строка Unicode символов
16 байт Длина ограничена объемом доступной памяти 19
Объявление переменных Перед тем как выполнять какие-либо действия с переменной, необходимо объявить её в вашем коде (пример из жизни: перед тем, как налить в стакан воды, нужно сначала где-то купить/достать стакан). Язык C#, в отличие, например, от Паскаля, позволяет объявлять переменные практически в любом месте вашей программы – там, где возникла такая потребность. Синтаксис объявления очень простой: тип_переменной имя_переменной; Указывается тип и имя (через пробел или несколько пробелов), заканчивается объявление оператором точка_запятая. Примеры объявления переменных: int a; string st; double pi; Несколько переменных одного типа можно объявить через запятую: int x, y, z; Операции Для выполнения вычислений и различных преобразований над переменными в программировании используются «операции». Операции являются базовыми элементами языка и обозначаются различными символами пунктуации, а не алфавитно-цифровыми. Английское слово operator, соответствующее термину «операция», иногда ошибочно переводят как «оператор». На самом деле (по историческим причинам) русский термин «оператор» обозначает то же, что и «инструкция», которой соответствует английское statement. Путаница усугубилась тем, что в языке C присваивание и инкремент/декремент являются и операторами, и операциями. В соответствии с количеством операндов, которые используются в операциях (табл. 4), они делятся на унарные (один операнд), бинарные (два операнда) и тернарную (три операнда) Таблица 4 Операторы языка C# Операция Описание Унарные операции . Доступ к элементу структуры [] Доступ к элементу массива 20
Продолжение табл. 4 Операция ++
Описание
Увеличение на единицу: префиксная операция - увеличивает операнд до его использования, постфиксная операция увеличивает операнд после его использования. -Уменьшение на единицу: префиксная операция - уменьшает операнд до его использования, постфиксная операция уменьшает операнд после его использования. typeof Получение типа Унарный минус + Унарный плюс ! Логическое отрицание (НЕ). В качестве логических значений используется 0 (false) - ложь и не 0 (true) - истина, отрицанием 0 будет 1, отрицанием любого ненулевого числа будет 0. new Выделение памяти (тип) Преобразование типа Бинарные операции Мультипликативные * умножение операндов арифметического типа / деление операндов арифметического типа (если операнды целочисленные, то выполняется целочисленное деление) % получение остатка от деления целочисленных операндов Аддитивные + бинарный плюс (сложение арифметических операндов) бинарный минус (вычитание арифметических операндов) Операции отношения и поверки типа < меньше, чем
больше >= больше или равно is проверка принадлежности типу as приведение типа Операции сравнения == равно != не равно
21
Окончание табл. 4 Операция Описание Логические операции && конъюнкция (И) целочисленных операндов или отношений, целочисленный результат ложь(0) или истина( не 0) || дизъюнкция (ИЛИ) целочисленных операндов или отношений, целочисленный результат ложь(0) или истина(не 0) Тернарная ?: Условная операция в ней используется три операнда. Выражение1 ? Выражение2 : Выражение3; Первым вычисляется значение выражения1. Если оно истинно, то вычисляется значение выражения2, которое становится результатом. Если при вычислении выражения1 получится 0, то в качестве результата берется значение выражения3. Например: x = (отношения) == != (сравнения) && (конъюнкция «И») || (дизъюнкция «ИЛИ») ?: (условная операция) = *= /= %= -= &= ^= |= = (операция присваивания)
Оператор выполняется следующим образом: сначала вычисляется выражение, стоящее справа, а затем результат помещается в переменную, имя которой указано слева от знака операции присваивания. В C# разрешается соединить две команды – объявление переменной и присвоение ей значения (это называется – инициализация). Рассмотрим примеры для разных типов данных: int a = 2018; double pi = 3.14; bool isReady = false; char ch = 'C'; string st = "Привет"; Обратите внимание, что значения для переменных типа char записываются в апострофах ‘C’, а для переменных типа string в кавычках “Привет”. Несколько переменных одного типа можно инициализировать через запятую: int x = 10, y = -15, z = 0;
23
Выражения Арифметические выражения Мы привыкли, что под фразой «выражение» (например, «найдите значение выражения…») подразумевается обычно некоторое математическое выражение, например,
. В программировании из переменных, разделителей и знаков операций также можно конструировать выражения. Каждое выражение представляет собой правило вычисления нового значения. Каждое выражение состоит из одного или нескольких операндов, символов операций и ограничителей. Если выражение формирует целое или вещественное число, то оно называется арифметическим, например, int x = 10; int y = x * x + 5 * x - 4; Любое выражение, завершающееся точкой с запятой, рассматривается как оператор, выполнение которого заключается в вычислении значения выражения или выполнении законченного действия, например, вызова метода. Примеры простейших выражений. 1) Пример: int a = 15, b = 10; int c = a + b; объявлены переменные типа int (целочисленные) a, b – им присвоены значения 15 и 10, затем объявлена переменная c, которой присвоено значение суммы значений переменных a, b в результате в переменной c получится значение 25 (арифметическая операция сложения выполняется над целочисленными значениями). 2) Пример: string s1 = “15”, s2 = “10”; string s3 = s1 + s2; объявлены переменные s1, s2 – им присвоены значения “15” и “10”, затем объявлена переменная s3, которой присвоено значение суммы 24
значений переменных s1, s2 в результате в переменной s3 получится значение “1510” – происходит «сцепление» текстовых значений (по англ. concatenation). Обратите внимание, что одна и та же операция над разными типами данных приводит к разным результатам! Одно из выражений, наиболее часто встречающихся программировании связано с изменением значения переменной.
в
Пример: int guestsCounter = 0; guestsCounter = guestsCounter + 10; данное выражение увеличивает значение переменной guestsCounter (счётчик гостей) на десять. Вы можете обратить внимание, что в данном примере имя переменной указывается дважды: слева и справа от операции «присвоить значение». В C# существует возможность использовать сокращённую запись для подобных выражений, называется она «составное присвоение» (по англ. compound assignment). Пример: int guestsCounter = 0; guestsCounter += 10; И ещё одно очень популярное выражение увеличение значения числовой переменной на единицу получило даже особое название – инкремент (по англ. increment) и операцию ++. Таким образом можно записать три варианта выражения увеличение значения числовой переменной на единицу: int guestCounter = 0; 1) guestCounter = guestCounter + 1; 2) guestCounter += 1; 3) guestCounter ++; Логические выражения Пара арифметических выражений, объединенная операцией сравнения, называется отношением. Помимо математических выражений в 25
программировании присутствуют логические выражения, результатом которых может быть одно из двух значений: «истина» или «ложь». Для логических выражений не применимы обычные арифметические операции, а определён набор встроенных операций сравнения (табл. 6), позволяющих строить логические выражения. Таблица 6 Операции сравнения (отношений) Операция Равно Не равно Больше Меньше Больше или равно Меньше или равно
Синтаксис == != > < >= 2, a > b 4 < 5, a < b 3 >= 3, a >= b 7.5» с помощью метода Write. Напомним, что метод WriteLine() выводит на экран текст и переводит курсор ввода на следующую строку в консоли, а метод Write() оставляет курсор в той же строке, чтобы прочувствовать разницу между этими методами вы можете провести эксперимент со строкой номер 2, заменяя методы Write / WriteLine. 3) происходит считывание в консоли текстовых данных, вводимых пользователем с помощью метода ReadLine(). Для выполнения дальнейших действий с этими данными, они записываются в переменную с именем name типа string. Обратите внимание, что имя переменной отражает смысл хранимых в ней данных. 4) на экран выводится текстовая строка, в которую с помощью индексной метки вставляется значение, хранимое в переменной name. 5) На экран выводится текстовое значение выражения, в котором происходит склеивание (конкатенация) двух строковых значений (записанных в кавычках) и значения переменной name. Четвёртый и пятый пункт демонстрируют два различных способа вывода на экран информации: склеивание текстовых значений и использование строки форматирования с индексными метками. 6) Метод ReadLine() в завершении программы не даёт операционной системе автоматически закрыть наше приложение, ожидая от пользователя нажатия клавиши «Enter». Вы можете провести эксперимент, удалив шестую строку кода и запустив программу повторно. Простейшая программа 2 Выполните следующие действия: Шаг 1. Создайте новый проект консольного приложения, назовите его Project02. Шаг 2. Откройте файл Program.cs, дважды кликнув по нему в окне «Обозреватель решений». Шаг 3. Внутри метода Main введите код, приведённый в листинге ниже.
37
Шаг 4. Нажмите клавишу «F5». Шаг 5. В консольной программе, следуя подсказкам, введите числа A, B и нажмите клавишу «Enter». В итоге на экране консольного приложения вы увидите результат (при введении, например, чисел 10 и 20. Сумма = 1020).
Такой результат получился потому что переменные A, B и C объявлены текстовыми (тип string) и операция сложения приводит к склеиванию символов. Переработайте программу следующим образом:
Как вы можете видеть, в четвёртой строке синтаксический анализатор Visual Studio выделил ошибку: Не удается неявно преобразовать тип “string” в тип “int”. Дело в том, что C# является языком строго типизированным и он требует, чтобы типы данных значений слева и справа от операции «присвоить значение» совпадали (это очень разумно, и можно сравнить с ситуацией, когда школьник, решая задачу по физике, находит сумму значений, представляющих килограммы и километры). Для исправления ошибки необходимо преобразовать текстовое значение из переменной st (типа string) в целочисленное значение
38
переменной A (типа int), для этого можно использовать метод Convert.ToInt32(st) или метод int.Parse(st). Переработайте программу следующим образом:
Как вы можете видеть, в шестой строке синтаксический анализатор Visual Studio выделил ошибку: Локальная переменная с именем “st” уже определена в этой области. Ошибка связана с тем, что в одной области видимости (обсудим детальнее немного позже) нельзя объявить несколько переменных с одинаковым именем. В данном случае можно использовать два варианта решения этой проблемы: 1) в шестой строке изменить имя переменной (например, вместо st заменить на st2). 2) в принципе, эта переменная в нашей программе играет роль промежуточного звена (посредника): в неё записывается текстовая информация, введённая пользователем, а затем эти данные заносятся в целочисленную переменную и, таким образом, повторно объявлять нам ещё одну такую переменную не нужно – мы можем использовать уже существующую повторно (ведь хранимые там данные уже не нужны, они содержатся в целочисленной A). Переработайте программу следующим образом:
После запуска увидим: 39
Пояснения к программе: 1 2 3 4 5 6 7 8 9 10
Console.WriteLine(" Сумма и среднее арифметическое"); Console.Write(" Введите число A >"); string st = Console.ReadLine(); int A = Convert.ToInt32(st); Console.Write(" Введите число B >"); st = Console.ReadLine(); int B = int.Parse(st); int C = A + B; Console.WriteLine(" Сумма = " + C.ToString()); Console.ReadLine();
1. Вывод на экран описания программы “Сумма и среднее арифметическое” методом WriteLine(). 2. Вывод на экран подсказки “Введите число A >” методом Write(). 3. Считываем текст, вводимый пользователем в консоли с помощью метода ReadLine() и сохраняем его в переменной st типа string. 4. Преобразуем текст, содержащийся в переменной st в целое число с помощью метода Convert.ToInt32() и сохраняем в переменную A типа int. 5. Вывод на экран подсказки “Введите число B >” методом Write(). 6. Считываем текст, вводимый пользователем в консоли с помощью метода ReadLine() и сохраняем его в st, используем повторно объявленную ранее (на шаге 3) переменную. 7. Преобразуем текст, содержащийся в переменной st в целое число с помощью метода int.Parse() и сохраняем в переменную B типа int. 8. Объявляем целочисленную переменную C и присваиваем ей значение суммы чисел A и B. 9. Выводим на экран консоли результат в виде двух сцепленных строк: “Сумма =” и строки, полученной с помощью метода ToString() из переменной C. 10. Метод ReadLine() в завершении программы не даёт операционной системе автоматически закрыть наше приложение, ожидая от пользователя нажатия клавиши «Enter». Расширим наш проект вычислением значения арифметического, для этого вместо десятого пункта запишите:
40
среднего
После запуска проекта и ввода чисел A=10, B = 5 вы увидите:
Значение среднего арифметического двух чисел 10 и 5 получилось 7. Немного не тот результат, которого можно было бы ожидать, поскольку 15/2 = 7,5. Этот результат связан с тем, что операция деления в C# может быть целочисленной или дробной в зависимости от контекста! В нашем случае, поскольку переменные middle и C являются целочисленными, в результате деления мы получили целый результат (дробная часть была отброшена). Однако, если мы изменим тип данных переменной middle на дробный (например, double) результат не изменится, потому что операция деления по-прежнему происходит над целыми числами: С и 2. Для получения дробного результата у нас есть два варианта: 1) объявить переменную C как дробную (например, тип double) или 2) при делении выполнить приведение одного из значений к дробному типу. Переработайте программу следующим образом:
После запуска на экране консоли (при вводе числе A=10, B=5) получим:
41
Приведение и преобразование типов Поскольку код C# является статически типизированным, после объявления переменной ее нельзя объявить повторно или использовать для хранения значений другого типа, если этот тип не преобразуется в тип переменной. Например, отсутствует преобразование из целого числа в произвольную строку. Поэтому после объявления переменной i как целого числа нельзя назначить ей строку "Hello", как показано в следующем коде.
Синтаксический анализатор Visual Studio выдаст ошибку: не удаётся неявно преобразовать тип string в тип int. Тем не менее иногда может потребоваться скопировать значение в переменную другого типа, такого рода операции называются преобразованиями типа (по англ. type casting). В C# можно выполнять следующие виды преобразований. 1. Неявные преобразования: никакой специальный синтаксис не требуется, поскольку преобразование является строго типизированным и данные не будут потеряны. Примеры включают преобразования из меньших в большие целочисленные типы и преобразования из производных классов в базовые классы. 2. Явные преобразования (приведения): явные преобразования требуют оператора приведения. Данная операция требуется, если в ходе преобразования данные могут быть утрачены или преобразование может завершиться сбоем по другим причинам. Типичными примерами являются числовое преобразование в тип с меньшей точностью или меньшим диапазоном и преобразование экземпляра базового класса в производный класс. 3. Преобразования с использованием вспомогательных классов. Чтобы выполнить преобразование между несовместимыми типами, например, целыми числами и строками. Рассмотрим на примерах. 1. Неявные преобразования. Для встроенных числовых типов неявное преобразование можно выполнить, если сохраняемое значение может уместиться в переменной без усечения или округления. Например, переменная типа int (4-байтное целое число со знаком) может хранить любое значение, которое может хранить переменная sbyte (1-байтное целое число со знаком). 42
В данном примере выполняется неявное преобразование, которое не может привести к потере данных (в качестве образного сравнения приведём следующую аналогию: из стакана переливаем воду в пустой графин – мы не рискуем потерять в этом процессе не капли воды). Однако, это преобразование мы можем указать и явно (хотя большой пользы в этом нет, как нет и ошибки).
2. Явные преобразования. Если преобразование нельзя выполнить без риска потери данных, компилятор требует выполнения явного преобразования, которое называется приведением. Приведение – это способ явно указать компилятору, что необходимо выполнить преобразование и что вам известно, что может произойти потеря данных. Чтобы выполнить приведение, укажите тип, в который производится приведение, в круглых скобках перед преобразуемым значением или переменной.
В данном примере число 250 из переменной a типа int (4 байта) с помощью приведения записывается в переменную b типа byte (1 байт). Эта ситуация потенциально опасна, потому что может произойти потеря данных. Но при выводе на экран переменной b мы видим исходное значение 250.
Если в переменной a будет значение, например, 325. То при выводе на экран переменной b мы видим значение 69! Произошла потеря данных. Число 325 в двоичном виде выглядит следующим образом 101000101 (получается 9 значащих битов). При записи этого значения в переменную b, из 9 битов в одном байте может уместиться только 8 из них, и часть значимой информации будет потеряна. Таким образом в переменной b получится число в двоичном виде 1000101, т.е. 69 в десятичной системе исчисления. 43
3. Преобразования с использованием вспомогательных классов и методов. Примеры:
Пояснения к коду программы: 1 2 3 4 5 6 7 8
string st = "125"; int A = Convert.ToInt32(st); int B = int.Parse(st); int C = A + B; string st1 = Convert.ToString(C); string st2 = C.ToString(); string text = st1 + st2; Console.WriteLine(text);
1) строковая (string) переменная st содержит текстовое значение “125”. 2) в целочисленную (int) переменную A с помощью преобразования из текста “125” методом Convert.ToInt32() записываем число 125. 3) в целочисленную (int) переменную B с помощью преобразования из текста “125” методом int.Parse() записываем число 125. 4) в целочисленную (int) переменную С записываем сумму значений переменных A и B, получится число 250. 5) в строковую (string) переменную st1 с помощью преобразования из числа 250 методом Convert.ToString() записываем текстовое значение “500”. 6) в строковую (string) переменную st2 с помощью преобразования из числа 250 методом ToString() записываем текстовое значение “500”. 7) в строковую (string) переменную text записываем сумму значений переменных st1 и st2, получится текст “250250”. 8) методом Console.WritelLine() выводим значение переменной text на экран.
44
Задачи для самостоятельного решения «Простейшие программы» А 1. Вычислить, какой процент составляет число А от числа В. 2. Заданы координаты трех вершин треугольника. Найти его периметр. 3. Даны два действительных числа. Найти среднее арифметическое этих чисел и среднее геометрическое их модулей. 4. Вычислить значение выражения по формуле: a)
x y xy 12 y 1 34 x
b) x * y
y y x 2 3
5. Дана длина ребра куба. Найти площадь грани, площадь полной поверхности и объем этого куба. 6. Определить периметр правильного n-угольника, описанного около окружности радиуса r. 7. Вычислить длину окружности и площадь круга одного и того же заданного радиуса R. 8. Найти сумму членов арифметической прогрессии, если известны ее первый член, знаменатель и число членов прогрессии. 9. Каменный уголь погрузили в вагон с высотой h м., длиной, а м., шириной b м. Сколько тонн угля погрузили, если удельный вес угля 1.3 т/куб. м? 10.Корова съедает в сутки X кг травы, пастбищный сезон в нашей области длится в среднем 150 суток, примерная урожайность пастбищных культур 250 ц/га. Какова площадь пастбища, необходимая для одной коровы на пастбищный сезон? 11.Вычислить периметр и площадь прямоугольного треугольника по длинам двух катетов а и b. 12.Составить программу вычисления объема цилиндра и конуса, которые имеют одинаковую высоту Н и одинаковый радиус основания R. 13.Дана сторона равностороннего треугольника. Найти площадь этого треугольника, радиусы вписанной и описанной окружностей. 14.Найти площадь кольца, внутренний радиус которого равен г, а внешний – заданному числу R (R > r). 15.Водяной паук строит в воде воздушный домик, перенося на лапках и на брюшке пузырьки атмосферного воздуха, и помещая их под купол паутины. Сколько рейсов нужно сделать пауку, чтобы построить домик объемом A куб. см., если каждый раз он берет В куб. миллиметров воздуха.
45
В 1. Для заданного а вычислить принадлежащий интервалу (, 2) корень уравнения ln(ctg x -1) = a. 2. Смешано а1 литров воды температуры t1 с а2 литрами воды температуры t2. Найти объём и температуру образовавшейся смеси. 3. Треугольник задан длинами сторон. Найти радиусы вписанной и описанной окружностей. 4. Заданы координаты трех вершин треугольника (x1, y1), (x2, y2), (x3, y3). Найти его периметр и площадь. 5. Составить программу для вычисления пути, пройденного лодкой, если ее скорость в стоячей воде V0 км/ч, скорость течения реки V1 км/ч, время движения по озеру t0 ч, а против течения реки – t1 ч. 6. Три сопротивления R1, R2, R3 соединены параллельно. Найдите сопротивление соединения. 7. Треугольник задан величинами своих углов и радиусом описанной окружности. Найти стороны треугольника. С 1. Найти (в градусах) все угла треугольника со сторонами а, b, c. 2. Текущее показание электронных часов: m часов, n минут и k секунд. Какое время будут показывать часы через p ч q мин и r c? 3. Пусть k- целое от 1 до 365. Присвоить целой переменной n значение 1, 2, …, 7 в зависимости от того, на какой день недели (понедельник, …, воскресенье) приходится k- ый день не високосного года, в котором день недели 1 января вводится с клавиатуры (например, понедельник – 1, вторник –2 и т.д.). 4. Составить программу перевода радианной меры угла в градусы, минуты и секунды. 5. Определить h -полное количество часов и m- полное количество минут, прошедших от начала суток до того момента, когда часовая стрелка повернулась на F градусов. 6. По известным трем вершинам прямоугольника (x1, y1), (x2, y2), (x3, y3) найти координаты четвертой вершины.
46
Структурное программирование Понятие “Алгоритм” Алгоритмом называется четкое описание последовательности действий, которые необходимо выполнить для решения задачи. Практически решение любой задачи требует получения результата по заданным исходным данным. То есть можно сказать, что Алгоритм – строго детерминированная последовательность действий, описывающая процесс преобразования объекта из начального состояния в конечное, записанная с помощью понятных исполнителю команд. Само слово происходит от имени учёного аль-Хорезми. Свойства алгоритмов: 1) Дискретность: • алгоритм должен представлять процесс решения задачи как последовательное выполнение некоторых простых шагов. • При этом для выполнения каждого шага алгоритма требуется конечный отрезок времени, то есть преобразование исходных данных в результат осуществляется во времени дискретно 2) Детерминированность: • В каждый момент времени следующий шаг работы однозначно определяется состоянием системы. • Таким образом, алгоритм выдаёт один и тот же результат (ответ) для одних и тех же исходных данных. 3) Понятность: • алгоритм для исполнителя должен включать только те команды, которые ему (исполнителю) доступны, которые входят в его систему команд 4) Завершаемость (конечность): • при корректно заданных исходных данных алгоритм должен завершать работу и выдавать результат за конечное число шагов. 5) Массовость (универсальность): • Алгоритм должен быть применим к разным наборам исходных данных. 6) Результативность: • Завершение алгоритма определёнными результатами.
47
Понятие о структурном подходе Основные структуры алгоритмов – это ограниченный набор блоков и стандартных способов их соединения для выполнения типичных последовательностей действий. Структурное программирование – методология разработки программного обеспечения, в основе которой лежат: 1) строгая типизация данных; 2) представление программы в виде иерархической структуры блоков; 3) использование подпрограмм; 4) проектирование «сверху-вниз»; Предложена эта методология в 70-х годах XX века Э. Дейкстрой, разработана и дополнена Н. Виртом. 1) Строгая типизация заключается в том, что: a) все переменные принадлежат каким-либо типам данных. b) Для изменения типа данных переменной необходимы специальные преобразования/операции. 2) Иерархическая структура программы На основе работ Дейкстры итальянские математики К.Бема и Г.Джакопини доказали следующую теорему: всякая программа может быть построена с использованием только трех управляющих конструкций: a) следования, b) ветвления и c) цикла. 3) Использование подпрограмм Выделение в основной программе самостоятельных частей – подпрограмм. 4) Проектирование «сверху-вниз» или метод пошаговой детализации алгоритма. a) поставленная задача разбивается на две или более подзадач, каждая из которых считается структурным блоком; b) данные структурные блоки соединяются между собой; c) если подзадача является сложной, для ее реализации она разбивается на ряд подзадач и т.д.
48
Оператор блок Составной оператор или блок представляет собой последовательность операторов, заключенных в фигурные скобки {}. Блок обладает собственной областью видимости: объявленные внутри блока имена доступны только внутри данного блока или блоков, вложенных в него. Составные операторы применяются в случае, когда правила языка предусматривают наличие только одного оператора, а логика программы требует нескольких операторов. Использование блоков помогает лучше структурировать программный код. Рассмотрим пример:
Пояснения: 1 int a = 10; 2 { int b = a + 10; 3 4 } 5 { int a = 20; 6 int b = a - 10; 7 8 } 1) объявлена переменная a, которой присвоено значение 10; 2) объявлен первый блок {; 3) в первом блоке объявлена переменная b, которой присвоено значение a -10, этот блок имеет доступ к переменной a, которая объявлена ранее в блоке более высокого уровня (пункт 1); 4) первый блок закрыт } 5) объявлен второй блок {; 6) во втором блоке объявлена переменная a, которой присвоено значение 20, в этой строчке синтаксический анализатор Visual Studio отмечает ошибку, это связано с тем, что во втором облаке остаётся видимой переменная a, объявленная ранее в блоке более высокого уровня (пункт 1); 49
7) во втором блоке объявлена переменная b, которой присвоено значение a-10, в этой строчке ошибки нет, несмотря на то, что переменная b также была объявлена в первом блоке (пункт 3); но в этом случае блоки первый и второй “не пересекаются”, и объявленные в них переменные даже с одинаковыми именами, являются всё же разными переменными! Итак, можно объявлять переменные с одинаковыми именами внутри “непересекающихся” структурных блоков. Все переменные объявленные локально внутри блока, автоматически “уничтожаются” (освобождается занимаемая ими память и зарезервированные имена) в момент его завершения. Переменные объявленные в блоке более высокого уровня доступны во всех вложенных структурных блоках.
50
Основные управляющие конструкции Эдсгер Вибе Дейкстра – нидерландский учёный, труды которого оказали влияние на развитие информатики и информационных технологий; один из разработчиков концепции структурного программирования. На основе работ Дейкстры итальянские математики К. Бема и Г. Джакопини доказали следующую теорему: «всякая программа может быть построена с использованием только трех управляющих конструкций»: 1) следования, 2) ветвления и 3) цикла. Для изображения этих конструкций (следование, ветвление, цикл) на схемах вводится понятие структурного (функционального) блока, который характеризуется конкретным действием, одним ходом и одним выходом. Следование Во всех примерах, которые мы рассматривали ранее, выполнение программы процессором происходило в прямом порядке записи команд (сверху вниз по тексту). Такое исполнение команд называется следование и на схемах обозначается так как на рис. 8.
Рис. 8. Следование означает, что управление передается от одного блока к следующему Однако написание программ с использованием одного только следования команд невозможно. Очень часто при разработке алгоритмов необходимо обеспечить возможность выполнять не все команды или выполнять их не в порядке записи. 51
Ветвление Оператор if Простейшая конструкция, позволяющая нарушить прямой порядок следования команд, называется “ветвление” (по англ. condition statement или selection). Рассмотрим простой пример, демонстрирующий необходимость нарушения прямого порядка команд в программе. Вспомним известную историю о мальчике, которого зовут Том Сойер, однажды тётя в наказание заставила его красить забор. Предположим, что мы пишем программу для робота, который должен помочь Тому в покраске. Инструкции: 1) Опустить кисть в ведро с краской; 2) Провести кистью по доске забора; 3) Сместиться вправо; 4) Опустить кисть в ведро с краской …. и т.д. А теперь представьте, что будет, когда краска в ведре закончится? Робот продолжит выполнять наши инструкции, выполняя напрасный труд с кисточкой без краски. В алгоритме необходимо при определённых условиях отступить от обычного порядка действий, но сохранить возможность продолжить работу в прежнем русле. Для реализации этой идеи во всех современных языках программирования есть специальная синтаксическая конструкция, называемая ВЕТВЛЕНИЕ или “условный оператор”, который используется для разветвления процесса обработки данных на два направления. Изменим наш пример с помощью ветвления. Инструкции: 1) Опустить кисть в ведро с краской; 2) Если краски в ведре нет – сходить в гараж с ведром, набрать краски и вернуться; 3) Провести кистью по доске забора; 4) Сместиться вправо; 5) Опустить кисть в ведро с краской …. и т.д. Ветвление – это разделение алгоритма на два пути (две ветви) по некоторому условию с дальнейшим выходом на общее положение. Ветвление бывает полным (рис. 9) и неполным (рис. 10) и на языке блоксхем это выглядит так:
52
Рис. 9. Полное ветвление
Рис. 10. Неполное ветвление
На языке C# синтаксис неполного ветвления (сокращенная форма оператора if) выглядит следующим образом: if (выражение A) выражение B; Условие – это логическое (может включать арифметическое) выражение A, истинность которого проверяется, а выражение B – оператор: простой или составной. При выполнении сокращенной формы оператора if сначала вычисляется выражение A, затем проводится анализ его результата: если A истинно, то выполняется оператор B; если A ложно, то оператор B пропускается. Таким образом, с помощью сокращенной формы оператора if можно либо выполнить оператор B, либо пропустить его. Пример: 1 2 3 4
int seconds = 54; seconds++; if (seconds >= 59) seconds = 0; Console.WriteLine("{0} секунд", seconds);
1) Объявляем целочисленную переменную seconds и присваиваем ей значение; 2) увеличиваем значение переменной seconds на единицу; 3) проверяем: если значение переменной seconds больше или равно числа 59, то присваиваем значение на ноль; 4) выводим на экран результат. 53
На языке C# синтаксис полного ветвления выглядит следующим образом: if (выражение A) выражение B; else выражение C. При выполнении полной формы оператора if сначала вычисляется выражение A, затем проводится анализ его результата: если A истинно, то выполняется оператор B; если A ложно, то выполняется оператор C. Таким образом, с помощью полной формы оператора if можно либо выполнить оператор B, либо выполнить оператор C. В качестве выражения B и/или выражения C может выступать составной оператор-блок, внутри которого может быть сколько угодно много простых и составных операторов. Рассмотрим пример для демонстрации полного ветвления с оператором блока. Объявим переменную money для хранения информации об имеющейся сумме наличных. Далее в зависимости от этой суммы мы должны выполнить одну из “веток”: если сумма больше нуля, выводим на экран два сообщения: “Деньги есть” и “Идём в магазин”, иначе выводим на экран два других сообщения: “Денег нет” и “Просим перевести средства на карту”. После ветвления выводим сообщение “Гуляем...” (это сообщение будет напечатано при любом значении переменной money).
Сложный алгоритм может состоять из множества соединенных между собой базовых структур. Существует два способа соединения структурных элементов алгоритма: последовательный и вложенный. На рис. 11 два ветвления следуют одно за другим, а на рис. 12 происходит вложение.
54
Рис. 11. Последовательный способ соединения структурных элементов алгоритма Внутри любого блока (S1, S2, S3, S4) также может находиться ветвление как на рис.12.
Рис. 12. Вложенный способ соединения структурных элементов алгоритма
55
Задача «Квадратное уравнение» Простая и известная задача поможет нам поближе познакомиться с ветвлением. Напишем программу для вычисления корней квадратного уравнения и генерации подробного текста с описанием полного решения. Для нахождения корней нам потребуется попросить пользователя ввести коэффициенты уравнения (A, B, C), затем вычислить дискриминант и рассмотреть три варианта решения в зависимости от значения дискриминанта (меньше нуля, равен нулю и больше нуля): Console.WriteLine("Решение квадратного уравнения:"); Console.WriteLine("A*X^2
+
B*X
+ C = 0");
Console.WriteLine("Ввод коэф-в:"); Console.Write("Введите A="); string st = Console.ReadLine(); int A = Convert.ToInt32(st); Console.Write("Введите B="); st = Console.ReadLine(); int B = Convert.ToInt32(st); Console.Write("Введите C="); st = Console.ReadLine(); int C = Convert.ToInt32(st); Console.WriteLine("A={0}, B={1}, C={2}", A, B, C); Console.WriteLine(); Console.WriteLine("Дано:"); Console.WriteLine("{0}*X^2 + {1}*X + {2} = 0", A, B, C); Console.WriteLine("Решение:"); Console.WriteLine("1) Находим Дискриминант"); Console.WriteLine("D = B^2 - 4*A*C"); int D = B * B - 4 * A * C; Console.WriteLine("D = {0}^2 - 4*{1}*{2}", B, A, C); Console.WriteLine("D = {0} - {1}", B * B, 4 * A * C); Console.WriteLine("D={0}", D); // Ветвление на три варианта: if (D < 0) { Console.WriteLine("D < 0 "); Console.WriteLine("Ответ: Действительных корней нет!"); } else if (D == 0) {
56
Console.WriteLine(" D = 0 "); Console.WriteLine("Есть одно решение"); Console.WriteLine("X = - B/(2*A)"); float X = -(float)B / (2 * A); Console.WriteLine("X =-{0}/(2*{1})", B, A); Console.WriteLine("X = {0}/{1}", -B, 2 * A); Console.WriteLine("X = {0}", X); Console.WriteLine("Ответ. X = {0}", X); } Else { Console.WriteLine("D > 0"); Console.WriteLine("Есть два корня"); Console.WriteLine("X1 = (-B - √D) / (2*A)"); Console.WriteLine("X2 = (-B + √D) / (2*A)"); Console.WriteLine("X1 = (-{0} - √{1}) / (2*{2})", B, D, A); float sD = (float)Math.Sqrt(D); Console.WriteLine("X1 = ({0} - {1})/{2}", -B, sD, 2 * A); Console.WriteLine("X1 = {0} / {1}", -B - sD, 2 * A); float X1 = (-B - sD) / (2 * A); Console.WriteLine("X1 = {0}", X1); Console.WriteLine("X2 = ({0} + {1})/{2}", -B, sD, 2 * A); Console.WriteLine("X2 = {0} / {1}", -B + sD, 2 * A); float X2 = (-B + sD) / (2 * A); Console.WriteLine("X2 = {0}", X2); Console.WriteLine("X1={0}
X2={1}", X1, X2);
} Console.ReadLine();
Вот так будет выглядеть результат работы программы:
57
Задача «Три числа по возрастанию» Рассмотрим хорошую задачу для более глубокого понимания управляющей структуры ветвления. Условие задачи: пользователь вводит три числа, необходимо вывести их в порядке возрастания. Как это всегда бывает – у задачи есть несколько вариантов решения, рассмотрим некоторые их них. Для начала напишем программный код для ввода данных пользователем в консоли: Console.WriteLine("ЗАДАЧА: ТРИ ЧИСЛА по ВОЗРАСТАНИЮ"); Console.Write("Введите 1-е число > "); string st = Console.ReadLine(); int a = Convert.ToInt32(st); Console.Write("Введите 2-е число > "); st = Console.ReadLine(); int b = Convert.ToInt32(st); Console.Write("Введите 3-е число > "); st = Console.ReadLine(); int c = Convert.ToInt32(st); Console.WriteLine("вы ввели: {0}, {1}, {2}", a, b, c);
Далее рассмотрим первый вариант решения, в котором будем попарно сравнивать числа друг с другом и вложенный способ соединения структурных элементов алгоритма. Также применим метод вывода на экран промежуточных “диагностических” сообщений для контроля хода выполнения алгоритма:
58
if (a > b) { Console.WriteLine("первое число больше второго"); if (b > c) { Console.WriteLine("второе число больше третьего"); Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", c, b, a); } else { Console.WriteLine("второе число не больше третьего"); if (a > c) { Console.WriteLine("первое число больше третьего"); Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", b, c, a); } else { Console.WriteLine("первое число не больше третьего"); Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", b, a, c); } } } else { Console.WriteLine("первое число не больше второго"); if (a > c) { Console.WriteLine("первое число больше третьего"); Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", c, a, b); } else { Console.WriteLine("первое число не больше третьего"); if (b > c) { Console.WriteLine("второе число больше третьего");
59
Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", a, c, b); } else { Console.WriteLine("второе число не больше третьего"); Console.Write("По возрастанию "); Console.WriteLine("{0} {1} {2}", a, b, c); } } }
Данное решение выводит числа в порядке возрастания на экран, но после выполнения данного кода, если нам потребуется выполнять какие-то действия с этими числами с учётом их упорядочения, мы не сможем этого сделать, поскольку сами переменные остались без изменения со своими исходными значениями. Теперь внесём изменения в программный код решения, минимизируя вывод информации на экран и сохраняя информацию об упорядочении значений в специальные переменные: int min=0, mid=0, max=0; if (a > b) { if (b > c) { min = c; mid = b; max = a; } else { if (a > c) { min = b; mid = c; max = a; } else { min = b; mid = a; max = c; } } } else 60
{ if (a > c) { min = c; mid = a; max = b; } else { if (b > c) { min = a; mid = c; max = b; } else { min = a; mid = b; max = c; } } } Console.Write("по возрастанию "); Console.WriteLine("{0} {1} {2}", min, mid, max);
После выполнения этого кода у нас остаются три переменные min, mid, max, позволяющие использовать далее полученную информацию об упорядочении значений. Рассмотрим второй вариант решения, в котором будем использовать в условии ветвления составное логическое выражение и последовательный способ соединения структурных элементов алгоритма: int min=0, mid=0, max=0; if (a > { max } if (a > { max } if (b > { max } if (b >
b && b > c) = a; mid = b; min = c; c && c > b) = a; mid = c; min = b; c && c > a) = b; mid = c; min = a; a && a > c) 61
{ max = b; mid = a; min = c; } if (c > a && a > b) { max = c; mid = a; min = b; } if (c > b && b > a) { max = c; mid = b; min = a; } Console.Write("по возрастанию "); Console.WriteLine("{0} {1} {2}", min, mid, max);
Ещё один вариант решения – применим функции Min и Max из класса Math, определенного в пространстве имен System. Функция Min позволяет получить меньшее из двух чисел, а функция Max – большее из двух, для определения среднего из двух значений применим нехитрые математические расчёты: int min=0, mid=0, max=0; max = Math.Max(a, b); max = Math.Max(max, c); min = Math.Min(a, b); min = Math.Min(min, c); mid = (a + b + c) - (max + min); Console.Write("по возрастанию "); Console.WriteLine("{0} {1} {2}", min, mid, max);
Данное решение можно записать ещё немного короче: int min=0, mid=0, max=0; int min=0, mid=0, max=0; max = Math.Max( Math.Max(a, b), Math.Max(b, c) ); min = Math.Min( Math.Min(a, b), Math.Min(b, c) ); mid = (a + b + c) - (max + min); Console.Write("по возрастанию "); Console.WriteLine("{0} {1} {2}", min, mid, max);
62
Тернарный оператор «?:» Этот оператор используется для сокращения объема кода. Им можно заменять простые по сложности операторы if-else. Тернарный оператор имеет такую структуру: Логическое_выражение ? выражение1 : выражение2; Сначала вычисляется логическое выражение. Если оно истинно, то вычисляется выражение1, в противном случае - вычисляется выражение2. Пример использования тернарного оператора «?:» int a; Console.WriteLine("Введите число:"); a = Convert.ToInt32(Console.ReadLine()); string resultMessage = a % 2 == 0 ? "чётное" : "нечётное"; Console.Write("Число " + resultMessage);
И этот же пример, но с использованием обычного ветвления (для сравнения): int a; Console.WriteLine("Введите число:"); a = Convert.ToInt32(Console.ReadLine()); string resultMessage = ""; if (a % 2 == 0) resultMessage = "чётное"; else resultMessage = "нечётное"; Console.Write("Число " + resultMessage);
Каскадное ветвление Иногда возникают ситуации, когда в алгоритме необходимо проверить не два варианта значений, а несколько и выполнять различные действия, в зависимости от получаемого результата. Эту задачу можно решить последовательно, вызывая условные операторы и проверяя соответствующие условия. Такая конструкция называется каскадное ветвление. Синтаксис каскадного ветвления выглядит следующим образом: if (выражение A) выражение B; else if (выражение C) выражение D; 63
else if (выражение E) выражение F; и т.д. При выполнении каскадной формы оператора if сначала вычисляется выражение A, затем проводится анализ его результата: если A истинно, то выполняется оператор B; если A ложно, то вычисляется выражение C и проводится анализ его результата. В свою очередь, если C истинно, то выполняется оператор D; если С ложно вычисляется выражение E и т.д. Рассмотрим простой пример. В зависимости от значения переменной number для переменной text выбираем один из нескольких вариантов: int number = 3; string text = ""; if (number == 1) text = "первый"; else if (number == 2) text = "второй"; else if (number == 3) text = "третий"; else if (number == 4) text = "четвёртый"; else if (number == 5) text = "пятый"; else text = "следующий"; Console.WriteLine(text);
В каскадном ветвлении также каждый из операторов может быть составным. Задача «Знаки зодиака» Рассмотрим задачу для практического закрепления каскадного ветвления. Условие задачи: пользователь вводит два числа: номер месяца и день. Необходимо определить соответствующий знак зодиака для получившейся даты. Для начала напишем программный код для ввода исходных данных пользователем в консоли: 64
Console.WriteLine("ЗАДАЧА: Знаки зодиака"); Console.Write("Введите номер месяца > "); string st = Console.ReadLine(); int month = Convert.ToInt32(st); Console.Write("Введите день > "); st = Console.ReadLine(); int day = Convert.ToInt32(st); Console.WriteLine("Вы ввели дату {0:d2}.{1:d2}", day, month);
Далее для решения задачи необходимо ознакомиться с таблицей 16 распределения знаков по датам, например, в википедии. Таблица 16 Знак Овен Телец Близнецы Рак Лев Дева Весы Скорпион Стрелец Козерог Водолей Рыбы
Даты (западная астрология) 21 марта – 19 апреля 20 апреля – 20 мая 21 мая – 20 июня 21 июня – 22 июля 23 июля – 22 августа 23 августа – 22 сентября 23 сентября – 22 октября 23 октября – 21 ноября 22 ноября – 21 декабря 22 декабря – 19 января 20 января – 18 февраля 19 февраля – 20 марта
Символ
После внимательного анализа данных в таблице вы можете увидеть, что в каждом месяце есть два знака зодиака или каждый знак зодиака присутствует в двух соседних месяцах. Запишем один из вариантов решения этой задачи с использованием каскадного ветвления. string zodiac = ""; if (month == 1) { if (day