Процедуры

В отличие от функций, процедуры не возвращают конкретных значений. Вызов процедур осуществляется по имени. Непустые списки аргументов всегда заключаются в круглые скобки. В приведенном ниже примере строка с вызовом процедуры выделена жирным шрифтом:

Option Strict On

Module Modulel

Sub ShowBottlesOfBeer(ByVal nbot As Integer)

Console.WriteLine(nbot & " bottles of beer on the wall")

Console.Writeline(nbot & " bottles of beer.")

Console.WriteLine("if one of those bottles hsould happen to fall")

Console.WriteLine(nbot -1&" bottles of beer on the wall")

End Sub

Sub Main()

Dim I As Integer

For I = 10 To 1 Step -1

ShowBottlesOfBeer(I)

Next

Console.WriteLine("All beer gone...")

Console. ReadLine()

End Sub

End Module

При вызове процедур указывать ключевое слово Sub не обязательно. Строку с вызовом процедуры из приведенного выше примера можно было записать и в таком виде:

Call ShowBottlesOfBeer(I)

Заголовок процедуры должен содержать объявления всех параметров с ключевыми словами ByVal или ByRef (по умолчанию используется ByVal, то есть передача по значению):

Sub имя_процедуры(В(ByVа1 аргумент1 As тип. ByVal аргумент2 As тип, ....)

команды

End Sub

При вызове процедуры в форме имя_процедуры(аргумент1, аргумент2, ...) или Call имя_процедуры(аргумент1. аргумент2, ...) VB .NET создает копии данных-аргументов и выполняет код, содержащийся в теле процедуры (поскольку в отличие от предыдущих версий по умолчанию параметры передаются по значению).

 

Преждевременный выход из функций или процедур

В некоторых ситуациях требуется покинуть функцию до того, как будет достигнута стандартная точка выхода (например, если проверка покажет, что исходные данные неверны и дальнейшие вычисления бессмысленны). Команда Return немедленно передает управление вызывающей стороне (если не определена секция Finally — см. главу 7):

Function BallOut (X As Double) As Double If X < 0 Then

Return 0' Вернуть фиктивное значение Else

' Основные действия

End If

End Function

'Выход из процедур осуществляется командой

Exit Sub.

 

Передача массивов функциям и процедурам

В VB .NET, как и в прежних версиях VB, существуют удобные средства для работы с одномерными и многомерными массивами в процедурах и функциях. Впрочем, существуют некоторые нюансы, обусловленные передачей по ссылке и по значению; мы рассмотрим их в главе 4. Перебор содержимого массива осуществляется конструкцией For Each или (более распространенный вариант) стандартным циклом For с вычислением верхней границы при помощи функции UBound (). Ниже приведен пример функции поиска максимального элемента в массиве:

Function FindMax(ByVa1 a() As Integer

Dim finish As Integer = UBound(a)

Dim max As Integer = a(0)

Dim i As Integer

For i = 0 To finish

If a(i) > max Then max = a(i)

Next i

Return max End Function

Обобщенная форма вызова UBound(имя_массива, I) возвращает верхнюю границу по 1-му измерению массива. Для одномерных массивов (списков) параметр 1 является необязательным.

Для проверки также можно воспользоваться методом Length, реализованным в классе массива, но этот метод возвращает количество элементов в массиве вместо верхней границы (в многомерных массивах эти величины не совпадают).

 

Процедуры и функции с необязательными аргументами

В VB. NET сохранена возможность определения процедур и функций с необязательными аргументами, но в отличие от VB6 для каждого необязательного параметра должно быть указано значение по умолчанию. Следующий пример демонстрирует синтаксис объявления необязательных параметров:

Sub ProcessAddress(TheName As String,

Address As String. City As String. State As String.

ZipCode As String. Optional ZipPlus4 As String = "0000")

В данном примере последний параметр является необязательным (Optional) и по умолчанию равен "0000".

В главе 4 описана перегрузка (overloading) —другой способ определения функций с необязательными параметрами.

VB .NET также позволяет определять процедуры и функции с произвольным количеством аргументов. Для этого в качестве параметра передается массив с ключевым словом РаramАrrау, как в следующем примере:

Function AddThemUp(ByVal ParamArray stuff() As Double) As Double

Dim total As Double = 0

Dim Number As Double = 0

Dim I As Integer

For I = 0 To UBound(stuff)

total = total + stuff(I)

Next

Return total End Function

Пример использования функции:

x = AddThemUp(3, 4. 5. 6)

В результате переменной х присваивается значение 18.

 

Именованные аргументы

При вызове функций и процедур с большим количеством параметров (особенно необязательных) существует такая элегантная возможность, как передача именованных аргументов. Если значения параметров при вызове передаются в виде «имя -:=значение», вам не придется беспокоиться о соблюдении порядка аргументов (регистр символов в именах игнорируется). В отличие от прежних версий VB, где именованные аргументы то работали, то нет, в VB .NET они работают всегда.

Именованные аргументы разделяются запятыми. При разумном выборе имен параметров именованные аргументы заметно упрощают чтение программы, особенно при большом количестве необязательных аргументов. Для примера возьмем приведенный выше заголовок функции ProcessAddress:

Sub ProcessAddress(TheName As String.

Address As String. City As String. State As String,

ZipCode As String, Optional ZipPlus4 As String = "0000")

Вызов этой процедуры может выглядеть так:

ProcessAddress(Address := "The Whitehouse"

Name := "GeorgeW",

City := "DC". _

State:= String.Empty. _

ZipCode:= "12345")

Обратите внимание: порядок перечисления аргументов отличается от заданного в заголовке процедуры.

 

Рекурсия

В VB .NET, как и в любом сколько-нибудь серьезном языке программирования, поддерживается рекурсия — решение задач посредством сведения их к более простым задачам того же типа. Одним из стандартных примеров рекурсивного решения является перебор дерева каталогов на диске (см. главу 9).

На концептуальном уровне рекурсивное решение выглядит следующим образом:

Решение задачи с применением рекурсии

If задача тривиальна

Решить Else

Упростить задачу, сводя ее к однотипной, но более простой задаче

Решить более простую задачу с применением рекурсии

End If

(Возможно) Объединить решение простой задачи (или задач)

с решением исходной задачи.

Рекурсивная функция или процедура постоянно вызывает сама себя, непрерывно упрощая задачу до тех пор, пока ее решение не станет тривиальным; в этот момент задача решается, и происходит возврат к предыдущему уровню. Применение рекурсии нередко связано с принципиальным изменением подхода к некоторым задачам, порождающим особенно элегантные решения и столь же элегантные программы (кстати, многие алгоритмы сортировки — такие, как встроенный метод Sort класса .NET Array, — основаны на принципе рекурсии).

В качестве примера мы рассмотрим программу поиска наибольшего общего делителя двух целых чисел (то есть наибольшего целого числа, на которое они оба делятся без остатка). Пример:

  • НОД(4,6) = 2 (наибольшее число, на которое 4 и 6 делятся без остатка).
  • НОД(12,7) - 1 (числа 12 и 7 не делятся ни на одно целое число, большее 1).

    Около 2000 лет назад Евклид предложил следующий алгоритм вычисления НОД двух целых чисел а и b:

  • Если а делится на b, НОД= b. В противном случае НОД (а, b) = НОД (b, a Mod b)
  • Вспомним, что функция Mod возвращает остаток от целочисленного деления, а выражение a Mod b,равно 0 лишь в том случае, если а кратно b. Пример:

    НОД(126.12)=НОД (12. 126 Mod 12)=НОД (12,6) - 6

    Ниже приведен пример рекурсивной функции для вычисления НОД. В строке, выделенной жирным шрифтом, функция GCD вызывает сама себя для более простого случая:

    Option Strict On Module Modulel

    Function GCD(ByVal p As Long, ByVal q As Long) As Long

    If Q Mod P = 0 Then

    Return P Else

    Return GCD(Q, P Mod Q)

    End If

    End Function Public Sub Main()

    Console.WriteLine("The GCD of 36 and 99 is " & GCD(36. 99))

    Console. ReadLine()

    End Sub

    End Module

    Сначала обрабатывается тривиальный случай Q Mod P = 0. Если это условие не выполняется, функция GCD снова вызывает себя для более простого случая, поскольку в результате применения Mod мы переходим к меньшим числам. В приведенном примере объединять результаты не нужно (как, например, в функции сортировки).