Главная Случайная страница


Полезное:

Как сделать разговор полезным и приятным Как сделать объемную звезду своими руками Как сделать то, что делать не хочется? Как сделать погремушку Как сделать так чтобы женщины сами знакомились с вами Как сделать идею коммерческой Как сделать хорошую растяжку ног? Как сделать наш разум здоровым? Как сделать, чтобы люди обманывали меньше Вопрос 4. Как сделать так, чтобы вас уважали и ценили? Как сделать лучше себе и другим людям Как сделать свидание интересным?


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Передача аргументов процедурам





Как вы уже успели заметить, процедуры принимают <$I[]аргумент (argument)> аргументы (arguments). Процедуры без аргументов тоже встречаются в «природе», но реже, чем с аргументами. Аргументы необходимы для того, чтобы процедура могла с помощью одного и того же кода выполнять разные задачи.

Одним из способов передачи аргументов может быть настройка глобальных переменных, которыми пользуется вызываемая процедура. Но этот способ настолько прозрачен, что, говоря о передаче параметров, никто не имеет в виду передачу такого типа.

Когда процедура получает аргументы, она работает с ними как с обычными переменными, которые либо уже содержат значения, либо готовы их принять в коде процедуры. Так же, как и обычные переменные, аргументы имеют типы (Integer, Double и т.д.), которые описывают аргументы в списке аргументов внутри скобок, принадлежащих имени процедуры. Список имеет следующий синтаксис:

[[ByVal | ByRef] Argument1 [As type1 ]] [, [ ByVal|ByRef ] Argument2 [As type2 ]] [...,[ByVal | ByRef] Argumentn [As typeN ]]

Список может быть пустым, и тогда можно снова говорить о том, процедура необязательно имеет аргументы.

Параметры могут передаваться в процедуру, как по ссылке, так и по значению. По умолчанию параметры передаются по ссылке, что позволяет не только передавать их в процедуру, так и пользоваться измененными (в коде процедуры) значениями после выхода из процедуры. Таким образом, в процедуре можно обработать и возвратить не один параметр, а даже целые массивы. Это относится и к функциям и процедурам. Указывать в списке параметров, что передача параметра осуществляется по ссылке (ключевым словом ByRef) необязательно, но это повышает понимаемость кода.

Иногда бывает нежелательно, чтобы процедура изменяла параметры. В таком случае следует передавать параметры по значению (использовать ключевое слово ByVal). При вызове процедуры ей передаются не ссылки, а значения аргументов. В памяти процедуры создаются копии аргументов. Конечно, это требует выделения дополнительного количества памяти. Такой вызов функции-процедуры называется вызовом по значению.

Рассмотрим случай, когда может потребоваться ключевое слово ByVal. Посмотрите внимательно на код листинга 7.3 и задайте себе вопрос, зачем нужна переменная xrem? В строке 9 этого листинга переменной xrem присваивается значение аргумента xL, а далее в коде все операторы используют только переменную xrem. Если оператор в строке 9 листинга 7.3 убрать и заменить все вхождения в текст функции-процедуры переменной xrem, код будет более привлекательным с точки зрения программиста (см. листинг 7.5).

Листинг 7.5. Модификация функции-процедуры int_intF

1 Function int_intF(xL As Long) As Long

2 'формирование числа из цифр числа xL,

3 'взятых в обратном порядке

5 Dim i As Integer, lenx As Integer

6 Dim xS As String, deli As Long

8 lenx = Len(Trim(Str(xL))) 'количество цифр в xL

10 For i = 1 To lenx

11 deli = 10 ^ (lenx - i) 'текущий делитель

12 xS = Trim(Str(xL \ deli)) & xS

13 xL = xL Mod deli 'остаток от деления

14 Next

16 int_intF = Val(xS)

17 End Function

Но, на самом деле, мы получили немного другую функцию. И вот почему. Протестируйте функцию-процедуру из листинга 7.3 со следующим кодом:

Dim xxL As Long

xxL = 76852

MsgBox xxL & "-" & int_intF(xxL) & "-" & xxL,, “”

Вы получите на экране окно, представленное на рис. 7.2. Протестируйте функцию-процедуру из листинга 7.5 с тем же кодом. Результат будет таким, как представлен на рис. 7.3.

Рис. 7.2

Результат вызова функции-процедуры int_intF из листинга 7.5

Рис. 7.3

Результат вызова функции-процедуры int_intF из листинга 7.3

В чем здесь дело? При вычислении выражения оператора

MsgBox xxL & "-" & int_intF(xxL) & "-" & xxL,, “”

Вычисляется значение int_intF(xxL), затем полученное значение конкатенируется слева и справа (вообще-то, достаточно было бы только с одной стороны) со значением переменной xxL. Значит, вопрос только в том, что происходит со значением переменной xxL при вызове функции-процедуры из листинга 7.5. Это значения изменяется, поскольку по умолчанию в процедуру передается ссылка на переменную. В коде этой функции мы активно пользуемся этой ссылкой и меняем значение переменной. В коде же листинга 7.3 мы не меняем значение переменной, а только присваиваем это значение рабочей переменной.

Что в этом случае делать? Отказаться от такого модифицированного кода? Все, на самом деле, очень просто. В Visual Basic можно «по-хорошему попросить» не изменять значение аргумента, передаваемого при вызове функции, несмотря на то, что сам аргумент используется в коде функции в качестве рабочей переменной. Для этого нужно описать параметр функции как ByVal:

Function int_intF(ByVal xL As Long) As Long

Как только это сделано, в код функции не будет передаваться ссылка на переменную; Visual Basic создаст новую временную переменную (на время работы функции-процедуры) и инициализирует ее значением входного аргумента. Все манипуляции с этой переменной никаким образом не повлияют на ту внешнюю по отношению к коду функции-процедуры переменную, которая использовалась при вызове функции-процедуры.

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

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

В листинге 7.6 приведена очень простая процедура MonthDates, которая возвращает два аргумента, передаваемых как ссылки. Процедура MonthDatesTest объявляет две переменные (BeginDate, EndDate) типа Date и вызывает процедуру MonthDates, указывая ей посредством слов ByRef, куда следует поместить результат работы.

Листинг 7.6. Модификация функции-процедуры int_intF

1 Sub MonthDates(ByRef D_begin As Date, ByRef D_end As Date)

2 'Формирование диапазона дат внутри текущего месяца

3 D_begin = Date - Day(Date) + 1

4 D_end = D_begin + 30

5 If Month(D_begin) < Month(D_end) Then

6 D_end = D_end - 1

7 End If

8 End Sub

10 Sub MonthDatesTest()

11 'тестирование процедуры MonthDates

12 Dim BeginDate As Date, EndDate As Date

13 Call MonthDates(BeginDate, EndDate)

14 MsgBox BeginDate & "-" & EndDate

16 End Sub

Использование необязательных аргументов

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

Например, код в листинге 7.7 имеет все необязательные аргументы.

 

 

Листинг 7.7. Использование необязательных аргументов

1 Dim strName As String

2 Dim strAddress As String

4 Sub ListText(Optional x As String, Optional y _

5 As String)

6 List1.AddItem x

7 List1.AddItem y

8 End Sub

10 Private Sub Command1_Click()

11 strName = "yourname"

12 strAddress = 12345 ' оба аргумента заданы

13 Call ListText(strName, strAddress)

14 End Sub

Код в листинге 7.8 имеет не все необязательные аргументы.

Листинг 7.8. Использование необязательных аргументов

1 Dim strName As String

2 Dim varAddress As Variant

4 Sub ListText(x As String, Optional y As Variant)

5 List1.AddItem x

6 If Not IsMissing(y) Then

7 List1.AddItem y

8 End If

9 End Sub

11 Private Sub Command1_Click()

12 strName = "yourname" ' второй аргумент не задан

13 Call ListText(strName)

14 End Sub

Если необязательный аргумент не предоставляется, ему присваивается значение Empty. Чтобы узнать, был ли при вызове процедуры использован аргумент, следует использовать функцию IsMissing.

Довольно часто в различных приложениях, например, связанных с базами данных, приходится создавать уникальные строки для использования их в качестве кодов некоторых объектов, например, для заполнения ключевых полей. Известно, что во многих системах программирования баз данных, например, в Access имеется механизм формирования значений ключевого поля (типа Счетчик), но он не всегда удобен: иногда в качестве значений ключевого поля необходимо (по тем или иным причинам) использовать текстовые строки, например, для обеспечения связи с таблицами, хранящимися в не Access-подобных базах данных. Обычно для этих целей используют генераторы случайных (чаще всего — псевдослучайных) чисел и (при необходимости) переводят их в строки. (Иногда также некоторыми способами преобразуют значения дат.) Если получаемые строки используются и создаются в течение долгого времени, существует вероятность совпадения новых строк со старыми. Чем меньше длина строки, используемая в качестве ключа, тем больше вероятность появления повторяющегося значения.

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

"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

Используя символы этой строки, мы можем формировать, например, такие строки (значения ключа) длиной 4 символа:

"0123", "BCDE", "AQRS", …

Можно расширить диапазон значений ключа посредством следующего набора (не забывайте и о символах кириллицы):

"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnoprstuvwxyz"

При формировании нового ключа будем рассматривать последний набор как набор цифр 62-ричной системы счисления и каждый новый ключ будем формировать как строковое выражение числа, на единицу большего, чем наибольшее из всех значений, имеющихся в поле ключа. Поскольку задача получения наибольшей строки легко выполняется операторами самой СУБД, нам остается только получить из имеющейся строки (некоторой длины) другую строку, которая будет на «единицу» больше. Слово «единица» выделено здесь кавычками, поскольку подразумевается, что символы в применяемом для формирования строк наборе необязательно должны иметь кодировку, отличающуюся на единицу. Важно только, чтобы набор был упорядочен по возрастанию. Можно, например, воспользоваться следующим набором:

"02468ABDEGHJKMNPQSTVWYZabcdfghiklmnprstvwxy"

Задача формирования уникальных значений может быть дополнена условием, что значения ключа должны находиться в некотором диапазоне. Если, например, записи из таблиц с одинаковыми ключевыми полями в определенный момент объединяются, то возникает задача одновременного и независимого формирования ключей, которые в момент объединения данных в одной таблице не должны совпадать. Эту задачу можно решить, например, использованием разных наборов символов для разных таблиц.

В листинге 7.9 представлена VB-функция, возвращающая строку такой же длины, что и входной параметр, но отличающаяся, по крайней мере, на один символ. Массив возможных значений символов указывается в строке-аргументе mas1, который является аргументом, имеющим значение по умолчанию.

Листинг 7.9. Генерация строки, «большей» на «единицу», чем входная строка

1 Function NetxCode(PrevCode As String, _

2 Optional mas1 As String = _

3 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") As String

4 'функция возвращает строку такой же длины, что и входной

5 'параметр, но отличающуюся, по крайней мере, на один символ

7 Dim LenMas1 As Integer, LenMasNewCode As Integer

8 Dim s As String

10 LenMas1 = Len(mas1) 'длина строки mas1

11 LenMasNewCode = Len(PrevCode) 'длина строки PrevCode

13 NetxCode = ""

15 'замена символов входной строки на новые,

16 'начиная с последнего символа:

17 For i = LenMasNewCode To 1 Step -1

18 s = Mid(PrevCode, i, 1) 'текущий PrevCode-символ

19 'положение i-го символа в заданном диапазоне:

20 posi = InStr(1, mas1, s)

21 If posi = LenMas1 Then

22 'символ последний в диапазоне:

23 NetxCode = Mid(mas1, 1, 1) & NetxCode

24 Else

25 'выбор следующего символа из диапазона

26 'и окончание работы функции:

27 NetxCode = Mid(PrevCode, 1, i - 1) & _

28 Mid(mas1, posi + 1, 1) & NetxCode

29 Exit For

30 End If

31 Next

33 End Function

С использованием цикла с отрицательным шагом (строки 17–31) входная строка PrevCode просматривается, начиная с последнего символа; определяется позиция последнего символа в заданном для него диапазоне. Если текущий рассматриваемый символ не совпадает с последним символом допустимого диапазона (находится в середине диапазона), то в выходной строке только этот символ заменяется на следующий символ из диапазона, задаваемого строкой mas1; на этом работа алгоритма заканчивается. Если же рассматриваемый символ совпадает с последним символом заданного диапазона, то в выходной строке он заменяется на первый символ диапазона строки mas1, а с символом, стоящим во входной строке слева от рассматриваемого, производятся те же действия, т.е. он становится текущим рассматриваемым символом.

Для положения текущего символа в диапазоне (в подстроке) используется функция InStr, позволяющая найти положение одной строки в другой строке. Функция Mid используется для выделения указанного (индексом) символа из строки. Следующий оператор выведет на экран строку "ABBA | PQYH":

MsgBox NetxCode1("ABB9", "0123456789ABCDEFG") _

& " | " & NetxCode1("PQXZ", "HIJKLMNOPQRSTUVWXYZ")

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

Использование неопределенного количества аргументов

Обычно число аргументов при вызове процедуры должно совпадать с числом аргументов, которые были указаны при описании процедуры. При помощи ключевого слова ParamArray вы можете передавать процедуре произвольное число аргументов. Например, можно написать функцию умножения, приведенную в листинге 7.10.

Листинг 7.10. Использование ключевого слова ParamArray

1 Dim x As Integer

2 Dim y As Integer

3 Dim intProd As Integer

5 Sub Prod(ParamArray intNums())

6 For Each x In intNums

7 y = y * x

8 Next x

9 intProd = y

10 End Sub

12 Private Sub Command1_Click()

13 Prod 1, 3, 5, 7, 8

14 List1.AddItem intSum

15 End Sub

Использование именованных аргументов для вызова процедуры

В Visual Basic поддерживается вызов процедур и функций с помощью именованных аргументов (named arguments). Синтаксис такого вызова следующий:

Синтаксис

<имя_процедуры> <имя_аргумента1>:=<значение1> _

[,<имя_аргументаN>:=<значениеN>]

или

<возвращаемое_значение>=<имя_процедуры> (<имя_аргумента1>:=<значение1> _

[,<имя_аргументаN>:=<значениеN>])

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

MsgBox Buttons:=vbOKOnly, _

Title:="Title", Prompt:="Prompt"

Этот оператор можно было бы написать и так:

answer_kod = MsgBox(Buttons:=vbOKOnly, _

Title:="Title", Prompt:="Prompt")

Следует отметить, что даже, если почти не смотреть на экран монитора, трудно не заметить, что для встроенных функций редактор Visual Basic постоянно предлагает вам помощь в виде подсказки со списком именованных аргументов. При этом необязательные аргументы заключаются в квадратные скобки.

Рекурсия

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

<$I[] Рекурсивная (recursive)> Рекурсивная функция или процедура — это функция или процедура, которая вызывает сама себя. Почти во всех случаях, рекурсия является ошибкой программирования и приводит к полному сбою программы. Наиболее общим симптомом возникновения этой проблемы является ошибка из-за нехватки памяти или ошибка из-за нехватки памяти в стеке. <$I[] Стек (stack)> Стек (stack) — это временная рабочая область компьютерной памяти. Для сохранения внутренних результатов выражений, копий аргументов функций, передаваемых по значению, результатов функций-процедур и в других случаях, когда необходима временная рабочая область, используется <$I[]стековая память> стековая память.

Замечание

Стек (stack) — это список элементов, доступных только в одном конце списка. Элементы добавляются или удаляются из списка только в вершине (top) стека. Стек является одной из наиболее используемых и важных структур данных. Например, распознавание синтаксиса в компиляторе, оценка выражений, передача параметров процедурам, выполнение вызова процедур и возращение из него основаны на использовании стеков.

Примеры рекурсивных функций

Самый простой пример, который может продемонстрировать рекурсивную функцию, это — пример функции возведения числа в степень. Алгоритм функции fPower основан на том факте, что число, возведенное в степень n, равно этому же числу, умноженному само на себя в степени n–1. Например, 23 равно 2×23-1 или 2×22 .

Листинг 7.11 содержит код функции fPower, возвращающей степень числа. В качестве аргументов функция принимает число и степень, в которую нужно возвести это число. Чтобы протестировать функцию fPower, поместите на форму кнопку, не меняя ее имени по умолчанию, и поместите в модуль формы код листинга 7.11. После запуска приложения щелкните на кнопке Command1.

Листинг 7.11. Функция fPower: пример рекурсии

1 Function fPower(num As Double, pwr As Integer) As Double

2 'рекурсивное возведение в степень

3 If pwr = 0 Then

4 fPower = 1 'окончание рекурсии

5 Else

6 fPower = num * fPower(num, pwr - 1) 'рекурсия

7 End If

8 End Function

10 Sub Command1_Click()

11 List1.AddItem fPower(2, 3)

12 End Sub

Функция fPower имеет два обязательных аргумента: num и pwr, и возвращает результат типа Double. В строке 6 функция вызывает сама себя. Чтобы понять, как работает функция fPower, рассмотрим порядок выполнения кода функции, вызванной процедурой Test_fPower. В этом случае при первом обращении аргумент num (и всегда) имеет значение 2, а pwr — значение 3. В строке 3 начинается оператор If…Then, который определяет момент прекращения рекурсии. Здесь проверяется, равно ли значение переменной pwr нулю. В этом вызове значение pwr равно 3, поэтому выполнение функции продолжается в строке 6.

В строке 6 выполняется присваивание функции; выражение, присваиваемое результату функции fPower, указывает на то, что результат функции равен значению num, умноженному на результат следующего вызова функции fPower со вторым аргументом, равным pwr-1. Если подставить литеральные промежуточные значения для этого примера в данное выражение, вызов функции fPower в строке 6 в этом месте будет следующим:

 

FPower(2,2)

 

Это — второй вызов функции fPower; значение аргумента pwr теперь равно 2. В строке 3 проверяется, равно ли нулю значение аргумента pwr; если — нет, снова выполняется присваивание функции в строке 6. Выражение в присваивании функции вызывает функцию fPower снова, передавая pwr-1 в качестве второго аргумента.

Если мы опять подставим литеральные промежуточные значения для этого примера в выражение в строке 6, вызовом функции fPower в этом месте будет:

 

FPower(2,1)

 

В этом третьем вызове функции fPower значение pwr теперь равно 1. В строке 3 проверяется, не равно ли нулю значение аргумента pwr; если — нет, то присваивание функции в строке 6 выполняется третий раз, приводя в результате к четвертому вызову fPower.

В четвертом вызове функции fPower значение pwr равно 0; результатом проверки (в строке 3) на равенство нулю значения аргумента pwr является значение True, и выполняется строка 4 с завершением рекурсии. Строка 4 — это еще один оператор присваивания функции, на этот раз присваивающий число 1 как результат функции. (Любое число, возведенное в степень 0, равно 1).

В вызовах 1–3 функции fPower функция не возвратила никакого результата; Visual Basic не может полностью вычислить выражение в операторе присваивания функции (строка 6) до тех пор, пока серия рекурсивных вызовов не прекращается в четвертом вызове функции fPower. Теперь, когда функция fPower возвращает результат, рекурсивный процесс прекращается и каждый отдельный вызов функции fPower возвращает ее результат.

Четвертый вызов fPower возвращает число 1 в выражение в строке 6 третьего вызова функции fPower. Подставим литеральные промежуточные значения из примера, и это выражение будет равно 2×1. Результат, возвращаемый третьим вызовом функции fPower, равен 2.

Visual Basic возвращает это значение (2) выражению присваивания функции во втором вызове функции так, что (подставляем литеральные промежуточные значения) это выражение равно теперь 2×2. Второй вызов функции fPower возвращает 4 выражению присваивания функции первого вызова fPower.

Снова подставляются литеральные значения, и выражение из первого вызова функции fPower равно теперь 2×4. Наконец, исходный первый вызов функции fPower возвращает значение 8 — правильный результат возведения 2 в третью степень.

Если вы хотите проследить последовательность передачи аргументов функции fPower, поместите на форму окно списка и вставьте перед строкой 3 следующий оператор:

 

List1.AddItem num & " и " & pwr

При этом в результате тестирования предыдущего примера вы увидите в окне элемента ListBox с именем List1 последовательность вызовов функции fPower и результирующее значение (рис. 7.4):

Рис. 7.4

Окно с промежуточными (и окончательным) результатами работы функции fPower

На самом деле, нет необходимости писать функцию fPower: VB-операция возведения в степень (^) имеет такой же результат. Функция fPower была выбрана для иллюстрации рекурсии, потому что она предоставляет самый простой пример работы рекурсии.

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

1) имеются два целых числа a и b; 2) если остаток от деления a на b равен нулю, то b — уже наибольший общий делитель, иначе — выполнить: (a0 = b), (a1= a Mod b), a = a0, b = a1 и вернуться к началу пункта 2.

Листинг 7.12. Функция GCD: пример рекурсии

1 Function GCD(a As Integer, b As Integer) As Integer

2 If (a Mod b) = 0 Then

3 GCD = b

4 Else

5 GCD = GCD(b, (a Mod b))

6 End If

7 End Function

9 Sub Command1_Click ()

10 Dim d As Integer

11 List1.AddItem GCD(a:=342, b:=612)

12 End Sub

Структура кода этой рекурсивной функции очень похожа на структуру предыдущего примера, поэтому, видимо, нет необходимости анализировать ее так же подробно. В результате тестирования функции GCD при помощи процедуры Command1_Click будет получен результат 18, который и является наибольшим общим делителем исходных чисел.

Если вы хотите проследить последовательность передачи аргументов функции GCD, вставьте перед строкой 2 следующий оператор:

 

List1.AddItem "a=" & a & " b=" & b

При этом в результате тестирования предыдущего примера вы увидите в окне элемента ListBox с именем List1 последовательность вызовов функции GCD и результирующее значение (рис. 7.5):

Рис. 7.5

Окно с промежуточными (и окончательным) результатами работы функции GCD

 

Как избежать случайной рекурсии и других проблем

Теперь, когда вы знаете о том, как работает рекурсивная функция-процедура, вы готовы к рассмотрению недостатков рекурсивных программ. Одним из наиболее очевидных недостатков рекурсивных функций и процедур является то, что их часто трудно понимать. Другой недостаток заключается в том, что они используют большой объем памяти; каждый раз, когда рекурсивная функция вызывает сама себя, Visual Basic передает все ее аргументы снова и для результата функции резервируется область памяти. Объем памяти, используемый серией вызовов рекурсивной функции, может быть значительным в зависимости от размера аргументов (в байтах) и размера возвращаемого функцией значения (в байтах).

Почти всякую задачу, которую можно выполнить с помощью рекурсивной функции, можно также выполнить с помощью структуры цикла без дополнительных потерь памяти; структуру цикла часто легче понять, чем функцию, выполняющую работу посредством рекурсии. (Структуры цикла описываются в главе 9.)

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

Если вы случайно создали рекурсивную функцию-процедуру, маловероятно, что рекурсия когда-либо закончится. Рекурсивные функции должны разрабатываться очень тщательно, чтобы обязательно было какое-либо условие, прекращающее рекурсивные вызовы. Если функция не тестирует какое-либо условие, чтобы явно завершать рекурсию (как делает функция fPower), вызовы рекурсивной функции продолжаются до тех пор, пока не заканчивается память (с возникновением ошибки времени исполнения).

Обычно при выполнении функции, которая содержит нечаянный рекурсивный вызов, компьютер как бы прерывает работу на некоторое время перед тем, как отобразится ошибка времени исполнения из-за нехватки памяти.

 


[1] В некоторых языках программирования при написании процедур и функций входные и выходные аргументы указываются совершенно определенно. В Visual Basic такого разделения нет, но сам программист, конечно, может сказать, какой аргумент является входным, а какой — выходным.

[2] Их также можно называть просто «функциями».

Date: 2015-09-02; view: 547; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.007 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию