Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Передача параметров по ссылке и по значению

Поиск

Передача параметров по значению - еще один способ повысить надежность программирования. Рассмотрим пример.

Вот процедура Квадр, вычисляющая периметр и площадь квадрата по его стороне:

Dim A As Integer 'сторона

Dim P As Integer 'периметр

Dim S As Integer 'площадь

 

Private Sub Квадр(Сторона As Integer, Периметр As Integer, Площадь As Integer)

Периметр = 4 * Сторона

Площадь = Сторона ^ 2

End Sub

 

Private Sub Command1_Click()

A = 10

Квадр A, P, S

Debug.Print "Сторона="; A; "Периметр="; P; "Площадь="; S

End Sub

Результат:

Сторона= 10 Периметр= 40 Площадь= 100

Здесь щелчком по кнопке вы приказываете компьютеру вычислить периметр и площадь квадрата со стороной 10. Компьютер, выполняя тело процедуры Квадр и присваивая значения переменным Периметр и Площадь, тем самым присваивает значения переменным P и S, то есть изменяет содержимое ячеек памяти, отведенных под эти переменные. Говорят, что в этом случае между вызывающей и вызываемой процедурами осуществляется передача параметров по ссылке. Чтобы подчеркнуть, что вы используете именно этот способ передачи параметров, вы можете записать заголовок процедуры так:

Private Sub Квадр (Сторона As Integer, ByRef Периметр As Integer, ByRef Площадь As Integer)

хоть это и излишне.

При этом способе вызываемая процедура получает полный контроль над переменными A, P и S вызывающей процедуры и может присваивать им все, что хочет. Это удобно, но небезопасно. Ведь в этом случае переменные становятся беззащитными против ошибок в вызываемой процедуре. Так, программист может случайно, для каких-то других нужд, включить в процедуру какой-нибудь оператор, меняющий значение параметра Сторона, например, Сторона=1. Предположим, в этом случае процедура примет такой вид:

Private Sub Квадр(Сторона As Integer, Периметр As Integer, Площадь As Integer)

Периметр = 4 * Сторона

Площадь = Сторона ^ 2

Сторона = 1

End Sub

Тогда результаты будут напечатаны неверно:

Сторона= 1 Периметр= 40 Площадь= 100

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

Private Sub Квадр (ByVal Сторона As Integer, Периметр As Integer, Площадь As Integer)

Теперь, что бы ни произошло с параметром Сторона, значение переменной A меняться не будет. Убедитесь, что теперь снова все в порядке. Такой способ передачи параметров называется передачей параметров по значению.

Не переборщите с надежностью. Так, глупо было бы писать

Private Sub Квадр (ByVal Сторона As Integer, ByVal Периметр As Integer, ByVal Площадь As Integer)

так как результат в этом случае был бы такой:

Сторона= 10 Периметр= 0 Площадь= 0

 

Задание 130: На двух метеостанциях (A и B) в течение года измерялась температура. Соответственно созданы два массива чисел длиной 365. Затем оказалось, что на обеих станциях термометры были испорчены: на станции A термометр все время показывал температуру на 2 градуса выше настоящей, а на станции B - на 3 градуса ниже. Написать процедуру с двумя параметрами, которая исправляет один исходный массив и с ее помощью исправить оба массива. Один параметр - величина поправки, другой - массив температур.

Индукция. Рекурсия

Понятие рекурсии - сложное, но необходимое понятие для программиста.

Здесь мне никуда не уйти от классического примера о факториале. Факториалом целого положительного числа N называется произведение всех целых чисел от 1 до N. Например, факториал пяти равен 1*2*3*4*5, то есть 120. Факториал единицы считается равным 1.

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

"Факториал единицы равен 1. Факториал любого целого положительного числа N, большего единицы, равен числу N, умноженному на факториал числа N-1 ."

Если вам уже все ясно, значит вы - профессор математики. Для обычных людей поясню. Возьмем какое-нибудь конкретное N, например, 100. Тогда ужасное определение будет звучать проще: Факториал числа 100 равен числу 100, умноженному на факториал числа 99.

Ну и что? И как же отсюда узнать, чему равен какой-нибудь конкретный факториал, скажем, факториал трех? Будем рассуждать также совершенно чудовищным образом:

 

Смотрю в определение: Факториал трех равен 3 умножить на факториал двух. Не знаю, чему равен факториал двух. Поэтому спускаюсь на ступеньку ниже.

 

Смотрю в определение: Факториал двух равен 2 умножить на факториал единицы. Не знаю, сколько это. Спускаюсь еще на ступеньку.

 

Смотрю в определение: Факториал единицы равен 1. Вот, наконец-то - впервые узнал конкретное число. Значит можно подниматься обратно.

 

Поднимаюсь на одну ступеньку. Факториал двух равен 2 умножить на 1, то есть 2. Хорошо.

 

Поднимаюсь еще на ступеньку. Факториал трех равен 3 умножить на 2, то есть 6. Задача решена!

 

Рассуждая таким образом, можно вычислить факториал любого числа. Этот способ рассуждения называется рекурсивным.

:

Какое отношение все это имеет к компьютерам? Дело в том, что рекурсивный способ рассуждений реализован во многих языках программирования, в том числе - и в Visual Basic. Значит, этим языкам должен быть понятен и индуктивный способ написания программ.

Обозначим кратко факториал числа N, как Factorial(N), и снова повторим наш индуктивный способ объяснения:

"Если N=1, то Factorial(N) = 1.

Если N>1, то Factorial(N) вычисляется умножением N на Factorial(N-1) ."

 

В соответствии с этим объяснением напишем на Visual Basic функцию Factorial для вычисления факториала:

Private Function Factorial(ByVal N As Integer) As Long

If N = 1 Then Factorial = 1

If N > 1 Then Factorial = N * Factorial(N - 1)

End Function

 

Private Sub Command1_Click()

Debug.Print Factorial(3)

End Sub

Что самое удивительное - функция работает! Несмотря на то, что в программе нигде не употребляется оператор цикла. Вся соль программы в том, что функция Factorial вместо этого включает в себя вызов самой себя - Factorial(N-1).

:

Что же происходит в компьютере во время выполнения программы? Механизм происходящего в точности соответствует нашему путешествию по ступенькам:

 

Все начинается с того, что мы щелкаем по кнопке и Visual Basic пробует выполнить строку Debug.Print Factorial(3). Для этого он вызывает функцию Factorial. Выполнение подпрограммы начинается с того, что в памяти отводится место для всех параметров и локальных переменных, а значит и для нашего параметра N. Затем число 3 подставляется на место параметра N, то есть в память в ячейку N посылается 3. Затем выполняется тело функции. Так как 3>1, то Visual Basic пытается выполнить умножение 3* Factorial(3-1) и сталкивается с необходимостью знать значение функции Factorial(2), для чего вызывает ее, то есть отправляется ее выполнять, недовыполнив Factorial(3), но предварительно запомнив, куда возвращаться.

 

Спускаюсь на ступеньку ниже. В соседнем месте памяти отводится место для N. Это уже другое N, путать их нельзя! В эту ячейку N посылается 2. Затем выполняется тело функции. Пусть вас не смущает, что Visual Basic второй раз выполняет тело функции, не закончив его выполнять в первый раз. Так как 2>1, то Visual Basic пытается выполнить умножение 2* Factorial(2-1) и сталкивается с необходимостью знать значение функции Factorial(1), для чего вызывает ее.

 

Спускаюсь еще на ступеньку. В соседнем месте памяти отводится место еще для одного N. В эту ячейку N посылается 1. Затем выполняется тело функции. Так как 1=1, то Visual Basic вычисляет Factorial=1. Вот - впервые конкретное число. Затем Visual Basic пытается выполнить следующую строку if N>1 then Factorial = N* Factorial(N-1). Поскольку нельзя сказать, что 1>1, то строка не выполняется и выполнение тела функции закончено. Значит можно подниматься.

 

Поднимаюсь на одну ступеньку. Visual Basic возвращается внутрь тела функции (той, где N=2) и успешно выполняет умножение - Factorial =2*1=2.

 

Поднимаюсь еще на ступеньку. Visual Basic возвращается внутрь тела функции (той, где N=3) и успешно выполняет умножение - Factorial =3*2=6. Задача решена.

 

Итак, рекурсией в программировании называется вызов подпрограммы из тела самой подпрограммы.

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

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

 

Задание 131: Напишите рекурсивную функцию fib для вычисления чисел Фибоначчи.

Сортировка

Здесь вы не узнаете ничего нового о Visual Basic. Будем совершенствовать технику программирования.

 

Пусть имеется ряд чисел: 8 2 5 4. Под сортировкой понимают их упорядочивание по возрастанию (2 4 5 8) или убыванию (8 5 4 2). Сортировать можно и строки (как слова в словаре).

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

 

Задача: Задан массив из 100 произвольных положительных чисел. Отсортировать его по возрастанию.

Идея решения: Если мы не можем сходу запрограммировать задачу, нужно подробно представить себе, в каком порядке мы решали бы ее вручную, без компьютера. Как бы мы сами сортировали 100 чисел? Мы бы запаслись пустым листом бумаги из 100 клеток. Затем нашли бы в исходном массиве максимальное число и записали его в самую правую клетку, а в исходном массиве на его месте записали бы число, меньшее самого маленького в массиве (в нашем случае подойдет 0). Затем нашли бы в изменившемся исходном массиве новое максимальное число и записали его на второе справа место, а на его место в исходном массиве - 0. И так далее.

Вот программа для 10 чисел:

Const N = 10 'N - размер массива

Dim massiv_ishodn(1 To N) As Integer 'Это исходный массив

Dim massiv_rezult(1 To N) As Integer 'Это наш пустой лист бумаги

 

'Вспомогательная функция для поиска максимума в массиве m(1 To N). Она выдает значение

'максимального элемента (maximum) и заодно номер этого элемента (Nomer_max):

Private Function maximum(m, N As Integer, Nomer_max As Integer) As Integer

Dim i As Integer, max As Integer

max = m(1): Nomer_max = 1 'max - "временный" максимум

For i = 2 To N

If max < m(i) Then

max = m(i)

Nomer_max = i

End If

maximum = max

Next

End Function

 

'Основная процедура сортировки исходного вектора mass_ish размера N в результирующий - mass_rez:

Private Sub sortirovka(mass_ish, N As Integer, mass_rez)

Dim Nom_max As Integer

For i = 1 To N

mass_rez(N + 1 - i) = maximum(mass_ish, N, Nom_max) 'Пишем "в правую клетку"

mass_ish(Nom_max) = 0 'Ноль - на старое место

Next

End Sub

 

Private Sub Command1_Click()

massiv_ishodn(1) = 41 'Задаем значения элементов исходного массива

massiv_ishodn(2) = 8

massiv_ishodn(3) = 17

massiv_ishodn(4) = 82

massiv_ishodn(5) = 20

massiv_ishodn(6) = 2

massiv_ishodn(7) = 30

massiv_ishodn(8) = 12

massiv_ishodn(9) = 6

massiv_ishodn(10) = 9

 

sortirovka massiv_ishodn, N, massiv_rezult 'Сортируем массив

 

For i = 1 To N

Debug.Print massiv_rezult(i); 'Распечатываем отсортированный массив

Next

End Sub

 

Функция maximum, кроме того, что сама имеет значение максимального элемента массива, выдает еще порядковый номер максимального элемента - Nomer_max. Это называется побочным эффектом функции.

 

Методов сортировки много. Приведенный метод - самый примитивный. Мало того, что нам пришлось расходовать память на второй массив, для выполнения сортировки массива из 100 элементов понадобилось бы около 100*100=10000 операций сравнения элементов массива между собой.

Существуют методы гораздо более эффективные. Приведу один из них - метод пузырька. Представьте себе тонкую вертикальную трубку с водой. Запустим снизу пузырек воздуха. Он поднимется до самого верха и остановится. Затем пустим еще один. Он поднимется наверх и остановится сразу же под первым. Затем запустим третий и так далее все сто пузырьков.

А теперь представим, что это не трубка, а наш исходный массив, а вместо пузырьков поднимаются максимальные элементы.

Вот алгоритм: Сравним первый элемент массива со вторым, и если второй больше, то ничего не делаем, а если первый больше, то меняем местами первый и второй элементы. В этом вся соль метода. Затем повторяем это со вторым и третьим элементами - если третий больше, то ничего не делаем, а если второй больше, то меняем местами второй и третий элементы. Затем повторяем все это с третьим и четвертым элементами и так далее. Где-то по пути мы встретим максимальный элемент, и он, как пузырек, поднимется у нас до самого верха.

Теперь, когда мы знаем, что элемент номер 100 у нас самый большой, нам предстоит решить задачу сортировки для массива из остальных 99 элементов. Метод тот же. Запускаем второй пузырек и так далее.

Метод пузырька не требует второго массива, да и сравнений здесь в два раза меньше.

Вот программа:

Const N = 10 'N - размер массива

Dim massiv(1 To N) As Integer 'Это массив

 

'Сортировка массива mass размером Razmer:

Private Sub puziryok(mass, Razmer As Integer)

Dim i As Integer

For m = Razmer To 2 Step -1 'Всего пузырьков - 9

For i = 1 To m - 1 'i увеличивается - пузырек ползет вверх

If mass(i) > mass(i + 1) Then 'Стоит ли обмениваться значениями

c = mass(i) 'Три оператора для обмена значений двух элементов с помощью

mass(i) = mass(i + 1) 'транзитного элемента c

mass(i + 1) = c

End If

Next i

Next m

End Sub

 

Private Sub Command1_Click()

massiv(1) = 41 'Задаем значения элементов массива

massiv(2) = 8

massiv(3) = 17

massiv(4) = 82

massiv(5) = 20

massiv(6) = 2

massiv(7) = 30

massiv(8) = 12

massiv(9) = 6

massiv(10) = 9

 

puziryok massiv, N 'Сортируем массив

 

For i = 1 To N

Debug.Print massiv(i); 'Распечатываем отсортированный массив

Next

End Sub

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

Задание 132: Отсортируйте по возрастанию двумерный массив. Я имею в виду - нужно сделать так, чтобы:

а(1,1) <= a(1,2) <=... <= a(1,N) <=

<= a(2,1) <= a(2,2) <=... <= a(2,N) <=

<= a(3,1) <= a(3,2) <=...



Поделиться:


Последнее изменение этой страницы: 2016-08-12; просмотров: 205; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.188.132.71 (0.009 с.)