Функции
Философия. Мы с вами уже сталкивались со стандартными функциями (например, Len(w), Abs(c+200) и др.). Стандартная функция – это некая скрытая программа, которая принимает свои параметры, указанные в скобках, в качестве исходных данных (аргументов), что-то делает с ними и в результате получает одну величину, которая и является значением функции. Много примеров функций вы найдете в 5.4.1 и по всей книге.
Когда мы видим оператор
b = a * (Len(w) – Abs(c+200))
то говорим, что при выполнении этого оператора компьютер обращается к функциям Len и Abs. А само упоминание этих функций в тексте оператора называем обращениями к этим функциям, чтобы отличить их от объявлений функций, которые являются солидными программами для вычисления значений этих функций, только скрытыми от нас. Здесь полная аналогия с обращением к процедуре пользователя. Обращение к процедуре – это коротенький оператор, а объявление процедуры может состоять из многих строк.
Обратите внимание, что обращения к стандартным функциям встречаются обычно в выражениях. Редко кто пишет отдельные операторы такого вида:
Len(w)
Abs(c + 20)
Да это и понятно. Ведь в этом случае неизвестно, как использовать вычисленную величину функции. Действительно, ну вот мы вычислили длину строки, а куда ее девать, как приспособить для какого-нибудь дела? Поэтому пишут так:
b = Len(w)
Здесь все понятно: мы в дальнейшем можем использовать, переменную b, которая несет в себе вычисленную величину функции, как хотим. Или, скажем, так:
Debug.WriteLine(Abs(c + 20))
Здесь тоже от абсолютной величины польза была: мы ее напечатали.
Сейчас я хочу, чтобы мы подошли к осознанию необходимости и удобства обладания собственными функциями – функциями пользователя. Функции пользователя вы создаете тогда, когда вам недостаточно функций из библиотеки классов .NET Framework. Например, вы хотите иметь функцию, аргументами которой были бы длины двух сторон прямоугольника, а значением – периметр этого прямоугольника. Но позвольте! – скажете вы – Мы только что создали процедуру, которая делает именно это, да еще и площадь вычисляет! Зачем нам нужна еще какая-то функция? А затем, что функция в данном случае удобнее и естественнее в использовании. Вы увидите, что удобнее создать и использовать две функции: Периметр и Площадь, чем одну процедуру. И не забывайте, кстати, что единую функцию Периметр_Площадь создать нельзя, так как функция может иметь только одно значение.
Пример 1. Вспомним задачу из 11.4.1. : «Известны стороны двух прямоугольников. Нужно напечатать периметр того прямоугольника, чья площадь больше.» Приведу целиком программу решения этой задачи, но уже с использованием не процедуры, как в 11.4.1. , а функций:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim A1, B1 As Integer 'Две стороны 1 прямоугольника
Dim A2, B2 As Integer 'Две стороны 2 прямоугольника
A1 = 10 : B1 = 50
A2 = 20 : B2 = 30
If Площадь(A1, B1) > Площадь(A2, B2) Then WriteLine(Периметр(A1, B1)) _
Else WriteLine(Периметр(A2, B2))
End Sub
Function Периметр(ByVal Сторона1 As Integer, ByVal Сторона2 As Integer) As Integer
Return 2 * Сторона1 + 2 * Сторона2
End Function
Function Площадь(ByVal Сторона1 As Integer, ByVal Сторона2 As Integer) As Integer
Площадь = Сторона1 * Сторона2
End Function
Пояснения. Оператор
If Площадь(A1, B1) > Площадь(A2, B2) Then WriteLine(Периметр(A1, B1)) _
Else WriteLine(Периметр(A2, B2))
содержит в себе два обращения к функции Площадь и два обращения к функции Периметр. Он хорош своей естественностью и понятностью. Действительно, он практически повторяет условие задачи:
«ЕСЛИ площадь первого прямоугольника больше площади второго,
ТО печатай периметр первого прямоугольника,
ИНАЧЕ печатай периметр второго».
Терминология. Как же устроены наши функции? Сначала о терминологии, которая очень похожа на терминологию процедур.
Все операторы, из которых состоит функция, без заголовка и конечной строки, будем называть телом функции. А вместе с этими строками – объявлением функции. Как видите, тела наших функций очень коротенькие, всего из одного оператора. Но операторов может быть сколько угодно, как и в процедуре.
Когда VB в процессе выполнения программы натыкается на функцию Периметр, он ищет в программе объявление функции с именем Периметр и начинает выполнять тело этой функции. Этот процесс называется вызовом функции или
обращением к функции . Говорят также, что управление передается функции. После выполнения тела функции VB возвращается к выполнению программы. Говорят, что управление возвращается к программе. Про значение функции говорят, что функция возвращает это значение в программу.
Отличия функций от процедур. Вы видите, что объявление функции очень похоже на объявление процедуры. Но функция в отличие от процедуры обладает некоторыми свойствами переменной величины. Объявление функции отличается от объявления процедуры следующими элементами:
- В заголовке функции мы пишем не Sub (процедура), а Function (функция)
- В заголовке функции после скобок с параметрами должен быть указан тип значения функции (так как у нас это численное значение имеет смысл площади или периметра, то я выбрал в обоих случаях Integer).
- Внутри тела функции ей хотя бы раз должно быть присвоено какое-нибудь значение. Именно это значение функция и будет возвращать. Присвоение можно делать двумя способами:
- Обычным оператором присваивания, в котором слева от знака равенства стоит имя функции, как если бы это была не функция, а обычная переменная (у нас этим занимается оператор Площадь = Сторона1 * Сторона2).
- При помощи специального оператора Return (у нас этим занимается оператор Return 2 * Сторона1 + 2 * Сторона2).
- В конечной строке мы пишем не End Sub (конец процедуры), а End Function (конец функции)
Напомню еще раз, что обращение к функции отличается от обращения к процедуре. Если обращение к процедуре – самостоятельный оператор, то обращение к функции – это обычно составная часть выражения.
Пример 2. Рассмотрим другой пример функции. Ее параметр (аргумент) – строка. Значение функции – та же строка, повторенная столько раз, сколько символов (букв) в исходной строке. Например, если параметр – «уж», то функция – «ужуж», параметр – «Вена», функция – «ВенаВенаВенаВена». Если длина параметра превышает 8 символов, значение функции таково – «Вы задали слишком длинное слово».
Вот программа:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim S As String
S = TextBox1.Text
TextBox1.Text = Размноженное_слово(S)
End Sub
Function Размноженное_слово( ByVal Slovo As String) As String
Dim i, Длина_слова As Integer
Длина_слова = Len(Slovo)
If Длина_слова > 8 Then
Return "Вы задали слишком длинное слово"
Else
Размноженное_слово = ""
For i = 1 To Длина_слова
Размноженное_слово = Размноженное_слово & Slovo
Next
End If
End Function
Пояснения. Здесь мы пишем в текстовом поле слово и щелчком по кнопке тут же получаем в том же текстовом поле результат – размноженное слово.
Посмотрим на объявление функции. В заголовке мы видим, что как параметр функции, так и сама функция имеют тип String. Процедуры и функции, как вы уже видели, могут содержать кроме параметров также и обычные локальные переменные (в нашем случае это i и Длина_слова). Тело функции состоит из нескольких операторов.
Механика работы этой функции проста. Переменная Размноженное_слово накапливает в себе слова, как сумматор сумму. Роль плюса играет знак &. На каждой итерации второй из двух операторов присваивания удлиняет Размноженное_слово на Slovo. А итераций столько, сколько букв в слове. «Обнуляет сумматор» оператор
Размноженное_слово = ""
Роль нуля исполняет пустая строка "" длиной в 0 символов.
Для возврата значений применяется в одном месте оператор Return и в двух местах – оператор присваивания Размноженное_слово = . . .. Не запутаемся ли? – Значение одно, операторов – три. Спрашивается, какой из этих операторов в действительности вернет значение? Чтобы ответить на этот вопрос, вам нужно знать, что
между операторами Return и присваивания, используемыми для возврата значения функции, имеется существенное различие. А именно: наткнувшись на оператор Return, компьютер выполняет его и на этом немедленно прекращает выполнение функции, возвращаясь в программу, ее вызвавшую. Ничего подобного при выполнении оператора присваивания не происходит – компьютер продолжает выполнять тело функции дальше.
Если вы внимательно разберете текст этой или любой другой функции, то увидите, что вернет значение оператор, последний по времени выполнения (а не в порядке записи). Какой именно? Все зависит от длины слова. Если оно длиннее 8 букв, то значение вернет оператор Return. Если его длина – от 1 до 8 букв, то второй из двух операторов присваивания. Если вы ничего не введете в текстовое поле, то – первый.
Обязательно прогоните программу в пошаговом режиме. Вам будет любопытно наблюдать, как постепенно удлиняется Размноженное_слово.
Побочный эффект. В теле функции можно писать сколько угодно любых операторов. А почему бы в таком случае не попытаться убить двух зайцев и не вычислять в теле функции Площадь еще и периметр? Ну и что, что он не будет возвращаемым значением функции! Зато тогда можно было бы обойтись одной функцией. Попробуем:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim A1, B1, П1 As Integer 'Две стороны и периметр 1 прямоугольника
Dim A2, B2, П2 As Integer 'Две стороны и периметр 2 прямоугольника
A1 = 10 : B1 = 50
A2 = 20 : B2 = 30
If Площадь(A1, B1, П1) > Площадь(A2, B2, П2) Then WriteLine(П1) Else WriteLine(П2)
End Sub
Function Площадь(ByVal Сторона1 As Integer, ByVal Сторона2 As Integer, ByRef Периметр As Integer) _
As Integer
Площадь = Сторона1 * Сторона2
Периметр = 2 * Сторона1 + 2 * Сторона2
End Function
Что мы выиграли и что проиграли? Сэкономили две строчки кода, но пришлось вводить дополнительные переменные и параметр. А самое главное – проиграли в простоте, единообразии и понятности. Функция получилась довольно нелепая.
Если функция кроме возвращаемого значения вычисляет что-то еще побочное, что мы используем в программе, то говорят, что функция имеет побочный эффект. Побочные эффекты довольно часто используются в программировании. Например, среди функций из библиотеки классов .NET Framework довольно много таких, возвращаемое значение которых никого не интересует, а интересует именно побочный эффект. Поэтому обращение к функции разрешено писать не только в выражениях, но и отдельным оператором. Например, так:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim A1, B1, П1 As Integer 'Две стороны и периметр прямоугольника
A1 = 10 : B1 = 50
Площадь(A1, B1, П1)
Debug.WriteLine(П1)
Math.Abs(-20)
End Sub
Как я уже упоминал, от площади в этом примере толк есть, а от абсолютной величины – нет.
Задание 65.
Напишите функцию с двумя строковыми параметрами. Функция должна выдать длину той строки, которая короче.