Понятие объекта, как экземпляра класса
Трудности. До сих пор мы с вами успешно пользовались свойствами и методами различных объектов, не встречая препятствий. Например, методом WriteLine класса Debug или методом Hide объекта Button1. Или свойством Width объекта Button1. Однако существуют объекты, их свойства и методы, которые не позволяют пользоваться ими напрямую, как мы делали до этого.
Попробуем, например, в окне кода изменить размер шрифта для текстового поля. Для этого напишем казалось бы правильный оператор:
TextBox1.Font.Size = 14
Здесь Font – шрифт, а Size – размер. Однако, VB подчеркивает его, а когда мы ставим на него мышь, выдает подсказку Property ‘Size’ is ‘ReadOnly’, что означает «Свойство Size предназначено только для чтения», то есть мы можем его значение видеть, но менять не можем. Почему?
Еще один пример. Попробуем начертить на форме круг. Я знаю, что для этого подходит метод DrawEllipse класса Graphics. Я пробую записать оператор
Graphics.DrawEllipse
но VB после того, как я поставил точку за словом Graphics, мне не предлагает метода DrawEllipse. Вручную писать его тоже бесполезно. Почему не предлагает?
Преодоление. В обоих случаях выход – в создании объектов, являющихся так называемыми экземплярами класса. Но не думайте, что экземпляры класса – это некий аспирин, который пьют только во время болезни. Объекты – экземпляры класса – это мощное и важное средство всего объектного программирования, то, без чего оно потеряло бы весь свой смысл.
Что такое объект – экземпляр класса? Вы видели, как дети в песочнице делают куличики из формочки? Так вот: формочка – это класс, куличик – это экземпляр класса. Формочка – одна, куличиков из нее – много. Или еще: штампуем алюминиевые тарелки. Штамп – это класс, тарелки – экземпляры класса.
Вот более точная аналогия. Скульптор долго создавал и наконец создал статую. Это оригинал статуи. Он один такой. Затем на заводе при помощи простейших приспособлений изготавливают сколько угодно точных копий этой статуи. Оригинал статуи – это класс. Копии – экземпляры класса.
Понятие объекта именно как экземпляра класса настолько вошло в традицию, что когда программист слышит слово «объект», то первое, что он представляет при этом – экземпляр класса. В дальнейшем изложении я тоже буду следовать этой традиции и употреблять термин «объект» именно в этом смысле. Однако нужно помнить, что в программировании слово «объект» используют также и в широком смысле: объектами называют и классы, и структуры и пространства имен и др. Я постараюсь избегать такого употребления.
А сейчас вы увидите и «пощупаете» объекты – экземпляры класса.
Создаем объекты из класса
Вручную. Создайте проект. Разместите на форме несколько кнопок. Знаете, что вы сейчас сделали? Догадайтесь. Ну конечно же, вы создали несколько объектов из класса! Класс – это Button, вот он – в облике кнопки Button в окне Toolbox. А вот созданные вами объекты в облике кнопок на форме – Button1, Button2, Button3. Это – экземпляры класса Button.
Вы видите, что при своем рождении объекты-кнопки одинаковы. Они во многом являются копиями своего класса. Во многом, но не во всем. После рождения вы можете изменять свойства объектов, например, одну кнопку растянуть и покрасить в желтый цвет, другую – уменьшить и покрасить в синий цвет. Однако, яблочко от яблони недалеко падает, поэтому кнопка останется кнопкой, сколько бы вы ни меняли ее внешний вид.
Аналогично вы можете «отштамповать» сколько угодно объектов из класса TextBox и других.
В коде. Объекты из класса можно создавать не только вручную в режиме проектирования, как мы только что сделали, но и запрограммировав это действие в окне кода. Напишите такой код для кнопки, скажем, Button2:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim Кнопка1 As Button
Кнопка1 = New Button()
Кнопка1.Text = "Кнопка №1"
Debug.WriteLine(Кнопка1.Width)
Me.Controls.Add(Кнопка1)
Dim Кнопка2 = New Button()
Кнопка2.Text = "Кнопка №2"
Me.Controls.Add(Кнопка2)
Кнопка2.Location = New Point(0, 50)
End Sub
Запустите проект. Нажмите кнопку Button2 – у вас на форме появятся еще две кнопки. Теперь выполните проект в пошаговом режиме, одновременно читая пояснения.
Пояснения:
Создаваемый объект нужно объявлять. Так, объявляя числовую переменную, вы придумывали ей имя и указывали, чтобы она принадлежала типу, скажем, Double, поэтому писали оператор
Dim Глубина As Double
Согласно этому оператору VB отводил в памяти место под переменную типа Double.
Объявляя объект, вы тоже придумываете ему имя и указываете, чтобы он принадлежал типу, скажем, Button, поэтому пишете оператор
Dim Кнопка1 As Button
Button – это один из многих типов (классов) объектов – элементов управления.
Глубина – это переменная величина, имеющая тип Double, являющийся числом.
Кнопка1 – это переменная величина, имеющая тип Button, являющийся классом.
Говорят, что переменная Кнопка1 имеет объектный тип. Это значит, что ее значением является не число и не строка, а объект – экземпляр класса. Пусть вас не удивляет, что такая сложная вещь, как объект, может быть значением такой простой вещи, как переменная величина. В конце концов, значением переменной величины Город_где_я_сейчас_нахожусь является такая грандиозная вещь, как Москва.
Итак, после выполнения первой строки в процедуре компьютер знает, что переменная Кнопка1 будет кнопкой, но новая кнопка пока еще не создана. Чтобы убедиться в этом, поставьте мышь в окне кода на слово Кнопка1 – всплывет подсказка Кнопка1=Nothing. Слово Nothing по-английски – «Ничто».
Чтобы создать кнопку, вы выполняете оператор
Кнопка1 = New Button()
Слово New вместе с Button означает, что создается НОВЫЙ объект класса Button. В этот момент и «штампуется тарелка». Чтобы убедиться в этом, поставьте мышь на слово Кнопка1 – всплывет подсказка Кнопка1={System.Windows.Forms.Button}. Это означает, что Кнопка1 стала объектом класса Button пространства имен System.Windows.Forms. VB отводит в памяти место под объект класса Button.
Объект создан, но создан только в памяти компьютера, на поверхности формы он пока не виден. Тем не менее, мы уже можем менять его свойства и узнавать их значение:
Кнопка1.Text = "Кнопка №1"
Debug.WriteLine(Кнопка1.Width)
Чтобы кнопка появилась на форме, форма должна «принять ее в свое лоно», добавить (Add) ее в коллекцию своих элементов управления (Controls):
Me.Controls.Add(Кнопка2)
Подробнее о коллекциях см. в 16.2.
(В пошаговом режиме созданная кнопка будет реально видна только после завершения процедуры.)
Создадим теперь из того же класса Button еще одну кнопку. Для краткости оператор объявления и оператор создания объекта объединяем в один:
Dim Кнопка2 = New Button()
Далее аналогично:
Кнопка2.Text = "Кнопка №2"
Me.Controls.Add(Кнопка2)
По умолчанию все кнопки появляются на форме в ее левом верхнем углу, и чтобы новая кнопка не загородила старую, я сдвину ее на 50 пикселей вниз:
Кнопка2.Location = New Point(0, 50)
Смысл этой строки вы поймете позже, а сейчас не будем отвлекаться.
Наши новые кнопки выглядят вполне прилично, но ничего пока не умеют делать. Как научить их работать, написано в 22.13.
Мы сейчас создавали элементы управления. Однако классов в VB гораздо больше, чем элементов управления. Как создавать объекты из них? Это делается в коде и совершенно аналогично тому, как мы делали это только что.
Невидимый код в окне кода – Windows Form Designer generated code
Итак, мы знаем два способа создания объекта из класса: «ручной» и «в коде». Однако первого из них на самом деле нет J, это все одна видимость, обман.
Чтобы проверить это, создайте новый проект (только совсем новый, а не скопированный из старого). Мы в режиме проектирования. Перед нами – квадрат формы Form1. Добавьте кнопку. Запускаем проект – на экране окно с кнопкой – копия формы. Приложение работает: мы можем перемещать окно, изменять его размер, нажимать кнопку и т.д. Парадокс вот в чем. Помните основной принцип работы компьютера? – Компьютер пальцем о палец не ударит без программы, то есть без кода. Но раз приложение работает, значит существует код, управляющий этой работой. Но ведь мы его не писали! Где же он?
Мы делаем единственный возможный вывод: код присутствует, только мы его не видим. Когда вы при помощи Toolbox размещаете на форме кнопку, VB автоматически и невидимо для вас пишет в окне кода программный текст подобный тому, что мы писали в предыдущем подразделе. Он-то, этот невидимый код, и делает все дело – создает кнопку. А все эти щелчки по Toolbox и по форме – так только, для нашего удобства и удовольствия.
Более того. В режиме проектирования форму на экран мы специально не помещали, она сама там появилась, вернее, нам ее подсунул услужливый VB. Форма – такой же объект, как и кнопка. Значит в окне кода должен также присутствовать невидимый код, создающий Form1? Так оно и есть.
Посмотрим на этот невидимый код. Перейдем в окно кода. Вот что мы видим (Рис. 6.1).
Рис. 6.1
Пока немного. Но это код видимый. А нам нужен невидимый. Для этого щелкните по плюсику в малюсеньком квадратике слева от строки Windows Form Designer generated code. Строка эта означает «Код, автоматически создаваемый дизайнером форм Windows». Вы увидите этот невидимый код. Здесь я привожу его, выкинув комментарии и пустые строки:
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Private components As System.ComponentModel.IContainer
Friend WithEvents Button1 As System.Windows.Forms.Button
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Button1 = New System.Windows.Forms.Button
Me.SuspendLayout()
Me.Button1.Location = New System.Drawing.Point(24, 24)
Me.Button1.Name = "Button1"
Me.Button1.TabIndex = 0
Me.Button1.Text = "Button1"
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(192, 77)
Me.Controls.Add(Me.Button1)
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region
Вы видите здесь три непонятного вида процедуры и еще кое-что. Разбираться в этом сложном коде мы не будем. Но на некоторые моменты обратим внимание.
Некоторые операторы почти понятны. Я их выделил полужирным шрифтом Например,
Me.Button1.Text = "Button1"
Вы даже можете в качестве эксперимента изменить в нем текст на кнопке. Но вообще менять что-то в операторах Windows Form Designer generated code не рекомендую. Если вам нужно изменить какие-то свойства формы и элементов управления, делайте это привычным образом через окно свойств. Вы увидите, что при этом изменится и код в Windows Form Designer generated code. А теперь посмотрите, как он дополнится, когда вы при помощи Toolbox разместите на форме еще и текстовое поле с меткой.
В заголовках процедур, в строке #Region и в других местах мы видим минусики в квадратиках. Щелкая по ним, мы скрываем из вида части кода (например, тела процедур), а минусики превращаются в плюсики. Все, как в проводнике Windows. Сделано это для удобства, для экономии места на экране. Щелкнув по минусику в строке #Region, мы скроем весь невидимый код. Пощелкайте.
Итак, мы сейчас увидели код, создающий форму и элементы управления. А где код, управляющий их поведением? Например, тем, что края формы можно таскать мышкой, что кнопка при нажатии слегка меняет вид, что в текстовом поле при нажатии клавиш клавиатуры появляются буквы? Этот код нам не увидеть, он запрятан в классах Form, Button и TextBox. Получается, что класс состоит из кода, управляющего поведением объектов этого класса.
Удобство визуального программирования
Мы, в принципе, показали, что при создании проекта можем в режиме проектирования не пользоваться окном свойств, окном Toolbox и даже вообще не заходить в закладку Form1.vb [design]. Вместо этого достаточно написать правильный код в окне кода и, запустив проект, увидеть правильно работающую форму со всеми нужными элементами управления. Но такой код писать долго и скучно, а для начинающего – еще и непонятно. Удобство визуального программирования в том и состоит, что VB освобождает вас от написания этого кода, заменив его удобными манипуляциями с окном свойств, окном Toolbox и другими окнами и панелями инструментов. А код автоматически создает сам, да еще и прячет его, чтобы не утомлять глаза программиста.
В режиме проектирования VB создает иллюзию существования на экране формы и элементов управления и даже позволяет менять их свойства, создавая видимость того, что у них изменились размеры, цвет и т.д. Но не забывайте, что это одна видимость. никакой формы и элементов управления в режиме проектирования нет. Существуют лишь их удобные видимые образы, как две капли воды похожие на оригиналы. И любая ваша манипуляция в режиме проектирования ничуть не меняет оригинал (которого нет), а меняет лишь невидимый код и видимый образ. Ведь код в окне кода, и видимый, и невидимый, выполняется не в режиме проектирования, а позже – после запуска проекта на выполнение, поэтому и настоящая форма, и настоящие элементы управления по командам кода порождаются и начинают жить реальной жизнью только после запуска проекта, в режиме работы.
Класс – это программа
Вы конечно же знаете общий принцип работы компьютера, заключающийся в том, что все, что компьютер ни делает, он делает по программе. Это значит, что если вы что-то видите на экране или на экране что-то происходит, значит кто-то когда-то написал программу, чтобы это появилось на экране и работало.
Где находится программа, которая приказывает компьютеру в режиме работы при нажатии на кнопку Button1 показывать ее немного «вдавленной»? Она невидимо для нас находится в классе Button (или в одном из его родительских классов, о которых мы с вами поговорим позже). Там же находятся и другие программы, управляющие поведением кнопки. Каждая из кнопок, рожденных из класса Button, управляется одинаковыми программами, а именно теми программами, которые находятся в этом классе.
Где находится программа, которая приказывает компьютеру в режиме работы во время ввода информации в текстовое поле TextBox1 при нажатии на клавиатуре буквы «Ш» показывать на экране в этом поле именно эту букву? Она находится в классе TextBox. Там же находятся и другие программы, управляющие поведением текстового поля. Каждое из текстовых полей, рожденных из класса TextBox, управляется одинаковыми программами, а именно теми программами, которые находятся в этом классе.
То же самое относится и к другим элементам управления и к форме.
Вообще, можно сказать, что класс – это программа. Класс входит в пространство имен, а пространство имен входит в сборку библиотеки классов .NET Framework, расположенную в файле (см. 4.2.4). До сих пор мы пользовались классами, не видя их кода, и дальше мы тоже его не увидим. За ненадобностью. Или потому, что авторы кода не хотят делиться своей интеллектуальной собственностью.
Невидимые объекты
В этой главе мы будем создавать объекты – экземпляры классов из библиотеки классов .NET Framework, которые дают нам возможность рисовать. В дальнейшем мы будем создавать уже собственные классы и их экземпляры. Программист – скульптор. Он долго пишет длинную программу – класс. А затем парой строчек кода он может создать сколько угодно объектов – экземпляров этого класса.
Форма и элементы управления – «видимые» объекты. Почему эти объекты видимы? Потому что в VS содержится и в режиме работы постоянно выполняется программа, которая рисует эти объекты. Как только мы меняем внешний вид объекта, например, размер формы, эта программа автоматически его перерисовывает на экране в соответствии с внесенными изменениями.
Но мы-то уже знаем, что вся механика, все особенности поведения объекта содержатся не в этой простой программе рисования объекта на экране, а в том коде, из которого состоит класс данного объекта. Если убрать эту программу рисования, объект останется, только будет невидим. Вы спросите: какой от него в таком случае толк? А вот, например, мы пользовались проигрывателем Windows Media Player. При воспроизведении музыки мы могли сделать его невидимым и тем не менее музыку слышали. Значит польза была и от невидимого объекта. Или вот хотя бы класс Math, позволяющий нам пользоваться математическими функциями. Он невидим за ненадобностью какой бы то ни было видимости, а польза от него несомненна.
В VB и вообще в Windows присутствует огромное количество невидимых, но очень полезных объектов. Как с ними общаться, если они невидимы? Очень просто:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Double = InputBox("Введите число")
Dim N As Integer = Math.Round(a)
MsgBox(N)
End Sub
В рассмотренном примере мы сообщили невидимому классу Math значение переменной a, задав его при помощи InputBox, а результат увидели при помощи MsgBox. Но чтобы воспользоваться объектом-невидимкой, мы должны знать механику его работы. В случае с классом Math это означает знать его функции. Впрочем, то же верно и по отношению к видимым объектам.
Таким образом, невидимые объекты общаются с нами через текстовые поля, кнопки и другие стандартные элементы VB. А есть невидимые объекты, которые с нами вообще не общаются, а общаются только с другими объектами, помогая им работать. Мы о них можем ничего и не знать.
Объекты – экземпляры класса
Работа с графикой в VB основывается на знании так называемых графических объектов. Поэтому данный раздел будет посвящен не графике, а более близкому знакомству с объектами. Не перепрыгивайте через него: чего хорошего, если вы будете рисовать, не понимая, как вы это делаете?
Класс Graphics
На чем мы можем рисовать в VB? На форме, на кнопке, на текстовом поле, на графическом поле PictureBox, на других элементах управления. Но ведь в коде классов формы и элементов управления нет программ рисования. Вы можете убедиться в этом, поставив точку после слова Button1 и просмотрев открывшийся список. Вы не найдете там ничего, что позволяет рисовать. Для обеспечения рисования в VB имеется специальный класс Graphics, расположенный в пространстве имен System.Drawing. Его код и содержит программы для рисования фигур и для другой работы с графической информацией.
Зайдите в Object Browser (см. 4.2.4) и найдите класс Graphics. Щелкните по нему – в правой панели вы увидите свойства и методы этого класса. Среди них много методов рисования фигур – хотя бы все те, что начинаются на Draw. Прекрасно. Однако, если вы в окне кода напишете слово Graphics и поставите точку, то в открывшемся списке вы этих методов не увидите. Значит, так просто их применять нельзя.
Все богатство возможностей класса рисования Graphics нам станет доступно лишь тогда, когда мы из этого класса создадим объект. Объект, как вы знаете, это «копия», экземпляр класса, обладающий всеми нужными его возможностями. Объект этот невидимый, но нам это безразлично, так как все равно рисовать он будет не «на себе», а на форме или элементе управления.
Существует еще такое требование: форме и каждому элементу управления – свой объект класса Graphics. Это значит, что если мы собираемся рисовать на форме, двух кнопках и на метке, то нам придется создавать 4 объекта класса Graphics. Сделано это потому, что одному объекту рисовать сразу на нескольких разных предметах трудновато, а удобнее настроиться на какой-то один.
Первая нарисованная линия
Задача: Нарисовать на форме отрезок прямой линии синего цвета между двумя точками. Координаты на форме первой точки: x=50, y=20. Координаты второй точки: x=200, y=100.
Создайте проект. Вспомните систему координат (3.5). Прикиньте глазами, где примерно должен проходить отрезок. Разместите на форме кнопку, при щелчке по которой будет нарисован отрезок.
Начинаем с того, что объявляем объект класса Graphics:
Dim Графика_для_формы As Graphics
Теперь нужно этот объект создать. Пока мы знаем, что объекты создаются при помощи слова New. Однако для объектов класса Graphics мы воспользуемся другим способом. У формы и элементов управления есть специальный метод CreateGraphics, при помощи которого каждый элемент управления или форма создает свой собственный, персональный объект класса Graphics.
Графика_для_формы = Me.CreateGraphics
Объект создан. Поскольку он создан методом CreateGraphics, принадлежащим форме, то он сможет рисовать только на ней.
Теперь можно рисовать. Для этого воспользуемся методом DrawLine объекта Графика_для_формы:
Графика_для_формы.DrawLine(Pens.Blue, 50, 20, 200, 100)
Четыре числа в качестве аргументов метода DrawLine – не что иное, как координаты двух точек, между которыми проводится отрезок. А вот про аргумент Pens.Blue поговорим подробнее. Он определяет цвет линии. Blue – это синий цвет. А при чем тут Pens? Поясняю.
Мы уже привыкли, что VB ведет себя словно старый бюрократ. Помните – когда мы хотели кнопку сделать красной, он заставлял вместо Red писать Color.Red (см. 2.1.3)? Это означает, что цвет Red принадлежит в качестве свойства структуре Color. Употребляя аналогию, можно сказать: краски для того, чтобы покрасить форму или элемент управления, мы должны брать из «коробки для красок», которую зовут Color.
Линии фигур VB чертит пером. Существует специальный класс Pens («Перья»), содержащий несколько десятков перьев различных цветов. Запись Pens.Blue как раз и означает взятое из этой «коробки для перьев» перо синего цвета. Попробуйте в качестве первого аргумента метода DrawLine указать не Pens.Blue, а Color.Blue – у вас ничего не получится: цвета одинаковые, да «коробки» разные.
В дальнейшем я более солидно обосную, где и какие выражения можно или нельзя писать. Вот текст процедуры целиком:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Графика_для_формы As Graphics
Графика_для_формы = Me.CreateGraphics
Графика_для_формы.DrawLine(Pens.Blue, 50, 20, 200, 100)
End Sub
Запустите проект, нажмите кнопку. Вот что вы увидите (Рис. 6.2).
Рис. 6.2
Слева сверху первая крайняя точка отрезка, справа снизу – вторая.
Рисуем отрезки, прямоугольники, круги, эллипсы
Поставим задачу нарисовать на форме 7 фигур, как изображено на Рис. 6.3. Числа на рисунке обозначают горизонтальную и вертикальную координаты на форме.
Рис. 6.3
Оба отрезка прямых будем рисовать методом DrawLine.
Прямоугольник и квадрат будем рисовать методом DrawRectangle.
Окружность и эллипсы (сплющенные окружности) будем рисовать методом DrawEllipse.
Вот программа, решающая задачу:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Граф As Graphics = Me.CreateGraphics
'Левый отрезок: 0, 400 - координаты одной из точек отрезка, 300, 0 – другой:
Граф.DrawLine(Pens.Black, 0, 400, 300, 0)
'Правый отрезок:
Граф.DrawLine(Pens.Black, 400, 300, 600, 400)
'Прямоугольник: '400, 300 - координаты левого верхнего угла прям-ка, 200 - ширина пр-ка, 100 - высота
Граф.DrawRectangle(Pens.Black, 400, 300, 200, 100)
'Эллипс, вписанный в этот прямоугольник - параметры те же, что и у прямоугольника:
Граф.DrawEllipse(Pens.Black, 400, 300, 200, 100)
Граф.DrawRectangle(Pens.Black, 200, 300, 100, 100) 'Квалрат
Граф.DrawEllipse(Pens.Black, 400, 0, 200, 200) 'Круг
Граф.DrawEllipse(Pens.Black, 0, 0, 200, 400) 'Высокий эллипс
End Sub
Не забывайте, что после ввода каждого очередного оператора проект нужно запускать и проверять, как он работает.
Пояснения:
Внутри процедуры для рождения объекта класса Graphics достаточно вместо двух операторов
Dim Граф As Graphics
Граф = Me.CreateGraphics
написать один
Dim Граф As Graphics = Me.CreateGraphics
Примечание. В дальнейших примерах я для краткости часто буду опускать операторы, порождающие объект Граф, поэтому, столкнувшись невзначай с именем Граф, знайте, что это всего лишь объект класса Graphics.
Будем называть величины, указанные в скобках за именем метода, параметрами метода. Сравните их с координатами и размерами фигур на рисунке, а также прочитайте комментарии к коду и дальнейшие пояснения.
Отрезки прямых рисуются методом DrawLine. Мы знаем, что отрезок прямой можно построить, если известно положение двух его крайних точек. Они-то и задаются в обращении к методу. Первая пара параметров вслед за указанием пера – координаты одной точки (любой из двух), вторая пара – другой. Первое число в каждой паре - горизонтальная координата, второе число - вертикальная.
Прямоугольники рисуются методом DrawRectangle. Прямоугольник можно построить, если известно положение его верхнего левого угла, ширина и высота. Четыре параметра в скобках вслед за указанием пера как раз и определяют эти величины.
Квадрат, как известно, тоже прямоугольник, поэтому чертится тем же методом.
Эллипсы рисуются методом DrawEllipse. Вокруг каждого эллипса можно описать прямоугольник. В правой нижней части рисунка вы как раз и видите эллипс с описанным вокруг него прямоугольником. Если вы хотите начертить эллипс, вообразите прямоугольник, описанный вокруг него, и параметрами для метода DrawEllipse укажите параметры для рисования этого воображаемого прямоугольника.
Круг – это эллипс, у которого одинаковы ширина и высота, поэтому чертится тем же методом.
Координаты в методах могут быть и отрицательными. В результате вся фигура или ее часть оказывается левей или выше формы и поэтому не видна. Координаты в методах могут быть и слишком большими положительными. В результате вся фигура или ее часть оказывается правей или ниже формы и поэтому не видна.
Задание 1.
Нарисуйте человечка и паровозик, как на Рис. 6.4. Велосипед можно не рисовать. Старайтесь, чтобы у вас вышла полная копия того, что вы видите на рисунке.
Рис. 6.4
Рисуем дуги, сектора и закрашенные фигуры
Поставим задачу нарисовать на форме 8 фигур, таких, как на Рис. 6.5.
Рис. 6.5
Вот программа, решающая задачу:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Граф As Graphics = Me.CreateGraphics
'Закрашенный прямоугольник
Граф.FillRectangle(Brushes.Yellow, 50, 50, 150, 100)
'Закрашенный эллипс
Граф.FillEllipse(Brushes.Yellow, 250, 50, 150, 100)
'Дуга величиной 60 градусов, начиная от 30 градуса:
Граф.DrawArc(Pens.Black, 50, 150, 100, 100, 30, 60)
'Дуга величиной 270 градусов, начиная от 0 градуса:
Граф.DrawArc(Pens.Black, 50, 300, 100, 100, 0, 270)
'Сектор величиной 60 градусов, начиная от 30 градуса:
Граф.DrawPie(Pens.Black, 200, 150, 100, 100, 30, 60)
'Сектор величиной 270 градусов, начиная от 0 градуса:
Граф.DrawPie(Pens.Black, 200, 300, 100, 100, 0, 270)
'Закрашенный сектор величиной 60 градусов, начиная от 30 градуса:
Граф.FillPie(Brushes.Yellow, 350, 150, 100, 100, 30, 60)
'Закрашенный сектор величиной 270 градусов, начиная от 0 градуса:
Граф.FillPie(Brushes.Yellow, 350, 300, 100, 100, 0, 270)
End Sub
Пояснения: Аналогично тому, как для рисования линий фигур вы должны выбрать перо, для рисования закрашенных фигур вы должны выбрать кисть. Подобно тому, как существует класс Pens, содержащий несколько десятков перьев различных цветов, существует и специальный класс Brushes, содержащий несколько десятков кистей различных цветов. Запись Brushes.Blue означает взятую кисть синего цвета.
Обращение к методам рисования закрашенных фигур отличается от обращения к методам рисования таких же незакрашенных фигур только тем, что в скобках вы вместо пера указываете кисть.
Рассмотрим верхние две фигуры рисунка.
Закрашенный прямоугольник рисуется методом FillRectangle.
Закрашенный эллипс рисуется методом FillEllipse.
Что касается дуг и секторов, то если вы не знакомы с градусной мерой угла, вам придется туговато. Напомню для тех, кто ее забыл: разрежьте круглый торт от центра на 360 одинаковых кусков, тогда остренький уголок каждого куска называется градусом.
Обратите внимание, что все три фигуры в нижнем ряду имеют одну и ту же градусную меру. То же можно сказать и про три фигуры в среднем ряду.
Две дуги (на рисунке они слева) рисуются методом DrawArc. Под дугой понимается дуга (кусок) эллипса (окружности). Поэтому первые пять параметров метода имеют тот же смысл, что и первые пять параметров метода DrawEllipse. Но добавляются еще два параметра, задающие в градусах размер и положение дуги на эллипсе (или окружности). На Рис. 6.6 поясняется смысл этих параметров. Конкретно, показана верхняя из двух дуг. Для простоты я выбрал дугу окружности. Углы измеряются по часовой стрелке от направления направо (на восток). Первый из двух параметров указывает начало дуги (30о), второй – длину дуги (60о).
Рис. 6.6
Два сектора (на рисунке они в центре) рисуются методом DrawPie. Все параметры имеют тот же смысл, что и в DrawArc.
Два закрашенные сектора (на рисунке они справа) рисуются методом FillPie. Отличается он от DrawPie только наличием кисти вместо пера.
Задание 2.
Нарисуйте цветочек, как на Рис. 6.7.
Рис. 6.7
Подсказка: Нарисуйте в одном месте два прямоугольника: сначала закрашенный, а потом – незакрашенный.
Следите пока, чтобы параметры не были дробными. О причинах такой осторожности я расскажу еще.
Рисуем на нескольких элементах управления
Создадим такой проект (Рис. 6.8).
Рис. 6.8
Слева мы видим текстовое поле (TextBox1), посредине – графическое поле (PictureBox1), справа – кнопки (начиная с Button1 наверху и кончая Button7 внизу). Смысл кнопок во многом ясен из надписей на них.
Вот программа. Ниже – пояснения.
Public Class Form1
Inherits System.Windows.Forms.Form
Windows Form Designer generated code
'Объявляем графические объекты для формы и элементов управления:
Dim Граф_для_формы As Graphics
Dim Граф_для_текстов_поля As Graphics
Dim Граф_для_графич_поля As Graphics
Dim Граф_для_кнопки As Graphics
'Создаем графические объекты для формы и элементов управления:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Граф_для_формы = Me.CreateGraphics
Граф_для_текстов_поля = TextBox1.CreateGraphics
Граф_для_графич_поля = PictureBox1.CreateGraphics
Граф_для_кнопки = Button5.CreateGraphics
End Sub
'Рисуем на форме:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Граф_для_формы.DrawLine(Pens.Black, 0, 0, 3000, 900)
End Sub
'Рисуем на текстовом поле:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Граф_для_текстов_поля.DrawRectangle(Pens.Black, 20, 20, 200, 120)
End Sub
'Рисуем на графическом поле:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Граф_для_графич_поля.FillPie(Brushes.Red, 30, 30, 80, 150, 90, 330)
End Sub
'Рисуем на кнопке:
Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
Граф_для_кнопки.DrawEllipse(Pens.Black, 10, 10, 30, 30)
End Sub
'Стираем с текстового поля:
Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
Граф_для_текстов_поля.Clear(Color.White)
End Sub
' Уничтожаем графические объекты для формы и элементов управления:
Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click
Граф_для_формы.Dispose()
Граф_для_текстов_поля.Dispose()
Граф_для_графич_поля.Dispose()
Граф_для_кнопки.Dispose()
End Sub
End Class
Пояснения. Прежде всего, я объявил графические объекты вне процедур, а не внутри. По той простой причине, что использую каждый графический объект не в одной, а в нескольких процедурах. Иначе графический объект, объявленный внутри одной процедуры, не был бы «виден» из других (подробнее о видимости переменных рассказано в 11.3).
Кнопка Button1 создает все графические объекты. Все остальные кнопки заставляют эти графические объекты что-нибудь сделать. Попробуйте, запустив проект, начать работу с нажатия не верхней кнопки, а какой-нибудь другой – VB выдаст ошибку. Действительно: нельзя заниматься графическими работами с нерожденными графическими объектами.
Смысл кнопок с Button2 по Button5 очевиден: каждая из них рисует то, что вы видите на рисунке.
Кнопка Button6 стирает. Для стирания всего, что нарисовано на объекте, существует метод Clear. В нем нужно указать цвет, которым все стирается. Этот цвет является свойством структуры Color. Я выбрал белый цвет. Можно было выбрать любой.
Если вы захотите методом Clear стереть что-нибудь на форме или другом элементе управления, то перед вами встанет вопрос, а какой цвет у этого объекта? Заглянув в окно свойств, вы увидите скорее всего, что это цвет Control. Но в структуре Color такого цвета нет! Что же делать? – Или заранее покрасить форму или элемент управления в другой цвет или почитать о системных цветах в 12.7.1.
Кнопка Button7 уничтожает все графические объекты. Каждый созданный объект, даже если он невидим, расходует ресурсы компьютера или, как нынче говорят, «напрягает» компьютер. Хорошим тоном у программистов является заботиться о компьютере и не перенапрягать его. Поэтому, когда ясно, что объект отработал свое и больше не понадобится, его уничтожают методом Dispose.
Замечания. Структура Color предлагает нам полторы сотни цветов, а окно свойств – гораздо большее количество. В 12.7 я расскажу вам, как пользоваться в коде несколькими миллионами цветов.
Обратите внимание, что рисунок в текстовом окне стирается и тогда, когда вы вручную начнете стирать в нем текст. А рисунок с кнопки стирается тогда, когда вы щелкнете по другой кнопке или по текстовому полю. Если вы, таская форму по экрану, затащите часть ее за край экрана, то все, нарисованное в этой части, сотрется. Если же форма полностью пропадет из вида, то сотрется все. О причинах этого явления и о том, как с ним бороться, написано в 12.5
Подробности о работе объекта класса Graphics. Когда объект класса Graphics создается методом CreateGraphics, он в момент создания запоминает размеры поверхности, на которой будет рисовать. Пусть, например, это форма. Размеры поверхности он запоминает как размеры формы в момент своего создания. Если же мы впоследствии изменим размеры формы, размеры поверхности рисования от этого не изменятся, что может нам не понравиться.
Это легко проверить. Запустите проект. Щелкните по первой кнопке. Объект порожден. А теперь растяните форму и щелкните по второй кнопке. На форме рисуется идущий наискосок отрезок прямой. Судя по координатам, он должен был быть очень длинным, однако он рисуется только в пределах размеров формы, какой она была при щелчке по первой кнопке.
Если вы не хотите задумываться об этой проблеме, объявляйте и создавайте графический объект прямо в той процедуре, которая рисует, как я и делал в этой книге в большинстве программ.
С учетом вышесказанного не советую создавать графический объект для формы в процедуре Form1_Load, то есть когда форма еще не появилась на экране.
Пишем
На форме и элементах управления можно не только рисовать, но и писать, то есть изображать на их поверхности текст. Вы скажете: это не новость, мы уже делали это, устанавливая свойство Text для текстового поля, метки и кнопки. Но это другое. Здесь мы будем обладать свободой писать текст в любой точке поверхности объекта и у нас будет гораздо больше возможностей для его красивого оформления (см., например, рисунки в 12.2.3). Здесь текст будет не задаваться, как мы это делали раньше, а рисоваться
методами объекта класса Graphics, как мы рисуем линии, кружочки и пр.
Продолжим работу над предыдущим проектом. Добавим в него кнопку Button8 «Пишем». По нажатии на нее на поверхности формы и элементов управления должно появиться четыре слова приветствия: "Привет!", "Здравствуй!", "Салют!", "Hello!" (см. Рис. 6.9).
Рис. 6.9
Начнем с того, что в режиме проектирования изменим для кнопки Button5 свойство Font (шрифт): сделаем шрифт пожирней и побольше. Вы видите результат этого изменения на рисунке в виде текста «Рисую на себе». Аналогично немного увеличьте и сделайте курсивным шрифт текстового поля TextBox1. Но это все не то – это все вспомогательные действия в режиме проектирования. Нас интересует рисование текста в коде.
Рисованием текста занимается все тот же объект класса Graphics. Для этого он использует свой метод DrawString («Нарисуй строку»). Как будто бы текст – это та же фигура, только поизвилистей.
Добавьте в проект следующую процедуру:
'Пишем на форме и элементах управления:
Private Sub Button8_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button8.Click
Граф_для_формы.DrawString("Привет!", Button5.Font, Brushes.Red, 70, 280)
Граф_для_текстов_поля.DrawString("Здравствуй!", Me.Font, Brushes.Brown, 50, 100)
Граф_для_графич_поля.DrawString("Салют!", Button5.Font, Brushes.White, 35, 70)
Граф_для_кнопки.DrawString("Hello!", TextBox1.Font, Brushes.Blue, 0, 0)
End Sub
Разберемся, что здесь написано.
Форма и каждый элемент управления пишут на себе своими собственными объектами класса Graphics. В том варианте метода DrawString, который я использую, в скобках вы видите 5 параметров, разделенных запятыми:
1 параметр – это собственно строка текста, которую вы хотите написать.
2 параметр – это шрифт, который мы выбираем для написания строки. Пока я еще не пояснил вам, как в коде свободно и независимо управлять шрифтом, поэтому мы будем пользоваться шрифтами, установленными в режиме проектирования. Так, мы только что в режиме проектирования установили свойства Button5.Font и TextBox1.Font. Свойство Font имеется и у формы (Me.Font), и у других элементов управления. Мы можем в коде свободно пользоваться этими шрифтами, как готовыми перьями или кистями. В процедуре вы видите, что для написания строк на форме и в графическом поле я использовал шрифт кнопки Button5, для написания строки в текстовом поле я использовал шрифт формы, а для написания строки на кнопке – шрифт текстового поля.
3 параметр – кисть. Да, буквы мы пишем кистью, а не пером. Цвет кисти выбираем по вкусу.
4 и 5 параметры – координаты места, в котором мы хотим написать текст, на форме или элементе управления. А чтобы быть более точным, вообразим, что текстовая строка заключена в тесный прямоугольник. Тогда эти два параметра – координаты верхнего левого угла этого прямоугольника относительно верхнего левого угла объекта, на котором пишем.
Пишем строковые выражения. Текстовая строка, являющаяся 1 параметром метода, может быть строковым выражением. Например, фрагмент
Dim a As Integer = 100
Dim s As String = " попугаев"
Граф.DrawString(a & " разноцветных" & s, Me.Font, Brushes.Black, 0, 0)
напишет на форме: 100 разноцветных попугаев
Свободное управление в коде параметрами шрифта отложим до 12.2.3.
Переменные и выражения вместо чисел
В качестве параметров графических (да и других) методов можно употреблять не только числа, как мы делали до этого, но и переменные, и выражения. Приведу два фрагмента, которые рисуют в одном и том же месте один и тот же прямоугольник:
Граф.DrawRectangle(Pens.Black, 50, 20, 200, 100)
и
Dim a As Integer = 50
Dim b As Integer = 20
Граф.DrawRectangle(Pens.Black, a, b, 10 * b, a + a)
Следите только пока, чтобы не было дробных чисел.
Вообще, в будущем, объясняя какой-нибудь новый оператор, я часто буду для простоты ограничиваться коротенькими примерами его записи, как в первом фрагменте. Вы должны знать, что почти везде на месте числа может стоять числовая переменная или арифметическое выражение, на месте строки – строковая переменная или строковое выражение. И вообще, вместо литерала данного типа может стоять переменная или выражение этого типа.
Задача: Построить треугольник по трем его вершинам. Конкретнее: даны координаты трех точек на форме, начертить между этими точками три отрезка прямых. Координаты вводить в 6 текстовых полей. Проект должен выглядеть так, как на Рис. 6.10 (пока не обращайте внимания на правый треугольник).
Рис. 6.10
Придумаем имена переменным – координатам точек: x1, y1, x2, y2, x3, y3.
Создадим 6 текстовых полей для ввода координат и к ним 6 меток. Для удобства переименуем все 6 текстовых полей так: txtX1, txtY1, txtX2, txtY2, txtX3, txtY3. Мы не стали их именовать точно такими же именами, как переменные величины, чтобы имена не перепутались. В таких случаях среди программистов принято начинать имя объектов с приставки (префикса). У нас этим префиксом стал txt.
Вот программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim x1, y1, x2, y2, x3, y3 As Integer
Dim Граф As Graphics = Me.CreateGraphics
'Присваиваем переменным значения из текстовых полей:
x1 = txtX1.Text : y1 = txtY1.Text
x2 = txtX2.Text : y2 = txtY2.Text
x3 = txtX3.Text : y3 = txtY3.Text
'Рисуем треугольник:
Граф.DrawLine(Pens.Black, x1, y1, x2, y2) 'Отрезок между 1 и 2 точками
Граф.DrawLine(Pens.Black, x2, y2, x3, y3) 'Отрезок между 2 и 3 точками
Граф.DrawLine(Pens.Black, x3, y3, x1, y1) 'Отрезок между 3 и 1 точками
End Sub
Разобрались? А теперь усложним задачу: Нарисовать еще один треугольник, точно такой же, но расположенный на 100 пикселей правее и на 30 выше. Для этого в процедуру достаточно добавить такой фрагмент:
'Рисуем правый треугольник:
Граф.DrawLine(Pens.Black, x1 + 100, y1 - 30, x2 + 100, y2 - 30)
Граф.DrawLine(Pens.Black, x2 + 100, y2 - 30, x3 + 100, y3 - 30)
Граф.DrawLine(Pens.Black, x3 + 100, y3 - 30, x1 + 100, y1 - 30)
Здесь каждая координата x увеличена на 100, а каждая координата y – уменьшена на 30, чем и достигается нужный результат.
Попробуйте придавать некоторым точкам координаты, выходящие за пределы формы, а именно большие или отрицательные, и вы увидите на форме только части треугольников.
Замечание: неплохо бы предусмотреть кнопку для стирания с формы.
Во многих будущих проектах вам придется использовать в графических методах переменные. Не всем это будет легко и понятно. Но стараться надо. Без переменных – никуда. И все же, если у вас не будет получаться – не отчаивайтесь: в 8.5 я объясню переход от чисел к переменным более подробно. Так что можете оставить неполучившиеся графические задания на потом. Но не заглядывайте в 8.5 раньше времени, материал этот пока вам будет непонятен.
Примечание: Старайтесь пока, чтобы во время рисования или перед тем ничто не заслоняло поверхность, на которой производится рисование, а то рисунка может не получиться. Особенно любят мешаться под ногами окна InputBox. Вы их сдвигайте подальше от формы, чтобы InputBox форму не заслонял. Потом мы научимся справляться с такими проблемами.
Задание 3.
Вспомним Задание 12: В самом углу прямоугольного двора стоит прямоугольный дом. Подсчитать площадь дома, свободную площадь двора и длину забора. В углу, где дом, забора, естественно, нет. Размеры дома и двора вводим при помощи InputBox. Все числа целые.
Потребуем теперь начертить двор, дом и забор заданных размеров, а результаты с пояснениями написать на форме. Советы: Двор и дом чертите залитыми прямоугольниками разного цвета, а забор – незалитым. Программирование будет легким, если верхние левые углы двора, дома и забора будут находиться в одной и той же точке, например, x=20, y=80.
Методы, «придирчивые» к типу параметров
Попробуйте в предыдущем примере о треугольнике в одном из методов DrawLine в качестве параметра задать дробное число, скажем так:
Граф.DrawLine(Pens.Black, x1 / 3, y1, x2, y2)
или так:
Граф.DrawLine(Pens.Black, x1 + 0.8, y1, x2, y2)
Немедленно VB подчеркнет оператор и выдаст подсказку об ошибке, в которой вы увидите слова Double, Integer и Single. В чем дело? Давайте разберемся.
Причина в том, что каждый метод четко определяет типы своих параметров и не любит другие типы. Как нам узнать нужные?
Первый способ. Поставьте текстовый курсор на название метода в окне кода (пусть это будет DrawLine) и нажмите клавишу F1. Перед вами возникнет окно помощи, в котором вы чаще всего найдете следующий список (Рис. 6.11).
Рис. 6.11
Заголовок означает «Список вариантов метода». Не обращайте пока внимания на слова Overloads Public Sub в начале каждой из 4 интересующих нас строк. Далее в каждой строке идет слово DrawLine, а за ним в скобках – список параметров метода, причем в списке приведены именно типы параметров. Почему строк 4, а не одна? Потому что существует 4 варианта этого метода. Каждый из них чертит одни и те же линии, а отличаются варианты параметрами. В типах верхних двух вариантов мы пока даже не можем разобраться, а вот нижние два для нас имеют смысл. Тип первого параметра – Pen. Мы этот тип пока не проходили, но согласимся. Он нас пока не интересует. Остальные четыре параметра – это, как мы знаем, координаты точек. Мы видим, что они должны иметь тип или Integer, или Single.
Второй способ. Найдите в Object Browser класс Graphics и выделите. В правой панели вы увидите его свойства и методы. Среди них вы найдете и 4 варианта метода DrawLine с указанием параметров (см. Рис. 6.12).
Рис. 6.12
Есть и другие способы.
Причина ошибки. Типы параметров мы узнали. Почему VB выдает ошибку? Потому что результат деления x1/3 имеет, как мы узнали в 5.4.6, тип Double. И результат сложения x1+0.8 тоже имеет тип Double. А нужен Integer или Single. Вот и весь ответ.
Что делать? Способов несколько.
Можно объявить переменные x1, y1, x2, y2 не как Integer, а как Single. Это поможет в случае с делением, так как деление Single на Integer дает Single. А в случае со сложением не поможет, так как сложение Single и Double дает Double.
Можно изменять надлежащим образом тип литералов, ставя в их конец символы типа. Так, x1 + 0.8F вполне удовлетворит VB.
Можно применять функции преобразования типов. Положение спасет, например:
Граф.DrawLine(Pens.Black, CSng(x1 + 0.8), y1, x2, y2)
Можно, в конце концов, отказаться от указания типа при объявлении переменных.
Задание 4.
1. Подготовительная задача. Начертить цилиндр радиуса 100 и высотой 200, такой, как на Рис. 6.13. Высота эллипсов, изображающих основания цилиндра, должна быть в два раза меньше их ширины. Переменными можно не пользоваться.
2. Основная задача. Даны радиус и высота цилиндра. Вычислить объем цилиндра и полную площадь его поверхности. Начертить (это нелегко) цилиндр данного радиуса и высоты. Радиус и высоту вводить в два текстовых поля. Объем и площадь выводить на поверхность формы методом DrawString с 3 знаками после запятой и с текстовыми пояснениями. Высота эллипсов, изображающих основания цилиндра, должна быть в два раза меньше их ширины (см. Рис. 6.13).
Рис. 6.13
Ненадолго расстаемся. Того, чему мы научились, нам вполне хватит для рисования в последующих главах любопытных вещей. Кроме методов рисования рассмотренных нами фигур существуют еще методы рисования многоугольников, кривых Безье, сплайнов и некоторых других. Кроме того, перья, кисти и шрифты гораздо более богаты, чем рассмотренные нами. Мы пока только чуть-чуть прикоснулись к графическим возможностям VB. Дальнейшие возможности графики будут изложены в Глава 12. и Глава 17. . Но не советую прямо сейчас туда отправляться. Будет непонятно.
Графические объекты. Рисуем и пишем.
Изображения на форму и элементы управления можно наносить двумя способами. Можно наносить на них фото или другие изображения, взятые из графических файлов, как мы делали в 3.6 и будем еще делать в 12.3. А можно рисовать на них линии и другие фигуры программным способом, как мы будем делать в этой главе.
Что такое выбор (ветвление)
У начинающего программиста интерес должен вызывать такой вопрос: как компьютер думает, как он принимает решения, как он выбирает, какое действие из нескольких возможных нужно выполнить в данный момент? Ведь до сих пор компьютер выполнял все, что ему приказывали, не рассуждая. Попробую ответить.
Возьмем игру в воздушный бой. Предположим, самолет на экране летит на автопилоте. Это значит, он должен самостоятельно поддерживать высоту полета между 3000 м и 3200 м. Для этого ему достаточно периодически определять высоту, и если она меньше 3000, набирать высоту, а если больше 3200 – снижаться. Естественно, этот выбор делает программа для игры в воздушный бой, сам по себе компьютер ничего выбрать не может. В программе заранее пишутся процедуры для набора высоты и для снижения, а выбор между ними делает специальная команда (оператор) выбора, имеющаяся в каждом языке программирования. Вот алгоритм выбора между набором высоты и снижением:
1.Определи высоту.
2.Если высота < 3000, то выполняй процедуру НАБОР ВЫСОТЫ.
3.Если высота > 3200, то выполняй процедуру СНИЖЕНИЕ.
4.Продолжай полет.
Здесь команды 2, 3 – команды выбора. В общем случае команда выбора содержит условие, от которого зависит, будет ли выполняться какая-нибудь команда или группа команд. Это условие может быть самым разным: нажата или нет любая клавиша, нажата или нет конкретная клавиша, был ли щелчок мышью над таким-то объектом, больше ли одно число другого, правда ли, что с клавиатуры введено такое-то слово и т.д. В нашем случае условие – это высота < 3000 или высота > 3200.
Напишем для примера примитивный алгоритм, позволяющий имитировать вежливое общение компьютера с человеком при включении компьютера:
1.Покажи на мониторе текст "Здравствуйте, я – компьютер, а вас как зовут?"
2.Жди ответа с клавиатуры.
3.Если на клавиатуре человек набрал "Петя" или "Вася", то покажи на мониторе текст "Рад встретиться со старым другом!", иначе покажи на мониторе текст "Рад познакомиться!"
4.Покажи на мониторе текст "Чем сегодня будем заниматься – программировать или играть?"
5.Жди ответа с клавиатуры.
6.Если . . . . . . . . . . . . . .
. . . . . . . . . . . . .
Выбор называют ветвлением по аналогии с разветвляющимся деревом (когда мы забираемся на дерево, мы время от времени делаем выбор, по какой из нескольких веток забираться дальше).
Идею разветвления в программе я изложил. Как видите, команды ветвления довольно просты. Как же с помощью таких простых команд запрограммировать сложное поведение компьютера? Ответ вы найдете в материале этой главы. Добавлю только, что вся мыслительная деятельность во всех
программах (в том числе и в той, что выиграла в шахматы у чемпиона мира Каспарова) осуществляется при помощи таких вот простых команд ветвления (выбора). Только их там много.
Разбираем оператор If на примерах
Выучим сначала три английских слова:
If | читается "иф" | переводится "если" | |||
Then | читается "зэн" | переводится "то" | |||
Else | читается "элз" | переводится "иначе" |
Теперь приведем пример записи нового для вас оператора:
If a=28 Then Debug.WriteLine(f) Else k=44
Переводится он так:
ЕСЛИ a=28 ТО пиши f ИНАЧЕ присвой k значение 44
Другими словами, мы предлагаем компьютеру прежде, чем что-то делать, сначала подумать, правда ли, что a=28 , и если правда, то выполнить оператор Debug.WriteLine(f), в противном случае выполнить оператор k=44. Таким образом, мы с вами впервые написали оператор, при выполнении которого компьютер не просто выполняет, что приказано, а сначала думает и делает выбор (пока одного из двух).
Мы видим, что оператор If включает в себя другие операторы, которые выполняются или не выполняются в зависимости от какого-то условия. Тем не менее, вся эта запись считается одним оператором If. Чтобы привыкнуть к оператору If, рассмотрим пару задач.
Задача 1. Компьютер должен перемножить два числа - 167 и 121. Если их произведение превышает 20000, то компьютер должен напечатать текст ПРОИЗВЕДЕНИЕ БОЛЬШОЕ, иначе текст ПРОИЗВЕДЕНИЕ МАЛЕНЬКОЕ. После этого компьютер в любом случае должен напечатать само произведение.
Программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a, b, y As Integer
a = 167
b = 121
y = a * b
If y > 20000 Then Debug.WriteLine("ПРОИЗВЕДЕНИЕ БОЛЬШОЕ") _
Else Debug.WriteLine("ПРОИЗВЕДЕНИЕ МАЛЕНЬКОЕ")
Debug.WriteLine(y)
End Sub
Пояснение: В процедуре 5 операторов, не считая оператора Dim, последний из них – Debug.WriteLine(y). Поскольку все 5 операторов выполняются по порядку, то он выполнится обязательно. Оператор If у нас – однострочный, пусть вас не вводит в заблуждение то, что он занимает физически две строки, ведь в конце первой из них я поставил знак переноса.
Обязательно выполните эту программу в пошаговом режиме. Обратите внимание, что подсветка после If y > 20000 Then перескакивает на Debug.WriteLine ("ПРОИЗВЕДЕНИЕ БОЛЬШОЕ"), а затем на Debug.WriteLine(y).
Теперь замените в программе a = 167 на a = 1 и снова выполните программу в пошаговом режиме. Обратите внимание, что теперь подсветка после If y > 20000 Then перескакивает на Debug.WriteLine("ПРОИЗВЕДЕНИЕ МАЛЕНЬКОЕ"), а затем уже на Debug.WriteLine(y).
Задача 2. В компьютер вводятся два произвольных положительных числа – длины сторон двух кубиков. Компьютер должен подсчитать объем одного из них – того, что больше по размеру.
Обозначим a1 - сторону одного кубика, a2 - сторону другого, bol - сторону большего кубика, V - объем кубика. Приведем три варианта программы:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a1, a2 As Double
a1 = InputBox("Введите сторону одного кубика")
a2 = InputBox("Введите сторону другого кубика")
If a1 > a2 Then Debug.WriteLine(a1 * a1 * a1) Else Debug.WriteLine(a2 * a2 * a2)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a1, a2, V As Double
a1 = InputBox("Введите сторону одного кубика")
a2 = InputBox("Введите сторону другого кубика")
If a1 > a2 Then V = a1 ^ 3 Else V = a2 ^ 3
Debug.WriteLine(V)
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a1, a2, bol As Double
a1 = InputBox("Введите сторону одного кубика")
a2 = InputBox("Введите сторону другого кубика")
If a1 > a2 Then bol = a1 Else bol = a2
Debug.WriteLine(bol ^ 3)
End Sub
Каждый из вариантов полезен и должен быть вами понят. Вы должны убедиться, что одна и та же задача может решаться разными программами. Если возникают трудности в понимании, то используйте пошаговый режим и следите за значениями переменных. Для каждого варианта пошаговый режим используйте два раза – когда больше первый кубик и когда больше второй кубик.
If без Else. Оператор If можно записывать и без части Else. Например,
If s<t Then w=a+1
Это означает, что если s<t, то нужно выполнить оператор w=a+1, в противном случае ничего не делать, а просто перейти к следующему оператору. Фрагмент
a = 6
If a > 5 Then a = a + 10
Debug.WriteLine(a)
напечатает 16, а фрагмент
a = 2
If a > 5 Then a = a + 10
Debug.WriteLine(a)
напечатает 2.
Еще один пример: Пусть в компьютер вводится слово. Компьютер должен просто распечатать его. Однако, если введенным словом будет «колхозник», то компьютер должен напечатать вместо него слово «фермер».
Вот как будет выглядеть наша программа-«цензор»:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim Slovo As String
Slovo = InputBox("Введите слово")
If Slovo = "колхозник" Then Slovo = "фермер"
Debug.WriteLine(Slovo)
End Sub
Несколько операторов после Then и Else. До сих пор мы после Then и после Else писали только по одному оператору. А если нужно больше?
Задача: Если a не равно 4, выполнить операторы b=3 и Debug.WriteLine(b), а в противном случае - операторы b=0, a=b+5 и с=0.
Вот оператор, решающий эту задачу:
If a <> 4 Then b = 3 : Button4.Width = 50 Else b = 0 : a = b + 5 : c = 0
Как видите, после Then и Else можно писать по нескольку операторов, разделенных двоеточиями. Однако, чаще для этого применяют многострочный If.
Правила записи однострочного оператора If
Любой оператор VB нужно записывать по определенным грамматическим правилам, в противном случае VB выдает сообщение об ошибке. У каждого человеческого языка есть своя грамматика, включающая в себя правила, по которым должны выстраиваться в цепочку слова и другие элементы языка, чтобы получилось правильное предложение. Совокупность этих правил образует часть грамматики, называемую синтаксисом. В языках программирования тоже есть предложения. Такими предложениями являются операторы. Поэтому у языка программирования тоже есть свой синтаксис, определяющий правила, по которым записываются операторы языка и из операторов составляется программа. После того, как человек запускает программу на выполнение, любая порядочная среда программирования прежде, чем действительно выполнять ее, сначала проверит, нет ли в ней синтаксических ошибок, и если есть, то программу выполнять не будет, а выдаст сообщение, указывающее человеку, в чем ошибка. А VB проверяет программу еще на стадии ввода кода.
У VB есть две формы оператора If: однострочная и многострочная. Пока мы пользовались только однострочным If и поэтому приведем правило записи только для него. Приведем это правило в виде так называемой синтаксической схемы:
If условие Then операторы Else операторы
Как понимать эту схему? Ее следует понимать, как образец, шаблон записи оператора, указывающий порядок, в котором оператор записывается из отдельных слов. Слова, которые в схеме я записал жирными буквами, при вводе оператора вы просто вводите один к одному. Вместо слов, которые в схеме записаны курсивом, нужно при записи оператора подставить то, что они означают. Поясним, что обозначают эти слова.
операторы | любой оператор VB или группа операторов, разделенных двоеточиями | ||
условие | пока
под условием будем понимать два арифметических или строковых выражения, соединенных знаком сравнения | ||
знак сравнения | знаков сравнения шесть:
> больше >= больше или равно = равно < меньше <= меньше или равно <> не равно |
Пример: If 5*a+4 <= a*b Then Beep Else a=b+5
Здесь
Beep - один оператор,
a=b+5 - другой оператор,
5*a+4 <= a*b - условие,
5*a+4 - одно выражение,
a*b - другое выражение,
<= - знак сравнения.
Вы уже видели, что однострочный оператор If можно записывать в краткой форме. Вот синтаксическая схема для этой формы:
If условие Then операторы
Таким образом, это уже вторая синтаксическая схема, касающаяся одного оператора. Удобно же весь синтаксис оператора иметь перед глазами в одной схеме. Соединим две схемы в одну. Вот эта схема:
Синтаксическая схема однострочного оператора If:
If условие Then операторы [ Else операторы ]
Квадратные скобки означают, что их содержимое можно писать, а можно и не писать в операторе.
Полезное замечание: Вычисляя выражения, стоящие в условии оператора If, VB не записывает их значения в память. Например, после выполнения фрагмента
b=6 : If b+1>0 Then s=20
в ячейке b будет храниться 6, а не 7. То же относится и к выражениям из оператора Debug.WriteLine. Например:
b=6 : Debug.WriteLine ( b+1)
И здесь тоже в ячейке b останется храниться 6, а не 7. И вообще, информация в ячейках памяти не меняется при вычислении выражений без присваивания.
Еще примеры и задания
Разберите еще несколько примеров работы оператора If:
ФРАГМЕНТ ПРОГРАММЫ | ПЕЧАТЬ | ||
a=10
If a>2 Then Debug.WriteLine("!!!") Else Debug.WriteLine("!") | !!! | ||
a=4
If a>5 Then a=a+10 Else a=a-1 Debug.WriteLine(a) | 3 | ||
s=6
If s-8<>0 Then s=2*s Debug.WriteLine(s) | 12 | ||
s=6
If s<0 Then s=2*s s=s+1 Debug.WriteLine(s) | 7 |
Задание 5.
Определить без компьютера, какие 3 числа напечатает следующий фрагмент:
k = 20 : k = k + 10 : If k + 10 <> 30 Then k = 8 Else k = k - 1
Debug.WriteLine(k)
k = 20 : k = k + 10 : If k + 10 = 30 Then k = 8 Else k = k - 1
Debug.WriteLine(k)
p = 1 : If p > 0 Then p = p + 5
If p > 6 Then p = p + 1
Debug.WriteLine(p)
Задание 6.
В компьютер вводятся два числа. Если первое больше второго, то напечатать их сумму, иначе – произведение. После этого компьютер должен напечатать текст ЗАДАЧА РЕШЕНА.
Задание 7.
В компьютер вводятся длины трех отрезков. Компьютер должен ответить на вопрос, правда ли, что первый отрезок слишком велик, чтобы образовать с другими двумя отрезками треугольник. Указание: Для этого его длина должна быть больше или равна сумме длин двух других отрезков.
В Задание 31 вам будет предложено определить, возможен ли треугольник из этих отрезков, а затем в Задание 35 – нарисовать треугольник по трем сторонам, если он возможен.
Задание 8.
Дракон каждый год отращивает по три головы, но после того, как ему исполнится 100 лет – только по две. Сколько голов и глаз у дракона, которому N лет?
Анализируем свойства объектов. Если в выражения, входящие в условие оператора If, включить свойства объектов, то вы можете заставить компьютер с ними работать. Например, компьютеру нужно определить, видимо или невидимо в данный момент текстовое поле TextBox1. Делает это такой оператор:
If TextBox1.Visible = True Then MsgBox("Видимо") Else MsgBox("Невидимо")
Таким образом, мы расширили понятие условия в операторе If, включив в него сравнение свойства с его значениями. Вы можете писать, например, так:
If TextBox1.Width < 50 Then …
Не все свойства позволяют делать такие сравнения.
Задание 9.
Если кнопка расположена на форме с вашей точки зрения слишком высоко, пусть при нажатии на нее она опустится на 20 пикселей.
Условный оператор If или как компьютер делает выбор
Теперь посмотрим, как писать разветвляющиеся программы на VB.
Функции Rnd и Randomize
Запустите такую программу:
Dim p As Double
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
p = Rnd()
Debug.WriteLine(p)
End Sub
Вот результат:
0,705547511577606
Это случайное число из диапазона от 0 до 1. Вырабатывает это число функция Rnd.
Щелкнем несколько раз по кнопке. Получим серию случайных чисел:
0,705547511577606
0,533424019813538
0,579518616199493
0,289562463760376
0,301948010921478
Завершим работу программы и снова ее запустим. Пощелкаем по кнопке. Получим ту же серию:
0,705547511577606
0,533424019813538
0,579518616199493
0,289562463760376
0,301948010921478
Выходит, что числа хоть и случайные, но после каждого запуска одинаковые. Не очень-то, получается, случайные. Как сделать их разными от запуска к запуску? Добавим кнопку и к ней процедуру с оператором Randomize:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Randomize()
End Sub
После выполнения оператора Randomize числа будут и случайными и разными от запуска к запуску.
Как получить случайное число из диапазона от 0 до 20? | Так – p = 20 * Rnd. | ||
А из диапазона от 6 до 7? | Так – p = 6 + Rnd. | ||
А из диапазона от 200 до 210? | Так – p = 200 + 10 * Rnd. |
Проверьте.
Как получить случайное целое число из диапазона от 200 до 210? Так –
p = Fix (200 + 11 * Rnd)
Функция Fix «отрезает» дробную часть у числа. Подумайте, почему я написал 11, а не 10. Если не можете додуматься, вычисляйте эту формулу по этапам:
s = 11 * Rnd()
t = 200 + s
p = Fix(t)
Debug.WriteLine(s)
Debug.WriteLine(t)
Debug.WriteLine(p)
Запуская этот фрагмент, наблюдайте за значениями s, t и p. Дождитесь, когда s перевалит за 10. Вы сразу все поймете. Если бы я в формуле вместо 11 написал 10, число p равное 210 никогда бы нам не встретилось.
Проект «Звездное небо».
Создайте новый проект с кнопкой. Покрасьте форму в цвет ночного неба. Пусть по щелчку по кнопке на форме рисуется маленькая белая окружность, такая маленькая, что неотличима от точки:
Граф.DrawEllipse(Pens.White, 100, 100, 3, 3)
Поставим задачу нарисовать такую звездочку в случайном месте формы. Посмотрим в окне свойств, чему равны размеры формы. Пусть они равны 500 по горизонтали и 400 по вертикали. Тогда дело решает следующий оператор:
Граф.DrawEllipse(Pens.White, 500 * Rnd(), 400 * Rnd(), 3, 3)
Пощелкайте по кнопке. С каждым щелчком на небе будет зажигаться новая звездочка (Рис. 7.1).
Рис. 7.1
Задание 10.
Создайте проект «Звездное небо в окне» (см. Рис. 7.2).
Рис. 7.2
Задание 11.
«Ловля кузнечика или измеритель шустрости». Создайте проект с большой формой и одной очень маленькой кнопкой. При нажатии на кнопку она должна прыгать в случайное место формы. Вы щелкнули по кнопке – она прыгнула, вы снова поскорее щелкнули – она снова прыгнула, и так далее. Старайтесь щелкать как можно чаще. Можете засечь, сколько раз вам удалось щелкнуть за 1 минуту. Побеждает тот, у кого за 1 минуту кнопка прыгнет наибольшее число раз. (В дальнейшем вы сможете научить компьютер, чтобы он сам засекал время и сам подсчитывал количество нажатий. Кстати, попробуйте опередить книгу и сами организуйте подсчет. В этом вам поможет оператор вида k=k+1.)
Указание: Чтобы кнопка прыгнула в случайное место формы, вам достаточно задать случайные значения двум свойствам кнопки - Left и Top. При этом вы должны добиться, чтобы кнопка не «упрыгивала» с формы. Подсказка: Чтобы можно было играть на форме любых размеров, вам может понадобиться такая, например, случайная величина – Me.Width * Rnd. Только имейте в виду, что размеры формы больше размеров ее рабочего пространства на размеры заголовка и бордюров. Поэтому указанную формулу надо немного подкорректировать в сторону уменьшения.
Задание 12.
«Угадай число» или «Экстрасенс ли вы». Это ваша первая простейшая игра с компьютером. Компьютер загадывает число – 0 или 1. Ваше дело – отгадать. А дело компьютера – сказать «Угадал» или «Не угадал». Некоторые экстрасенсы утверждают, что благодаря сверхчувственному контакту с компьютером они могут из 100 раз угадать 80.
Программа готова? Настройтесь на сверхчувственный контакт! Пуск!
Указание: Здесь вам нужно получить целое число из диапазона от 0 до 1. Получается оно абсолютно по той же методе, что и целое число из диапазона от 200 до 210.
Случайные величины
Без случайных величин компьютер всегда бы, как робот, на одинаковые действия человека реагировал одинаково. Но тогда невозможны были бы игры.
Разбираем многострочный If на примерах
Вспомним недавнюю задачу: Если a не равно 4, выполнить операторы b=3 и Debug.WriteLine(b), а в противном случае - операторы b=0, a=b+5 и с=0. Вот однострочный оператор If, решающий эту задачу:
If a <> 4 Then b = 3 : Debug.WriteLine(b) Else b = 0 : a = b + 5 :
c = 0
Однако, часто количество операторов после Then и Else бывает гораздо большим, да и сами эти операторы бывают гораздо более сложными и длинными. В этом случае строка становится неудобочитаемой, да и вообще не умещается на ширине экрана. Для таких случаев создан многострочный (или блочный) оператор If. Вот как решается наша задача с его помощью:
If a <> 4 Then
b = 3
Debug.WriteLine(b)
Else
b = 0
a = b + 5
c = 0
End If
Конструкция End If означает просто, что в этом месте оператор If заканчивается.
Часть Else может и отсутствовать. Например,
If a <> 4 Then
b = 3
Debug.WriteLine(b)
End If
Самое замечательное в блочном If то, что здесь можно одно за другим проверять несколько условий. Проиллюстрирую на примерах.
Задача: В компьютер вводится число a. Компьютер должен:
Если a<0, сказать «Число отрицательно».
Если a=0, сказать «Вы ввели нуль».
Если a>100, сказать «Число большое».
В остальных случаях компьютер ничего не должен говорить, а только вычислить и напечатать его квадрат.
В любом случае после всего этого компьютер должен сказать «До свидания».
Вот программа:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
a = InputBox("Введите число")
If a < 0 Then
MsgBox("Число отрицательно")
ElseIf a = 0 Then
MsgBox("Вы ввели нуль")
ElseIf a > 100 Then
MsgBox("Число большое")
Else
Debug.WriteLine(a ^ 2)
End If
MsgBox("До свидания!")
End Sub
А вот перевод приведенного оператора If на русский:
ЕСЛИ a < 0 ТО
MsgBox("Число отрицательно")
ИНАЧЕ ЕСЛИ a = 0 ТО
MsgBox("Вы ввели нуль")
ИНАЧЕ ЕСЛИ a > 100 ТО
MsgBox("Число большое")
ИНАЧЕ
Debug.WriteLine(a ^ 2)
КОНЕЦ ОПЕРАТОРА
ElseIf переводят так – «иначе если».
Многострочный If выполняется так: Сначала проверяется первое условие (a < 0). Если оно не выполняется, то VB переходит к проверке второго условия(a = 0), третьего и так далее. Наткнувшись наконец на условие, которое выполняется, VB выполняет операторы, стоящие после его Then и на этом заканчивает работу, даже если ниже есть условия, которые тоже выполняются. Если не выполняется ни одно из условий, VB выполняет операторы, стоящие за Else. Выполнив многострочный If, компьютер переходит к выполнению следующего оператора (у нас это MsgBox("До свидания!")).
Проверьте работу этого оператора в пошаговом режиме 4 раза: для отрицательного, нулевого, положительного и большого положительного значения a. Это нужно для того, чтобы убедиться, что каждая из 4 ветвей оператора работает нормально.
Обратите внимание, что вам нигде не пришлось объяснять компьютеру, что «в остальных случаях» означает на самом деле «между 0 и 100». Все получилось само собой.
Правила записи многострочного If
Вот синтаксис многострочного оператора If:
If условие Then
операторы
операторы
…………….
[ ElseIf условие Then
операторы
операторы
……………. ]
……………………….….
[ Else
операторы
операторы
……………. ]
End If
Ветвей ElseIf может быть сколько угодно или совсем не быть. Ветвь Else может присутствовать, а может отсутствовать. Если она есть, то одна и стоит последней.
Имейте в виду, что у вас нет права и вы не сможете, экономя место по вертикали экрана, объединять строки многострочного оператора If, например, так:
If условие Then операторы ElseIf операторы
и переносить слова Then, ElseIf и другие со своего законного места, например, так:
If
условие
Then
операторы
ElseIf
операторы
Кое в чем вам могут помочь двоеточия.
Задание 13.
Определите без компьютера, что напечатает данная процедура:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Integer = 0
If 3 > 2 Then
a = a + 1
a = a + 2
ElseIf 3 > 2 Then
a = a + 4
End If
a = a + 10
If 3 < 2 Then
a = a + 20
ElseIf 3 > 2 Then
a = a + 40
a = a + 100
Else
a = a + 200
End If
a = a + 500
If 3 < 2 Then
a = a + 1000
ElseIf 3 < 2 Then
a = a + 2000
Else
a = a + 4000
End If
a = a + 8000
Debug.WriteLine(a)
End Sub
Если у вас не сошлось с ответом – это катастрофа, значит вы чего-то не понимаете. В этом случае запустите процедуру в пошаговом режиме, который вам все объяснит.
Задание 14.
Компьютер спрашивает пользователя, как его зовут, а затем приветствует его в соответствии с именем: Колю – «Привет», Васю – «Здорово», Джона – «Hi», а остальных – «Здравствуйте». Для Васи, кроме этого, он красит форму в зеленый цвет.
Задание 15.
Видоизменить диалог с компьютером, начатый в 5.6.2. Пусть компьютер, выяснив в разговоре имя и возраст человека, дальнейшую беседу ведет по двум вариантам. Если возраст больше 17, то компьютер должен задать вопрос: «В каком институте ты учишься?» и получив ответ, глубокомысленно заметить «Хороший институт». Если же возраст меньше или равен 17, то соответственно – «В какой школе ты учишься?» и «Неплохая школа». После этого, каков бы ни был вариант, компьютер должен попрощаться: «До следующей встречи!». Если хотите, можете запрограммировать продолжение разговора.
Ступенчатая запись программы
Возьмем любую программу и посмотрим, как она записана. Конкретнее – обратим внимание на отступы от левого края листа в записи каждой строки. Не будем рассматривать самые верхние и нижнюю строчки в окне кода, смысла которых мы пока не понимаем. Вот пример бессмысленной программы:
Dim a, b, c
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
a = InputBox("Введите число")
If a > 4 Then
b = 3
Debug.WriteLine(b)
Else
b = 0
a = b + 5
c = 0
End If
b = 5
MsgBox("До свидания!")
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
a = 0
b = 0
End Sub
Операторы Dim вне процедур, а также строки начала и конца процедур записываются с одинаковым минимальным отступом. Мы видим, что верхняя процедура состоит из четырех операторов: а=, If, b= и MsgBox. Все они выполняются по порядку, один за другим, поэтому каждый из них записан с одинаковым отступом. Если оператор сложный, то есть включает в себя другие операторы (мы знаем пока один такой оператор - If), то составляющие его операторы записываются еще правее. Так, у нас операторы b=0, a=b+5 и с=0 входят в состав оператора If и должны выполняться по порядку один за другим, поэтому их отступ слева одинаков и больше, чем у If.
Сделано все это для удобства чтения программы, для того, чтобы глаз мог сразу же уловить структуру программы, а именно, из каких частей состоит как сама программа, так и каждый из элементов, ее составляющих. Впрочем, вам с первого взгляда может показаться, что такая запись, наоборот, неудобна для чтения. Однако, заметьте, что она принята во всем мире и глаза профессиональных программистов привыкли именно к ней. Настолько привыкли, что программа, записанная без соблюдения ступенчатого стиля, вызывает раздражение. VB не позволяет нарушать ступенчатый стиль, а вот более ранние версии Бейсика позволяли, и по его отсутствию сразу же можно было определить, что программу писал любитель.
Конечно, допустимы и некоторые отклонения от ступенчатого стиля. Например, как я уже говорил, несколько коротких похожих операторов вполне можно записать в одну строку:
a=0 : b=0 : c=0 : f=4
Этим мы экономим дефицитное место по вертикали экрана или листа бумаги.
Вложенные операторы If
Согласно синтаксической схеме оператора If, после Then и Else могут стоять любые операторы VB, а значит и еще один или несколько If.
Решим задачу: В компьютер вводится число (пусть для конкретности это будет дальность какого-нибудь выстрела). Если оно находится в интервале от 28 до 30, то напечатать текст ПОПАЛ, иначе – НЕ ПОПАЛ.
Сначала составим алгоритм: Введи число. Если оно меньше 28, то печатай НЕ ПОПАЛ, в противном случае надо еще подумать. А о чем же думать? А вот о чем: Если число меньше 30, то печатай ПОПАЛ, иначе печатай НЕ ПОПАЛ.
А теперь по составленному алгоритму напишем программу:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim a As Double = InputBox("Введите дальность выстрела")
If a < 28 Then
MsgBox("НЕ ПОПАЛ")
Else
If a < 30 Then MsgBox("ПОПАЛ") Else MsgBox("НЕ ПОПАЛ")
End If
End Sub
Здесь оператор If a < 30 входит в состав оператора If a < 28. Говорят, что он вложен в него. Естественно, вложенный If вполне и сам может быть многострочным и содержать другие If, вложенные в него. И так далее.
Некоторые программы с вложенными If можно достаточно просто переписать без вложенных If, применяя ветви ElseIf. Вот как мы сделаем это с нашей программой:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a As Double = InputBox("Введите дальность выстрела")
If a < 28 Then
MsgBox("НЕ ПОПАЛ")
ElseIf a < 30 Then
MsgBox("ПОПАЛ")
Else
MsgBox("НЕ ПОПАЛ")
End If
End Sub
Логические операции And, Or, Not
Применение большого числа вложенных If создает довольно громоздкую, трудную для понимания программу. Применение вместо них ветвей ElseIf не всегда делает программу понятнее. Поэтому в VB есть возможность записывать программы короче и понятнее, используя так называемые логические операции. Что это такое, разберем на примерах.
Сформулируем последнюю задачу так: Если число больше 28 и одновременно меньше 30, то печатай ПОПАЛ, в противном случае – НЕ ПОПАЛ.
Обратите внимание, насколько эта формулировка короче и понятнее прежнего алгоритма. Упрощение достигнуто благодаря применению союза «и». В языках программирования в роли этого союза выступает логическая операция And (по-русски – логическая операция И). Вот как с ее помощью записывается наша программа:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a As Double = InputBox("Введите дальность выстрела")
If a > 28 And a < 30 Then MsgBox("ПОПАЛ") Else MsgBox("НЕ ПОПАЛ")
End Sub
Как видите, намного короче.
Обратите внимание, что условие в операторе If уже не такое простое, как мы описывали раньше, а стало таким:
a > 28 And a < 30
Оно состоит из двух условий, между которыми стоит знак логической операции And .
Знак логической операции And, поставленный между двумя условиями, говорит о том, что должны выполняться сразу оба эти условия.
Разберем еще один пример.
Задача «Разборчивая принцесса». В прихожей у принцессы – длинная очередь женихов. Принцессе нравятся только голубоглазые маленького роста. Устав принимать женихов и отбирать из них подходящих, принцесса вместо себя поставила компьютер, написав для него программу, которая говорит OK тем, у кого цвет глаз голубой и одновременно рост меньше 140. Остальным программа говорит BYE.
Вот эта программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Tsvet As String = InputBox("Каков цвет ваших глаз?")
Dim Rost As Integer = InputBox("Введите ваш рост в сантиметрах")
If Tsvet = "Голубой" And Rost < 140 Then MsgBox("OK ") Else MsgBox("BYE")
End Sub
Оператор If в данном примере можно прочесть так – если цвет глаз голубой И рост меньше 140 сантиметров, то говори OK , иначе говори BYE.
Поэтому наш оператор If ответит BYE и высоким голубоглазым, и высоким неголубоглазым, и маленьким неголубоглазым. И лишь маленьким голубоглазым он ответит OK . В общем, And – строгая операция.
Примеры:
ФРАГМЕНТ |
РЕЗУЛЬТАТ |
a=8: b=6: If a>b And b>1 Then k=1 Else k=0 |
k=1 |
a=8: b=6: If a>b And b>7 Then k=1 Else k=0 |
k=0 |
If 8>2 And 3>5 Then k=1 Else k=0 |
k=0 |
If c>d And c<d Then k=1 Else k=0 |
k=0 |
Задача «Неразборчивая принцесса». Неразборчивой принцессе нравятся все маленькие независимо от цвета глаз и все голубоглазые независимо от роста. Программа неразборчивой принцессы будет отличаться от программы разборчивой одним единственным знаком логической операции:
If Tsvet = "Голубой" Or Rost < 140 Then MsgBox("OK ") Else MsgBox("BYE")
Оператор If в данном примере можно прочесть так – если цвет глаз голубой ИЛИ рост меньше 140 сантиметров, то говори OK , иначе говори BYE.
Здесь мы употребили логическую операцию Or (по-русски – логическую операцию ИЛИ).
Поставленный между двумя условиями, знак Or говорит о том, что достаточно, если будет выполняться хотя бы одно из них.
Поэтому теперь оператор If ответит OK и высоким голубоглазым и маленьким голубоглазым и маленьким неголубоглазым. И лишь высоким неголубоглазым он ответит BYE. В общем, Or – добрая операция.
Примеры:
ФРАГМЕНТ |
РЕЗУЛЬТАТ |
a=8: b=6: If a>b Or b>7 Then k=1 Else k=0 |
k=1 |
a=8: b=6: If a<b Or b>7 Then k=1 Else k=0 |
k=0 |
If 1>2 Or 5<4 Then k=1 Else k=0 |
k=0 |
Несколько And и Or. Знаками And и Or можно объединять не только два, а сколько угодно условий. Например:
If a>2 Or x=b Or c<>1 Then k=99 Else k=33
Здесь оператор k=99 выполнится, если верно хотя бы одно из трех условий, и лишь когда все три неверны, будет выполняться оператор k=33. Еще один пример:
If a>2 And x=b And c<>1 Then k=99 Else k=33
Здесь наоборот: оператор k=99 выполнится только тогда, когда верны все три условия, а когда хотя бы одно из них неверно, будет выполняться оператор k=33.
Not. Кроме логических операций And и Or применяется еще логическая операция Not (по-русски – НЕ). Запись
If Not a>b Then...
переводится так –
ЕСЛИ НЕПРАВДА, ЧТО a больше b, ТО....
Вот фрагмент:
a=2: b=3: If Not a>b Then k=1 Else k=0
Здесь выполнится оператор k=1, так как неправда, что 2>3.
Знак логической операции Not, поставленный перед условием, говорит о том, что это условие не должно выполняться.
Задание 16.
Усложним Задание 22 из 7.2.3 о треугольнике: В компьютер вводятся длины трех отрезков. Компьютер должен ответить на вопрос, можно или нельзя из этих отрезков образовать треугольник. Указание: Для этого каждый отрезок должен быть меньше суммы длин двух других отрезков. Напишите 3 варианта программы: без использования логических операций, с использованием логических операций Or, с использованием логических операций And.
Задание 17.
Человек вводит с клавиатуры строку, смысл которой – приветствие при встрече. Компьютер тоже должен ответить приветствием. Отвечать нужно в соответствии со следующей таблицей:
ПРИВЕТСТВИЕ ЧЕЛОВЕКА |
ОТВЕТ КОМПЬЮТЕРА |
Привет |
Привет |
Здравствуйте |
Здравствуйте |
Добрый день |
Салют |
Приветик |
Салют |
Салют |
Салют |
Здравия желаю |
Вольно |
Любое другое приветствие |
Я вас не понимаю |
Используя логические операции, постарайтесь уложиться в один оператор If с 4 ветвями.
Логические выражения
Выражения
a>28
a > 28 And a < 30
Tsvet ="Голубой"
Tsvet ="Голубой" Or Rost<140
a>b-2 Or 5*x+1=Sqrt(b) Or c<>10-y
имеют ту общую черту, что про каждое из них можно сказать, верно оно или нет в каждый момент времени. Такие выражения называются логическими выражениями. Если логическое выражение верно, то говорят, что оно имеет значение True (Истина). Если логическое выражение неверно, то говорят, что оно имеет значение False (Ложь). Любое логическое выражение может стоять в качестве условия в операторе If.
Логические выражения могут быть и более сложными, чем приведенные – содержать одновременно операции And, Or, Not. Например, такое выражение:
a>2 And Not b>3 Or s>8
Чтобы его понять, нужно знать порядок логических действий. В арифметике сначала выполняется умножение, потом сложение. В логических выражениях сначала выполняется Not, затем And, затем Or. Для облегчения понимания не возбраняется расставлять скобки:
(a>2 And (Not b>3)) Or s>8
Это выражение равносильно предыдущему. По-русски его смысл можно выразить так: оно верно тогда, когда или s больше 8 или одновременно a больше 2 и b не больше 3.
Скобки можно расставлять и чтобы изменить порядок действий:
a>2 And Not (b>3 Or s>8)
По-русски смысл этого выражения можно выразить так: оно верно тогда, когда a больше 2 и одновременно неправда, что или b больше 3 или s больше 8.
Логический тип данных Boolean
До этого момента мы с вами были знакомы с двумя видами переменных и выражений: арифметическими (их значение – число) и строковыми (их значение – текстовая строка).
Теперь мы с вами познакомились с логическими выражениями. Существуют и широко применяются в программировании также и логические переменные. Это переменные, которые, как и логические выражения, могут принимать только одно из двух значений: True или False.
Раз есть переменная, должен быть и тип. Объявляются логические переменные так:
Dim a, b As Boolean
Тип данных Boolean (булевский тип) назван так по имени прадедушки логической алгебры, которого звали Буль.
Теперь вы можете писать так:
a = True
или
If b Then …
Зачем это нужно и какая выгода от таких «скучных» переменных, выяснится позже, когда вы будете программировать реальные проекты, например – «Будильник-секундомер» (13.5).
Задание 18.
«Замысловатая принцесса». Определите без компьютера, кто нравится принцессе, по фрагменту из ее программы:
If Tsvet = "Черный" And (Rost < 180 Or Rost > 184) Then MsgBox("OK ") Else MsgBox("BYE")
Задание 19.
Усложним нашу задачу про ПОПАЛ – НЕ ПОПАЛ: Целей для нашего выстрела две. Одна находится в диапазоне дальности 28-30, другая – в диапазоне 65-70. Человек вводит в компьютер число – дальность выстрела. Если снаряд попал в цель, то есть число находится в интервале от 28 до 30 или от 65 до 70, то нужно выдать сообщение ПОПАЛ. Если снаряд упал на расстоянии ближе 10 к любой из целей, то нужно выдать сообщение БЛИЗКО. Кроме этого предусмотрите варианты ПЕРЕЛЕТ, НЕДОЛЕТ, МЕЖДУ ЦЕЛЯМИ и НЕ БЕЙ ПО СВОИМ. Нарисуйте схему выстрела (Рис. 7.3). Горизонтальная линия – земля. Два синих прямоугольника в заданном месте и заданных размеров – это цели. Черный квадратик слева – пушка. Вся картина должна появляться на форме до ввода дальности выстрела, после ввода должен появляться красный. кружочек – место попадания снаряда. Перед рисованием вам придется выбрать масштаб: скажем, 1 к 10. Это значит, что, например, первая цель будет прямоугольником, занимающим пространство на расстоянии от 280 до 300 пикселей от пушки.
Рис. 7.3
Задание 20.
Это задание трудное и необязательное, и поэтому очень полезное. Оно является добавлением к Задание 31 о треугольнике и предназначено для тех, кто чувствует себя уверенно в программировании и геометрии:
В компьютер вводятся длины трех отрезков. Если из этих отрезков можно построить треугольник, постройте его. Или ответьте, что нельзя.
Подсказка: Придется находить координаты вершин треугольника, а для этого надо, возможно, искать высоту треугольника при помощи формулы Герона или же теоремы Пифагора с решением уравнений. Кроме этого рекомендую занести в три дополнительные отдельные переменные размеры самого длинного отрезка и двух других отрезков.
Вложенные операторы If. Логические операции и выражения
Для решения самых сложных логических задач компьютеру вполне достаточно тех операторов If, которые мы разобрали, при условии, что мы будем вкладывать операторы If один в другой.. А применение так называемых логических операций кроме того и значительно упрощает программу.
Оператор варианта Select Case
Суть оператора. У авторов языков программирования есть похвальное стремление сделать язык попроще, попонятнее. Они с ужасом взирают на многочисленные «иначе если» и логические операции и стараются, где можно, от них избавиться. Возьмем, например, такую задачу: Компьютер спрашивает школьника, какую он получил отметку по физике, и реагирует на нее подходящим текстом. Вот программа без нововведений, использующая If:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Otmetka As Integer = InputBox("Какую отметку ты получил по физике?")
If Otmetka = 1 Or Otmetka = 2 Then
MsgBox("Кошмар!")
ElseIf Otmetka = 3 Then
MsgBox("Неважно")
ElseIf Otmetka = 4 Then
MsgBox("Неплохо")
ElseIf Otmetka = 5 Then
MsgBox("Молодец!")
Else
MsgBox("Таких отметок не бывает")
End If
End Sub
Здесь может вызвать раздражение слишком часто встречающееся имя Otmetka, а также то, что и в такой простой задаче не обошлось без логических операций. Хорошо бы программу можно было писать попроще, например, так (по-русски):
Выбери вариант отметки
Вариант 1, 2
говори "Кошмар!"
Вариант 3
говори "Неважно"
Вариант 4
говори "Неплохо"
Вариант 5
говори "Молодец!"
Вариант остальное
говори "Таких отметок не бывает"
Конец выбора
И такой оператор варианта был придуман и назван Select Case, что и означает в переводе ВЫБЕРИ ВАРИАНТ. Теперь я просто-напросто переписываю русский вариант программы по-английски:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim Otmetka As Integer = InputBox("Какую отметку ты получил по физике?")
Select Case Otmetka
Case 1, 2
MsgBox("Кошмар!")
Case 3
MsgBox("Неважно")
Case 4
MsgBox("Неплохо")
Case 5
MsgBox("Молодец!")
Case Else
MsgBox("Таких отметок не бывает")
End Select
End Sub
Логика работы Select Case абсолютно такая же, как и у многострочного If. В процессе исполнения оператора компьютер сравнивает значение переменной Otmetka по очереди со всеми значениями, перечисленными в вариантах, сверху вниз. Наткнувшись на совпадающее значение, он выполняет операторы, стоящие в этом варианте. На этом исполнение оператора Select Case завершается. Если же совпадающего значения так и не нашлось, то выполняются операторы, стоящие в варианте Case Else (в нашей программе он полезен на тот случай, если ученик – угрюмый мечтатель и вводит число 6).
Возможности оператора. Оператор Select Case предоставляет более широкие возможности, чем рассмотренные в только что приведенном примере. Проиллюстрируем их:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim a, k As Double
a = 3
Select Case a * a + 1
Case 8, 4 * a - 3, 26
k = 0
Debug.WriteLine(k)
Debug.WriteLine(a)
Case 7, 9 To 12
k = 1
Debug.WriteLine(k)
Case Is < 0, 2, 4, 2.57 To 1.8 + a, 44, 68, Is > 100 + a
k = 3
End Select
End Sub
Эта программа напечатает 1. Здесь мы видим несколько новых для нас элементов:
После слов Select Case стоит не переменная, а выражение, поэтому с перечисленными в вариантах значениями будет сравниваться число 10, полученное как 3*3+1.
В качестве значений вариантов тоже могут стоять выражения, как, например, у нас – 4*a-3.
Значений вариантов довольно много, все они перечислены через запятые.
Здесь у нас в верхних двух вариантах не по одному, а по нескольку выполняющихся операторов.
Конструкция 9 To 12. Она обозначает то же, что и 9 <= a*a+1 <= 12, и служит в нашем случае для сокращения записи.
Конструкция Is < 0. Она обозначает то же, что и a*a+1 < 0. Слово Is, таким образом, служит заменителем выражения a*a+1 и используется для сокращения. Итак, Select Case не только сравнивает значения на равенство, но и проверяет неравенства.
Здесь отсутствует вариант Case Else. Это значит, что если бы в нашей программе оператор Select Case не выбрал ни один из своих вариантов, то, не найдя также Case Else, он завершил бы свою работу, так ничего и не сделав.
Недостаток оператора. Чем платим за удобство Select Case по сравнению с If? Что может If такого, чего не может Select Case? Самое главное – условия в If могут быть совершенно произвольны, а в Select Case мы привязаны к a*a+1.
Синтаксис оператора Select Case:
Select Case проверяемое выражение
[Case значение, значение……
[операторы
операторы
……………...]]
[Case значение, значение……
[операторы
операторы
……………...]]
…………………….……………….
[Case Else
[операторы
операторы
……………...]]
End Select
Здесь значение – это:
- выражение
- выражение To выражение
- Is знак сравнения выражение
Выражения могут быть не только числовые, но и строковые. Пример:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim a As String = "Дом"
Select Case a + a
Case "Домик"
Debug.WriteLine(44)
Case "ДомДом"
Debug.WriteLine(99)
End Select
End Sub
Здесь будет напечатано 99.
Задание 21.
Ученик вводит с клавиатуры букву русского алфавита. Компьютер должен сказать, какой звук обозначает это буква – гласный, согласный звонкий, согласный глухой или какой-нибудь другой (можно и НЕ ЗНАЮ). Подсказка: Буква – это строка из одного символа.
Задание 22.
Необязательное. Если у вас есть микрофон и вы умеете записывать свой голос в файл, то попробуйте усовершенствовать изученные нами диалоги с компьютером. Пусть компьютер подает вам реплики голосом. Для этого вам заранее придется записать на диск все реплики компьютера в виде звуковых файлов и при помощи операторов Select Case выбирать между ними в зависимости от реплик человека с клавиатуры.
Последний маленький шажок – сделать так, чтобы компьютер правильно реагировал на ваши реплики, подаваемые не с клавиатуры, а с микрофона. Но наука пока не в силах сделать этот маленький шажок.
Проверка ввода чисел в текстовое поле
Давайте запустим наш калькулятор в том виде, который он получил в 5.4.8:
Dim Чис1 As Double 'Переменная, содержащая число из текстового поля Число1
Dim Чис2 As Double 'Переменная, содержащая число из текстового поля Число2
Dim Рез As Double 'Переменная-результат, предназначенный для текстового поля Результат
Private Sub Кл_сложения_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Кл_сложения.Click
Чис1 = Число1.Text 'Значения исходных данных переходят из текстовых полей в переменные
Чис2 = Число2.Text
Рез = Чис1 + Чис2 'Обработка переменных для получения результата
Результат.Text = Рез 'Значение результата переходит из переменной в текстовое поле
End Sub
Попробуем ввести в верхнее текстовое поле вместо числа какую-нибудь ерунду, например, Куку. Щелкнем по кнопке сложения. VB выдает сообщение об ошибке из-за несовпадения типов числовой переменной Чис1 и вводимой текстовой информации. Все верно.
Теперь подумаем – хорошо ли это, что VB реагирует на ваши неправильные действия собственными сообщениями об ошибке? Для вас, которые уже что-то умеют в программировании и делают калькулятор исключительно для собственной пользы и удовольствия, это может быть и хорошо. Ну а представьте, что вы решили похвастаться своим калькулятором перед подружкой, которая знакома с программированием только понаслышке, и она, работая с калькулятором, допустила такую же ошибку – ввела буквы вместо цифр! Что она подумает, увидев неожиданно возникшее окно с непонятными английскими словами? Вы думаете, она поймет, что это сообщение об ошибке? Скорее всего она подумает, что это сломался компьютер. И на какие кнопки, скажите на милость, ей теперь нажимать? "Ну и гадость твой калькулятор!" – скажет она и будет права.
Та же проблема стоит и перед всеми профессиональными программистами, которые создают свои продукты для пользователей, не обязанных ничего знать о программировании. Программист обязан сделать так, чтобы на все неправильные действия пользователя реагировал не VB своими непонятными сообщениями, а сама программа, причем понятно, по-русски, и тут же давала возможность исправиться.
Для этого в VB должны быть средства. И они там есть. Что нам нужно сейчас? Нам нужно средство, которое определяло бы, число ли записано в текстовом поле или же все, что угодно, но только не число. Этим занимается функция IsNumeric. Ее аргумент может иметь любой тип, в том числе и строковый. Функция анализирует строку и если видит, что она состоит из цифр, знака минус и запятой (точки) таким образом, что получается правильное число (и больше ничего в строке нет), то функция получает значение True, что означает "Истина", в противном случае - False, что означает "Ложь". Проверим:
Оператор Debug.WriteLine( IsNumeric("КУ-КУ")) печатает False.
Оператор Debug.WriteLine( IsNumeric("-67,3")) печатает True.
Раз так, то функцию IsNumeric можно включать в логические выражения и использовать в качестве условия оператора If. Например,
If IsNumeric("-67,3") Then MsgBox ("Это число") Else MsgBox ("Это не число")
что означает: "Если -67,3 – число, то …" Получается, что совсем не обязательно писать
If IsNumeric("-67,3") = True Then ….
Алгоритм работы калькулятора с проверкой звучит примерно так: Если в текстовом поле Число1 введено число и в текстовом поле Число2 введено число, то делай все, что положено, иначе выдавай сообщение «Вводите только числа». Процедура сложения с учетом этого алгоритма будет выглядеть так:
Private Sub Кл_сложения_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Кл_сложения.Click
If IsNumeric(Число1.Text) And IsNumeric(Число2.Text) Then
Чис1 = Число1.Text
Чис2 = Число2.Text
Результат.Text = Чис1 + Чис2
Else
MsgBox("Вводите только числа")
End If
End Sub
Здесь я немного сэкономил на переменной Рез. Поскольку мы ее никак не обрабатываем, я обошелся без нее. Процедуры для остальных арифметических действий пишутся аналогично.
Кстати, функция IsNumeric – это метод. А где его хозяин?
Запрет деления на ноль
Вторая ошибка, на которую реагирует VB, это деление на ноль. От этой реакции мы избавимся, если запретим компьютеру делить на ноль, записав вместо оператора
Результат.Text = Чис1 / Чис2
такой:
If Чис2 <> 0 Then Результат.Text = Чис1 / Чис2 Else MsgBox ("На ноль делить нельзя")
Вот как будет выглядеть теперь процедура деления:
Private Sub Кл_деления_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Кл_деления.Click
If IsNumeric(Число1.Text) And IsNumeric(Число2.Text) Then
Чис1 = Число1.Text
Чис2 = Число2.Text
If Чис2 <> 0 Then Результат.Text = Чис1 / Чис2 Else MsgBox("На ноль делить нельзя")
Else
MsgBox("Вводите только числа")
End If
End Sub
Как видите, здесь в состав многострочного If входит однострочный.
Ставим пароль на калькулятор
Ваш калькулятор стал достаточно надежен и удобен. Теперь его не стыдно показать друзьям. Ну а защищаться от врагов будем паролем.
Наша задача – сделать так, чтобы при попытке запустить калькулятор на экране появлялось приглашение ввести пароль, известный только вам. При попытке ввода неправильного пароля, программа должна заканчивать свою работу.
Поскольку приглашение на ввод пароля должно появляться раньше появления калькулятора на экране, то программируем его в процедуре Form1_Load. Предварительно выдумаем сам пароль, например, «калям».
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim Parol As String 'Переменная-пароль
Parol = InputBox("Введите пароль")
If Parol <> "калям" Then MsgBox("Пароль неверный!") : End
End Sub
Новый для вас оператор End делает всего одну вещь – вызывает завершение программы.
Запустите проект и проверьте, как он работает.
То же самое можно было бы запрограммировать короче и без использования переменной:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
If InputBox("Введите пароль") <> "калям" Then MsgBox("Пароль неверный!") : End
End Sub
Вообще, переменные нужны, если необходима неоднократная обработка какой-то информации: сложение чего-то, затем сравнение этого чего-то с чем-то другим и т.д. В нашем же случае пароль нужен всего один раз, так что можно обойтись и без переменной.
Вы скажете: Кто угодно перед запуском моей программы посмотрит в ее текст и сразу же увидит пароль. Совершенно верно. Существует много способов сделать пароль в тексте программы неудобочитаемым (например, соединив его из нескольких переменных, каждая из которых – коротенькая строчка), но самый лучший способ – не показывайте посторонним текст программы, а запускайте исполняемый файл (он находится в папке BIN вашего проекта).
Усовершенствуем пароль. Вы можете использовать пароль и более тонко. Например, пусть враг запустил ваш калькулятор. Он запустился, враг ликует. Но недолго, потому что видит, что кнопки сложения и вычитания неработоспособны. Для этого в процедуре Form1_Load предусмотрите их деактивацию при помощи установки в False их свойства Enabled. Для ввода пароля вы в этом случае используете не InputBox, а дополнительное текстовое поле (TextBox1) и кнопку (Button1) с текстом ОК. Введя пароль, вы щелкаете по кнопке ОК и кнопки сложения и вычитания становятся работоспособными.
Сделаем также так, чтобы в случае ввода пароля в текстовое поле там для секретности появлялись не буквы пароля, а звездочки (как это обычно принято) или любой другой символ. В этом случае никто из-за вашей спины не сможет пароль подсмотреть. Для этого достаточно в режиме проектирования или в коде до ввода пароля установить свойство текстового поля PasswordChar, выбрав в качестве его значения любой символ (в том числе и звездочку):
Textbox1.PasswordChar = "Ж"
Вот нужные две процедуры:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
TextBox1.PasswordChar = "*"
Кл_сложения.Enabled = False
Кл_вычитания.Enabled = False
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.Text = "калям" Then
Кл_сложения.Enabled = True
Кл_вычитания.Enabled = True
Else
MsgBox("Пароль неверный!")
End If
End Sub
Улучшаем калькулятор
Наш калькулятор пока не предохранен от попыток выполнения арифметических действий над текстом вместо чисел и от деления на ноль. Предохраним его от этого, а также научимся ставить пароль на проект.
Функция MsgBox
Простое окно MsgBox. В 3.7 я вкратце описал, как пользоваться окном MsgBox. Здесь я более подробно разберу его действие. Обеспечивает его появление на экране функция MsgBox. Если мы выполним оператор
MsgBox("Привет!")
то увидим на экране такое окно (Рис. 7.4).
Рис. 7.4
Обратите внимание, что кнопка ОК выделена (активна). Это значит, что вместо того, чтобы щелкать по ней мышью, можно просто на клавиатуре нажать клавишу Enter – результат будет тот же.
Точно такое же окно мы увидим, если выполним оператор
y = MsgBox("Привет!")
Поскольку MsgBox, как и всякая функция, имеет значение, оно и будет присвоено переменной y. Что это за значение и зачем оно нужно, мы чуть ниже выясним.
Более сложное окно MsgBox. Рассмотрим следующее окно MsgBox (Рис. 7.5).
Рис. 7.5
Оно отличается от предыдущего количеством кнопок, картинкой и текстом в заголовке. Также активна у нас кнопка No. Задается окно такой функцией MsgBox:
MsgBox ("Хотите знать, сколько будет дважды два?", MsgBoxStyle.YesNoCancel Or _
MsgBoxStyle.Information Or MsgBoxStyle.DefaultButton2, "Ответьте на вопрос")
У функции – 3 параметра, разделенные запятыми:
Первый параметр – текст сообщения «Хотите знать, сколько будет дважды два?».
Третий параметр – текст заголовка «Ответьте на вопрос». Если этот параметр вами не указан, то в заголовок выносится название вашего проекта.
Второй параметр у нас такой:
MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Information Or MsgBoxStyle.DefaultButton2
Он определяет количество и названия кнопок (у нас – MsgBoxStyle.YesNoCancel), внешний вид и поведение окна (у нас – MsgBoxStyle.Information), а также то, какая кнопка из трех будет активной (у нас – MsgBoxStyle.DefaultButton2).
Вам не нужно запоминать их названий. VB сам предложит их вам на выбор во всплывающем списке, как только вы при вводе этой строки в окно кода введете запятую после первого параметра.
Составные части второго параметра отделяются друг от друга знаком логической функции Or. Не буду пояснять, почему. Составных частей может быть одна, две и больше. Они являются значениями перечисления MsgBoxStyle. Смысл трех из них ясен из таблицы:
Кнопки |
|
OKOnly |
Кнопка OK |
OKCancel |
Кнопки OK, Cancel (отменить) |
YesNo |
Кнопки Yes, No |
YesNoCancel |
Кнопки Yes, No, Cancel |
RetryCancel |
Кнопки Retry (попробовать еще раз), Cancel |
AbortRetryIgnore |
Кнопки Abort (отменить), Retry, Ignore (игнорировать) |
Картинки |
|
Critical |
Х – крест |
Question |
? – вопрос |
Exclamation |
! – восклицание |
Information |
i – информация |
Какая кнопка активна |
|
DefaultButton1 |
1-я |
DefaultButton2 |
2-я |
DefaultButton3 |
3-я |
Есть еще несколько любопытных составных частей, на которых я не останавливаюсь.
Если второй и третий параметры функции не указаны, окно имеет такой вид, как на Рис. 7.4.
Нажимаем на разные кнопки. Мы можем, если хотим, задавать действия, которые должны выполниться при нажатии той или иной кнопки в окне MsgBox. Для этого существует перечисление MsgBoxResult. Его значения повторяют названия кнопок в окне: OK, Yes, No, Cancel, Abort, Retry, Ignore. При нажатии на кнопку функция MsgBox принимает соответствующее значение, а значит, мы можем его анализировать оператором If.
Вот пример обработки нажатия на кнопки для нашего окна:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim y = MsgBox("Хотите знать, сколько будет дважды два?", _
MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Information Or MsgBoxStyle.DefaultButton2, _
"Ответьте на вопрос")
If y = MsgBoxResult.Yes Then
Debug.WriteLine("Четыре")
ElseIf y = MsgBoxResult.No Then
Debug.WriteLine("Я вижу, Вы не хотите этого знать!")
ElseIf y = MsgBoxResult.Cancel Then
Debug.WriteLine("Вы, к сожалению, отказались отвечать, нажав кнопку Cancel или крестик")
End If
End Sub
Имейте в виду, что нажатие на крестик в окне MsgBox вызывает ту же реакцию, что и нажатие кнопки Cancel.
Названия кнопок сами по себе не играют никакой роли, их действие, как вы только что убедились, полностью определяется кодом, который вы напишете в процедуре. Однако в среде пользователей и программистов уже утвердились некоторые привычки, которые вам небесполезно знать:
OK означает просто принять сообщение к сведению, Cancel
– отменить намечавшееся действие, Abort – прекратить неудавшуюся попытку (например, когда ваша процедура занимается распечаткой документа на принтере и приходит сообщение «Принтер не готов»), Retry – повторить неудавшуюся попытку, Ignore – проигнорировать предупреждение и продолжать, как ни в чем не бывало, Yes-No – ответить да или нет на вопрос, содержащийся в сообщении, и предпринять соответствующие действия.
Цикл с GoTo. Метки
Посмотрим, как осуществить цикл в VB. Предположим, мы хотим, чтобы компьютер бесконечно повторял выполнение следующего фрагмента:
Debug.Write("Это ")
Debug.Write("тело ")
Debug.WriteLine("цикла")
в результате чего в окне Output мы бы увидели:
Это тело цикла
Это тело цикла
Это тело цикла
Это тело цикла
. . . . . . . .
Если бы операторы VB можно было писать по-русски, то для достижения нашей цели было бы естественно воспользоваться такой конструкцией:
метка m1: Debug.Write("Это ")
Debug.Write("тело ")
Debug.WriteLine("цикла")
иди к оператору, помеченному меткой m1
Здесь мы видим новый для нас «оператор» ИДИ, который выполняется после оператора Debug.WriteLine("цикла") и единственная работа которого заключается в том, чтобы заставить компьютер перескочить к выполнению оператора Debug.Write( "Это "), помеченного меткой m1.
А вот как этот фрагмент выглядит реально на VB (не запускайте его пока):
m1: Debug.Write("Это ")
Debug.Write("тело ")
Debug.WriteLine("цикла")
GoTo m1
Здесь GoTo m1 – оператор безусловного перехода, переводится «иди к», m1: – метка. Метка - это произвольное имя или произвольное не слишком большое целое положительное число. Метка перед оператором должна заканчиваться двоеточием.
Оператор GoTo можно писать в любых местах процедуры и метку можно ставить перед любым оператором процедуры, заставляя компьютер таким образом перескакивать внутри процедуры откуда угодно куда угодно. Правда, в сложных процедурах и внутри сложных операторов эта свобода перескакивания существенно ограничивается, так что я не советую вам врываться снаружи внутрь вложенных операторов, а вот изнутри наружу – пожалуйста. Между процедурами скакать нельзя.
А теперь запустите эту программу, но только в пошаговом режиме. Посмотрите, как заполняется окно Output. Чтобы оно заполнялось быстрее, нажмите клавишу F11 и не отпускайте.
Группа операторов, выполняющихся многократно, называется телом цикла. У нас это все 4 оператора.
Теперь запустите эту программу обычным
Теперь запустите эту программу обычным образом (не в пошаговом режиме). Через некоторое время перед вами должен встать жизненно важный вопрос – когда же она остановится? А никогда! Вы видите, что окно Output лихорадочно заполняется текстом, а белка в колесе и не думает уставать.
Вы обнаружите, что кнопки и другие элементы управления на форме не отзываются на нажатие мыши, и вообще, до программы «не докричишься». Любопытно, что так «глохнет» любая нормальная программа в процессе выполнения операторов кода. И все ваши прежние программы тоже так «глохли». Но вы этого не замечали по той простой причине, что весь код их процедур выполнялся очень быстро. Не успели вы нажать на кнопку, а все уже выполнилось и снова программа реагирует на ваши действия.
В нормальной программе, написанной без ошибок, исключены ситуации, когда код выполняется бесконечно или на протяжении слишком долгого времени. Если же вы допустили ошибку и в программе выполняется бесконечный цикл, то возникает как раз такая ситуация. Вы вечно будете смотреть на экран, по которому бесконечно бегут непонятные числа или слова или рисуются бесконечные вереницы графических фигур, а возможно и ничего не происходит – все зависит от характера программы. Говорят, что программа зациклилась.
Для прерывания работы программы, в том числе и зациклившейся, вы нажимаете кнопку Stop. Можно щелкнуть и по крестику на форме, но в этом случае VB подвергнет вас небольшому допросу.
Примеры
Все нижеприведенные примеры прогоните в обычном и в пошаговом режиме.
Пример 1. Определите без компьютера, что напечатает фрагмент:
Dim a = 100
GoTo 8
a = a + 40
Dim k = 7
Debug.Write(a)
8: a = a + 2
k = k + 10
Debug.Write(a)
Этот фрагмент напечатает 102. Операторы выполняются в такой последовательности:
Dim a = 100
GoTo 8
a = a + 2
k = k + 10
Debug.Write(a)
А операторы
a = a + 40
Dim k = 7
Debug.Write(a)
выполнены не будут вообще, несмотря на то, что написаны. Цикла здесь нет.
Пример 2. Печатать числа 0, 1, 2, 3 . . . и так без конца.
Вот программа:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim n As Long = 0
m1: Debug.Write(n & " ")
n = n + 1
GoTo m1
End Sub
Здесь в оператор Debug.Write(n & " ") вставлена печать пробела, чтобы как-то отделить друг от друга числа в одной строке.
Обратите внимание, что когда компьютер, скажем, в 24-й раз выполняет тело цикла, значение n как раз равно 24. Значит, зная в любой момент значение n, мы можем сказать, в какой раз выполняется тело цикла (еще говорят: какая итерация цикла выполняется). Переменная, обладающая таким свойством, называется счетчиком циклов.
Пример 3. Выводить числа 0, 1, 2, 3 . . . , но не на печать, а в элемент управления Label (что весьма приятно наблюдать).
Вот программа:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim n As Long = 0
m1: Label1.Text = n
Label1.Refresh()
n = n + 1
GoTo m1
End Sub
Здесь новостью для нас является оператор Label1.Refresh(), который можно перевести так – «перерисовать, обновить, освежить Label1». Без него вы не увидели бы в Label1 никаких новых чисел, информация в ней просто не обновлялась бы. Метод Refresh подходит в некоторых случаях и к другим элементам управления.
Пример 4. Печатать числа 200, 205, 210, 215 . . . и так без конца.
Вот программа:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim n As Long = 200
m1: Debug.Write(n & " ")
n = n + 5
GoTo m1
End Sub
Задание 23.
Определить без компьютера, что будет печатать программа:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
Dim n As Integer = 10
Dim k As Integer = 0
Debug.WriteLine("Считаем зайцев")
met5: Debug.Write(n)
n = n + k
GoTo m1
n = n + 1
m1: Debug.WriteLine(" зайцев")
k = k + 1
GoTo met5
Debug.Write("Посчитали зайцев")
End Sub
Если не можете определить – посмотрите в пошаговом режиме.
Движение объектов по экрану
Вам уже приходилось заставлять кнопки прыгать по экрану. Попробуем добиться плавного движения объекта. Создайте проект с большой формой и добавьте в него маленький элемент управления PictureBox. Поместите его в левой части формы. Придайте ему картинку (свойство Image). Лучше всего, пока вы еще не умеете работать с изображениями, в качестве картинки взять один из файлов значков (иконок), находящихся по адресу
Program Files\Microsoft Visual Studio .NET\Common7\Graphics\icons
Напишем программу, которая двигала бы картинку плавно направо:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
m: PictureBox1.Left += 2 ’Значение PictureBox1.Left увеличивается на 2 пикселя
GoTo m
End Sub
Если движение получилось слишком медленным, то прибавьте шаг – напишите 5 вместо 2. Если слишком быстрым, то уменьшите – 1.
Чтобы сделать движение еще более медленным, вам придется сделать шаг меньшим 1. Но в данной программе это не сработает, так как значения свойства Left имеют тип Integer и поэтому все равно будут округляться или до 1, или до 0. Придется использовать переменную величину дробного типа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim x As Double
x = PictureBox1.Left 'Компьютер узнает, откуда начинать движение
m: PictureBox1.Left = x 'Изображение встает на место, указанное гориз. координатой
x += 0.0001 'Компьютер увеличивает в уме горизонтальную координату
GoTo m
End Sub
Не удивляйтесь, что вам не пришлось в цикле рисовать и стирать объект. Когда дело касается элемента управления, заботы о перерисовке берет на себя VB. А вот когда вы сами будете рисовать различные фигуры на форме и попытаетесь их двигать, тогда вам придется их и рисовать и стирать.
Задание 24.
Напишите 4 процедуры, в которых заставьте картинку или кнопку двигаться налево, вниз, вверх, наискосок.
Что дальше? Пока мы никак не можем влиять на полученное движение. Только можем останавливать программу. Как с помощью мыши или клавиатуры влиять на движение во время движения? Как хотя бы запрограммировать остановку в нужном месте? Об этом позже.
Оператор перехода GoTo. Цикл. Метки
Оператор GoTo является одним из самых низкоуровневых средств современных языков программирования и на практике им почти не пользуются. Но сам компьютер «думает» именно на низком уровне, и поэтому настоящий программист должен быть с GoTo «на ты».
Выход из цикла с помощью If
Бесконечный цикл. Начнем с привычного. Задача: При помощи цикла печатать такие текст и числа:
Начало счета 3 5 7 9 11 13 15 . . .
Для краткости я во многих программах не буду показывать строки объявлений, заголовков и завершения процедур, а также строку, импортирующую класс Debug. Так что останется одна «суть». Не обращайте также пока внимания на непонятные слова (Do…Loop) в заголовках таблиц, эти слова понадобятся чуть позже. Вот вариант программы. Я назвал его нулевым:
0 ВАРИАНТ (Do …. Loop) | |
Write("Начало счета ")
f = 3 m: Write(f & " ") f = f + 2 GoTo m |
Выход из цикла. Теперь вопрос: как нам запрограммировать завершение работы цикла, чтобы он не выполнялся бесконечно? Для этого нужно применить оператор GoTo внутри оператора If.
Задача: При помощи цикла напечатать такие текст и числа:
Начало счета 3 5 7 9 11 13 15 . . . . 329 331 333 Конец счета
Для удобства отладки изменим условие задачи – напечатать:
Начало счета 3 5 7 9 Конец счета
Вы уже достаточно опытны в программировании, чтобы догадаться, что программа для измененного условия будет копией программы для исходного, за исключением одного числа.
Вот 4 варианта программы. Первый – самый простой, а остальные нам понадобятся в дальнейшем. Все 4 варианта делают одно и то же, они очень похожи, но чем-то и отличаются. Вот в этом отличии вам и надо разобраться, иначе не будет понятен дальнейший материал.
Создайте проект с 4 кнопками и выполните в пошаговом режиме все 4 варианта:
1 ВАРИАНТ (Do …. Loop While) | 2 ВАРИАНТ (Do …. Loop Until) | ||
Write("Начало счета ")
f = 3 m: Write(f & " ") f = f + 2 If f <= 9 Then GoTo m Write("Конец счета") | Write("Начало счета ")
f = 3 m1: Write(f & " ") f = f + 2 If f > 9 Then GoTo m2 Else GoTo m1 m2: Write("Конец счета") |
Вот в каком порядке выполняются операторы программы 1 варианта:
Write("Начало счета") f=3 Write(f & " ") {печатается3} f=f+2 {f становится равным 5} If f<=9 Then GoTo m Write(f& " ") {печ. 5} f=f+2 {f = 7} If f<=9 Then GoTo m Write(f & " ") {печ. 7} f=f+2 {f = 9} If f<=9 Then GoTo m Write(f & " ") {печ. 9} f=f+2 {f = 11} If f<=9 Then GoTo m Write("Конец счета")
Здесь оператор GoTo m выполняется три раза. На четвертый раз условие f<=9 оказывается ложным и поэтому выполняется не GoTo m, а следующий за If оператор Write("Конец счета"), то есть программа выходит из цикла и завершает свою работу.
3 ВАРИАНТ (Do While …. Loop) |
4 ВАРИАНТ (Do Until …. Loop) |
Write("Начало счета ") f = 3 m1: If f <= 9 Then GoTo m3 Else GoTo m2 m3: Write(f & " ") f = f + 2 GoTo m1 m2: Write("Конец счета") |
Write("Начало счета ") f = 3 m1: If f > 9 Then GoTo m2 Else GoTo m3 m3: Write(f & " ") f = f + 2 GoTo m1 m2: Write("Конец счета") |
Задача: Напечатать пары чисел - 0 1000 1 999 2 998 . . . . . . 1000 0.
Программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim f, s As Integer
f = 0
m: s = 1000 - f
Write(f & " " & s & " ")
f = f + 1
If f <= 1000 Then GoTo m
End Sub
Задание 25.
А. Напечатать 1 2 3 4 . . . 99 100
Б. Напечатать 100 99 . . . 3 2 1
В. Напечатать 1 2 3 4 . . . 99 100 99 . . . 3 2 1
Задача «Таблицы Брадиса» или «Таблицы логарифмов». В те далекие времена, когда калькуляторов еще не было, были такие напечатанные в виде брошюрок таблицы для школьников и студентов, по которым они могли быстро посмотреть численные значения квадратов, логарифмов и других математических функций. Поставим задачу вычислить и напечатать таблицы Брадиса. На первый раз достаточно вычислить и напечатать с 6 десятичными знаками квадраты и логарифмы чисел 0.001 0.002 0.003 . . . 0.999 1.000.
Программа:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a, Квадрат, Логарифм As Decimal
a = 0.001
m: Квадрат = a ^ 2
Логарифм = Math.Log(a)
Debug.WriteLine(Format(a, "0.000") & " " & Format(Квадрат, "0.000000") _
& " " & Format(Логарифм, "0.000000"))
a = a + 0.001
If a <= 1 Then GoTo m
End Sub
Почему я объявил a как Decimal, а не Double? Причина в незначительных погрешностях, которые допускает компьютер при действиях с десятичными дробями (о чем я уже писал). На моем компьютере при многократном прибавлении 0.001 значение a типа Double на некотором этапе переставало быть точным. Конкретнее, у меня получалось вот что:
0,682 + 0,001 = 0,683000000000001
Вследствие этого, при дальнейшем нарастании а последнее сложение было таким:
0,999000000000001 + 0,001 = 1,000000000000001
Легко видеть, что в этом случае для a=1 задание не выполняется, так как неправда, что
1,000000000000001 <= 1
Тип же Decimal, как я уже писал, обеспечивает абсолютную точность сложений и вычитаний.
Можно было обойтись и типом Double, но в этом случае оператор If пришлось бы чуть-чуть изменить, например, так:
If a < 1.00001 Then GoTo m
Задание 26.
Для х=2700, 900, 300, 100 . . . и т.д. вычислять и печатать y=x/4 + 20 и z=2y+0.23 до тех пор, пока yz не станет меньше 1/х.
Задание 27.
Пусть движущееся изображение, описанное в 8.1.4, через некоторое время остановится.
Задание 28.
Изображение, пройдя немного слева направо, поворачивает вниз и, пройдя немного, через некоторое время останавливается.
Оператор Do …. Loop
Попытаемся составить с использованием 0 варианта оператора Do программу решения задачи о бесконечной печати чисел 3 5 7 … из предыдущего подраздела. Для того, чтобы точно определить работу этого варианта оператора Do, приведем ее параллельно с 0 вариантом программы решения этой задачи из того же подраздела. Работают эти параллельные варианты абсолютно одинаково. При этом объяснением любого оператора в правом столбце является оператор, стоящий в той же строчке в левом столбце.
0 ВАРИАНТ | 0 ВАРИАНТ ОПЕРАТОРА Do | ||
Write("Начало счета ") | Write("Начало счета ") | ||
f = 3 | f = 3 | ||
m: | Do | ||
Write(f & " ") | Write(f & " ") | ||
f = f + 2 | f = f + 2 | ||
GoTo m | Loop |
Do можно перевести, как «Делай», а понимать следует просто как метку.
Loop можно перевести, как «Петля» или «Возврат назад», а понимать следует так: «Возвращайся к метке Do».
Порядок работы обеих программ совершенно одинаков, так что можно считать слово Do заменой метки m:, а слово Loop считать заменой оператора GoTo m. Обе программы бесконечно печатают 3 5 7 9 11 …..
Общий смысл оператора Do
такой: выполни по порядку сверху вниз все операторы между словами Do и Loop, затем выполни их по порядку еще раз и еще раз и так далее.
Толку в 0 варианте оператора Do мы из-за зацикливания видим мало (пока!).
Синтаксис оператора Do …. Loop:
Do
операторы
операторы
…………….
Loop
Строки операторов между Do и Loop называются телом цикла.
Оператор Do …. Loop While
Добавьте в ваш проект еще 4 кнопки и выполните в пошаговом режиме программы с вариантами оператора Do 1 – 4, которые я привел ниже. Вы увидите, что все 4 варианта делают одно и то же и они очень похожи. Вопрос о том, зачем нужно целых 4 похожих варианта, рассмотрим чуть позже. Уверяю, они все нужны.
Составим с использованием 1 варианта оператора Do программу решения задачи о печати чисел 3 5 7 9 из предыдущего подраздела. Для того, чтобы точно определить работу этого варианта оператора Do, приведем ее параллельно с 1 вариантом программы решения этой задачи из того же подраздела. Объяснением любого оператора в правом столбце является оператор, стоящий в той же строчке в левом столбце.
1 ВАРИАНТ | 1 ВАРИАНТ ОПЕРАТОРА Do | ||
Write("Начало счета ") | Write("Начало счета ") | ||
f = 3 | f = 3 | ||
m: | Do | ||
Write(f & " ") | Write(f & " ") | ||
f = f + 2 | f = f + 2 | ||
If f <= 9 Then GoTo m | Loop While f <= 9 | ||
Write("Конец счета") | Write("Конец счета") |
While переводится «Пока». Значит, Loop While f <= 9 понимать следует так: «Возвращайся к метке Do, пока f<=9».
Порядок работы обеих программ совершенно одинаков, так что можно считать слово Do заменой метки m:, а конструкцию Loop While f <= 9 считать заменой оператора If f <= 9 Then GoTo m.
Синтаксис оператора Do …. Loop While:
Do
операторы
операторы
…………….
Loop While условие продолжения
работы цикла
Оператор Do …. Loop Until
2 ВАРИАНТ | 2 ВАРИАНТ ОПЕРАТОРА Do | ||
Write("Начало счета ") | Write("Начало счета ") | ||
f = 3 | f = 3 | ||
m1: | Do | ||
Write(f & " ") | Write(f & " ") | ||
f = f + 2 | f = f + 2 | ||
If f > 9 Then GoTo m2 Else GoTo m1 | Loop Until f > 9 | ||
m2: Write("Конец счета") | Write("Конец счета") |
Until переводится «До тех пор, пока не».
Значит, Loop Until f > 9 понимать следует так: «Возвращайся к метке Do до тех пор, пока не выполнится условие f > 9».
Синтаксис оператора Do …. Loop Until:
Do
операторы
операторы
…………….
Loop Until условие завершения
работы цикла
Оператор Do While …. Loop
3 ВАРИАНТ | 3 ВАРИАНТ ОПЕРАТОРА Do | ||
Write("Начало счета ") | Write("Начало счета ") | ||
f = 3 | f = 3 | ||
m1: If f <= 9 Then GoTo m3 Else GoTo m2 | Do While f <= 9 | ||
m3: Write(f & " ") | Write(f & " ") | ||
f = f + 2 | f = f + 2 | ||
GoTo m1 | Loop | ||
m2: Write("Конец счета") | Write("Конец счета") |
Do While f <= 9 понимать следует так: «Пока f <= 9, выполняй нижестоящие операторы вплоть до Loop».
Синтаксис оператора Do While …. Loop:
Do While условие продолжения работы цикла
операторы
операторы
…………….
Loop
Оператор Do Until …. Loop
4 ВАРИАНТ | 4 ВАРИАНТ ОПЕРАТОРА Do | ||
Write("Начало счета ") | Write("Начало счета ") | ||
f = 3 | f = 3 | ||
m1: If f > 9 Then GoTo m2 Else GoTo m3 | Do Until f > 9 | ||
m3: Write(f & " ") | Write(f & " ") | ||
f = f + 2 | f = f + 2 | ||
GoTo m1 | Loop | ||
m2: Write("Конец счета") | Write("Конец счета") |
Do Until f > 9 понимать следует так: «Выполняй нижестоящие операторы вплоть до Loop, до тех пор, пока не выполнится условие f > 9».
Синтаксис оператора Do Until …. Loop:
Do Until условие завершения работы цикла
операторы
операторы
…………….
Loop
Разница между вариантами операторов Do
Разницы две:
Между While и Until. Здесь соображения удобства. Что вам удобнее: указывать компьютеру, когда цикл нужно продолжать (f <= 9) или когда его нужно заканчивать (f > 9)?
В том, куда поставить условие – после Do или после Loop. Здесь разница не только в удобстве. В первом случае можно придумать такое условие, когда тело цикла не выполнится ни разу. Например,
Dim f As Integer = 3
Do Until f > 0
Debug.Write(f)
f = f - 10
Loop
Во втором случае, каково бы ни было условие, тело цикла хотя бы раз, да выполнится, потому что добраться до стоящего внизу условия можно только пройдя все стоящие выше него операторы.
Часто эти отличия для начинающих малосущественны, поэтому пока выбирайте вариант по вкусу.
Типичная ошибка начинающих – небрежное обращение со знаками сравнения. Многие не видят большой разницы в том, как записать – While f<=9 или While f<9, а затем, «недополучив» результат, удивляются, куда он делся. Если вы не понимаете, куда, попробуйте ошибочный вариант программы с While f<9 выполнить в пошаговом режиме.
Примеры и задания
Задание 29.
Выполнить с использованием оператора Do задачу из 8.2: Напечатать пары чисел – 0 1000 1 999 2 998 . . . . . . 1000 0. Напишите два варианта программы с использованием 1 и 2 вариантов оператора Do.
Задание 30.
Выполнить с использованием оператора Do Задание 43: Изображение, пройдя немного слева направо, поворачивает вниз и, пройдя немного, через некоторое время останавливается. Используйте 3 и 4 варианты оператора Do.
Задача: Компьютер предлагает человеку ввести слово, после чего распечатывает это слово, снабдив его восклицательным знаком. Затем снова предлагает ввести слово и так до тех пор, пока человек не введет слово «Хватит». Распечатав его с восклицательным знаком, компьютер отвечает «Хватит так хватит» и заканчивает работу.
Придумаем строковую переменную, в которую человек будет с клавиатуры вводить слово. Назовем ее Slovo. Выберем подходящий вариант оператора Do, это будет 2-й вариант (а 3-й и 4-й здесь вообще не подойдут), и пишем программу:
Dim Slovo As String
Do
Slovo = InputBox("Введите слово")
Debug.WriteLine(Slovo & "!")
Loop Until Slovo = "Хватит"
Debug.WriteLine("Хватит так хватит")
Задание 31.
Усложним эту задачу. Пусть компьютер перед распечаткой каждого слова ставит его порядковый номер. И еще: если слово длинней 10 букв, компьютер должен добавить – «Тяжелая жизнь.».
Задание 32.
«Полет камня». Это непростое задание разделим на два этапа:
Вычислительная часть. Если камень бросить горизонтально со 100-метровой башни со скоростью v=20м/с, то его расстояние от башни по горизонтали s будет выражаться формулой s=vt, где t – время полета камня в секундах. Высота над землей h будет выражаться формулой h=100-9.81t2/2. Требуется вычислять и печатать t, s и h для значений t = 0, 0.2, 0.4, 0.6 и так далее до тех пор, пока камень не упадет на землю.
Графическая часть (выполнять только в том случае, если получилась вычислительная). Нарисовать землю, башню и траекторию камня (Рис. 8.1). Указание: Траектория – серия кружочков. За одну итерацию цикла рисуется один кружочек. Высота башни – 100 пикселей. И в остальном тоже выберем масштаб – 1 метр – 1 пиксель, что удобно. Горизонтальная координата кружка на форме – это s с небольшим сдвигом вправо, так как бросаем не от левого края формы. Вертикальная координата кружка на форме – это 100-h с небольшим сдвигом вниз, так как бросаем не от верхнего края формы. Переменная h взята со знаком минус потому, что вертикальная ось в компьютерной системе координат направлена вниз.
Рис. 8.1
В Задание 104 мы изобразим полет камня в реальном времени.
Оператор Exit Do
Оператор Exit Do нужен для того, чтобы выходить из цикла не в начале тела цикла, как в вариантах 3-4, не в конце, как в вариантах 1-2, а в середине. Добавим, например, Exit Do в тело цикла одного из вариантов предыдущей программы:
Write("Начало счета ")
f = 3
Do
Write(f & " ")
Exit Do
f = f + 2
Loop While f <= 9
Write("Конец счета")
Вот результат работы этой программы:
Начало счета 3 Конец счета
Как видите, Exit Do – это всего лишь приказ перейти к оператору, следующему за словом Loop. Толк от Exit Do будет тогда, когда его поместят внутрь оператора ветвления:
Write("Начало счета ")
f = 3
Do
Write(f & " ")
If f >= 5 Then Exit Do
f = f + 2
Loop While f <= 9
Write("Конец счета")
Вот результат работы этой программы:
Начало счета 3 5 Конец счета
Пока в Exit Do особого смысла вы не видите. А увидите вы его в следующей задаче, где не подойдет ни один из вариантов 1 – 4 оператора Do, а подойдет Exit Do в совокупности с 0 вариантом.
Задача. Выполнить с использованием Do и Exit Do Задание 41: Для х=2700, 900, 300, 100 .. . и т.д. вычислять и печатать y=x/4+20 и z=2y+0.23 до тех пор, пока yz не станет меньше 1/х.
Загляните в ответ – как решена задача без Do. А вот как – с Do.
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim x, y, z As Double
x = 2700
Do
y = x / 4 + 20
z = 2 * y + 0.23
If y * z < 1 / x Then Exit Do
Debug.WriteLine(Format(x, "0.000000") & " " & Format(y, "0.000000") & " " & Format(z, "0.000000"))
x = x / 3
Loop
End Sub
Как видите, очень похоже. Обратите внимание, что если вы захотите обойтись без Exit Do и использовать один из вариантов 1 – 4 оператора Do, то у вас при использовании 1-2 вариантов «новый» x в условии y*z<1/x будет сравниваться со «старыми» y и z (в нашем конкретном случае это не приведет к катастрофе, но все равно это нехорошо). А использование 3-4 вариантов вообще невозможно без неоправданного усложнения программы.
Задание 33.
Решить чуть измененную задачу про «Хватит так хватит» из предыдущего раздела: Компьютер предлагает человеку ввести слово, после чего распечатывает это слово, снабдив его восклицательным знаком. Затем снова предлагает ввести слово и так до тех пор, пока человек не введет слово «Хватит». Его он не распечатывает вообще, а отвечает «Хватит так хватит» и заканчивает работу. Указание: Используйте Exit Do в совокупности с 0 вариантом Do. Остальные варианты не подойдут или подойдут плохо.
Оператор цикла While …End While
Имеется оператор цикла While …… End While, привычный для других языков и для предыдущих версий Бейсика. Вот вам его синтаксис:
While условие продолжения работы цикла
операторы
операторы
…………….
End While
А смысл – абсолютно тот же, что и у Do While …. Loop
Операторы цикла Do
Циклы настолько широко применяются в программах, что у программистов давным-давно появилась потребность написать специальный оператор цикла, не использующий оператор GoTo. Последний неудобен хотя бы тем, что у программистов, пишущих большие программы, много времени и внимания уходит на поиск взглядом меток в тексте программы. К тому же GoTo нарушает стройную идеологию так называемого «структурного программирования», когда порядок действий задается не скачками из одной части программы в другую, а цепочкой вложенных друг в друга операторов. В общем, нынче широко использовать GoTo так же неприлично, как не объявлять переменные.
Операторы цикла в VB делятся на 2 вида: Do и For. Операторы вида Do встречаются в 5 вариантах:
0 ВАРИАНТ | 1 ВАРИАНТ | 2 ВАРИАНТ | 3 ВАРИАНТ | 4 ВАРИАНТ | |||||
Do
…. Loop | Do
…. Loop While | Do
…. Loop Until | Do While
…. Loop | Do Until
…. Loop |
Операторы вида For встречаются в 2 вариантах. О них – в следующем разделе.
Объясняю For на примерах
Задача 1: 200 раз напечатать слово ФУТБОЛ.
Очевидно, нам нужно написать циклическую программу, где цикл выполнялся бы 200 раз. Попробуем сначала решить задачу при помощи оператора GoTo. Для выхода из цикла оператор GoTo нужно включить в состав оператора If. Кроме этого нужна переменная, меняющая свое значение от одного выполнения цикла к следующему. Удобнее всего, если эта переменная будет менять свое значение на 1, начиная с 0 или 1. Напомним, что переменная, организованная таким образом, является счетчиком циклов (См. 8.1.3). Придумаем этой величине какое-нибудь имя, скажем i. Задачу решает такой причудливый фрагмент:
i = 1
m1: If i > 200 Then GoTo m2
Debug.Write("ФУТБОЛ ")
i = i + 1
GoTo m1
m2:
Здесь i вначале равно 1, но к каждому следующему выполнению цикла оно увеличивается на 1. В первый раз выполняя оператор If, компьютер проверяет условие 1>200 и найдя его ложным, выполняет следующий оператор – Debug.Write("ФУТБОЛ "). Во второй раз проверяется условие 2>200 ит.д. В 200-й раз компьютер проверяет условие 200>200 и найдя его ложным, в 200-й раз выполняет оператор Debug.Write("ФУТБОЛ "). В 201-й раз компьютер проверяет условие 201>200 и найдя его истинным, выходит из цикла, перейдя к метке m2. Все. ФУТБОЛ напечатан ровно 200 раз.
В нашем примере «полезную» работу выполняет только одна строка из пяти – Debug.Write("ФУТБОЛ "). Остальные строки заняты тем, что обеспечивают выполнение «полезной» строки ровно 200 раз. Чтобы избежать такого перекоса в сторону «обслуживающего персонала» и для вящей краткости, придумали оператор For и вместо верхних 2 строк:
i = 1
m1: If i > 200 Then GoTo m2
стали писать 1 строку:
For i = 1 To 200
а вместо нижних 2 строк:
i = i + 1
GoTo m1
тоже стали писать 1 строку:
Next i
смысл программы оставляя тем же самым.
Получилось вот что:
For i = 1 To 200
Debug.Write("ФУТБОЛ ")
Next i
Как видите, гораздо короче.
Слово For переводится «Для». Слово To переводится «до». Конструкция For i=1 To 200 понимается так: Для i, изменяющегося от 1 до
200, выполняй операторы, стоящие ниже вплоть до слова Next.
Слово Next говорит о том, что надо увеличивать i на 1 и возвращаться к началу цикла. При первом выполнении цикла i будет равно 1, при втором – 2, и так далее. При последнем – 200. Переменная i называется переменной цикла.
Задача 2. В предыдущем конкретном случае сами по себе значения i не были важны, нужный результат мы бы получили и с оператором
For i = 501 To 700
А вот где значения переменной цикла будут важны: Распечатать пары чисел – 101 1010 102 1020 103 1030 ……. 110 1100
Вот программа:
For a = 101 To 110
b = 10 * a
Debug.WriteLine(a & " " & b)
Next a