ПОНЯТНО О Visual Basic NET (том 2)

Функции


Философия. Мы с вами уже сталкивались со стандартными функциями (например, 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.        
    Напишите функцию с двумя строковыми параметрами. Функция должна выдать длину той строки, которая короче.

    Содержание раздела