Visual Basic_учебник (1108585), страница 12
Текст из файла (страница 12)
В начале каждой итерации, очевидно, необходимо вычислить значения аргумента ифункции в середине отрезка.Do While Math.Abs(b - a) > 2 * epsDim c As Double = (a + b) / 2Dim Fc As Double = Math.Exp(-c) - cТеперь нам необходимо сравнить знаки переменных Fa, Fb и Fс, и, взависимости от результатов, переопределить значения переменных(a,b) либо на значения (a,c), либо на (с,b).Можно выразить условие различия знаков переменных Fa и Fс непосредственно: Fa < 0 And Fc >= 0 Or Fa >= 0 And Fc < 0.
Обратите внимание: приоритет операции And выше Or (аналогично тому,как в арифметических выражениях приоритет умножения выше сложения). Можно сократить приведенную запись с учетом свойств опера-84ции умножения: Fa * Fc < 0, а можно воспользоваться функциейSign (см. таблицу 3): Math.Sign(Fa) <> Math.Sign(Fc).Теперь необходимо выполнить разный набор предложений в зависимости от вычисленного значения условия и завершить цикл.If Math.Sign(Fa) <> Math.Sign(Fc) Thenb = cFb = FcElsea = cFa = FcEnd IfLoopConsole.WriteLine("Решение: {0}", (a + b) / 2)Из двух приведенных выше фрагментов получился законченный и довольно сложный программный цикл. Обсудим некоторые его свойства.Во-первых, для правильной работы алгоритма перед началом циклазнаки переменных Fa и Fb должны быть различны.
В рассуждениях освойствах программ подобное требование называется предусловием. Вданном случае проверить предусловие можно во время составленияпрограммы, так как исходные границы отрезка и функция фиксированы. В общем же случае для правильной работы программы необходимо проверить предусловие и сообщить об ошибке, если оно не выполнено.В процессе исполнения каждой итерации одна из переменных Fa илиFb меняет значение.
Но в конце цикла они обязательно будут иметьразные знаки. И это можно доказать! Действительно, значения этихпеременных меняются только в конструкции If … End If. Возможныдва варианта. Если условие в If выполнено, то знаки Fa и Fc отличаются. Значение Fb устанавливается по текущему значению Fc и знакэтого значения также отличен от Fa. Поэтому по завершении итерации85Fa и Fb будут иметь разные знаки. Второй вариант: знаки Fa и Fc оди-наковы.
Но из предусловия следует, что знаки Fa и Fb различны. Следовательно, знаки Fc и Fb различны. Так как значение Fa устанавливается по текущему значению Fc, то и в этом случае по завершении итерации Fa и Fb будут иметь разные знаки.Предусловие, продолжающее быть верным по завершении каждойитерации, называется инвариантом цикла. Правильная формулировкаинварианта является ключом к написанию сложных программ. Еслипрограмма работает неправильно, то одним из первых шагов должнабыть вставка диагностических предложений, осуществляющих проверку выполнения инварианта в процессе исполнения программы.В рассматриваемом случае инвариантом цикла является следующееусловие: = , = , ( ) ≠ ( ). Поэтому для отладки можно временно добавить в предыдущем фрагменте программы перед Loop следующее предложение:If Fa <> Math.Exp(-a) - a _Or Fb <> Math.Exp(-b) - b _Or Math.Sign(Fa) = Math.Sign(Fb) ThenConsole.WriteLine("!!!ОШИБКА")End IfЕсли цикл запрограммирован неправильно и на каком-либо этапе инвариант перестанет выполняться, программа во время исполненияначнет печатать строки с сообщением об ошибке.
Это будет поводомболее внимательно проверить текст программы на предмет несоответствия требуемому алгоритму.Приведем теперь программу решения уравнения целиком.86Sub Main()' Поиск корня уравнения exp(-x)-x = 0 на [0,1]Dim a As Double = 0Dim b As Double = 1Dim eps As Double = 0.000001Dim Fa = Math.Exp(-a) - aDim Fb = Math.Exp(-b) - bDo While Math.Abs(b - a) > 2 * epsDim c = (a + b) / 2Dim Fc = Math.Exp(-c) - cIf Math.Sign(Fa) <> Math.Sign(Fc) Thenb = cFb = FcElsea = cFa = FcEnd If' Диагностика инварианта цикла необходима' только для выявления возможных ошибокIf Fa <> Math.Exp(-a) - a _Or Fb <> Math.Exp(-b) - b _Or Math.Sign(Fa) = Math.Sign(Fb) ThenConsole.WriteLine("!!!ОШИБКА")End IfLoopConsole.WriteLine("Решение: {0}", (a + b) / 2)Console.ReadLine()End Sub87Глава 8.
Подпрограммы и функцииВ конце предыдущей главы приведена программа для поиска решенияуравнения − − = 0. Очевидно, что это лишь частный случай, тот жесамый алгоритм метода дихотомии может быть использован для решения любого алгебраического уравнения, записанного в виде = 0. Нужно лишь предварительно найти такие два аргумента и, что функция () непрерывна на отрезке [, ] и принимает на концах этого отрезка значения разных знаков.Предположим, мы хотим заменить уравнение, решаемое программой.Для этого нам придется заменить выражение, вычисляющее функциюв нескольких разных предложениях программы (самостоятельно найдите все 5 вычислений функции в программе на стр. 87).
А если длявычисления функции недостаточно простого арифметического выражения, а требуется реализовать сложный алгоритм?При помощи конструкции Function … End Function мы можем отдельно записать алгоритм вычисления функции, не конкретизируя значения ее параметра, а затем многократно применять этот алгоритм восновной программе путем вызова функции от определенного аргумента.Вначале изучите модифицированную программу решения уравненияметодом деления отрезка пополам, а затем мы подробно обсудимобщие правила записи функций и подпрограмм.88Module Module1Function f(ByVal x As Double) As DoubleReturn Math.Exp(-x) - xEnd FunctionSub Main()' Поиск корня уравнения exp(-x)-x = 0 на [0,1]Dim a As Double = 0Dim b As Double = 1Dim c As DoubleDim eps As Double = 0.000001Dim Fa = f(a)Dim Fb = f(b)Do While Math.Abs(b - a) > 2 * epsc = (a + b) / 2Dim Fc = f(c)If Math.Sign(Fa) <> Math.Sign(Fc) Thenb = cFb = FcElsea = cFa = FcEnd If' Диагностика инварианта цикла необходима' только для выявления возможных ошибокIf Fa <> f(a) Or Fb <> f(b) _Or Math.Sign(Fa) = Math.Sign(Fb) ThenConsole.WriteLine("!!!ОШИБКА")End IfLoopc = (a + b) / 2Console.WriteLine("Реш-е: x={0}, f(x)={1:F7}", _c, f(c))Console.ReadLine()End SubEnd ModuleПрежде всего, обратите внимание на то, что описание функций (конструкции Function … End Function), также как и описание подпрограмм (конструкции Sub … End Sub) должны быть вложены в тот илииной модуль (Module … End Module).
В состав модуля могут такжевходить константы и переменные, общие для всех функций и подпро89грамм данного модуля1. Более подробно переменные модуля обсуждаются ниже в подразделе «Параметры и переменные».Описание подпрограмм и функцийОписание функций и подпрограмм осуществляется похожими друг надруга синтаксическими конструкциями:Function <имя> ( [ <параметр1> [ , <параметр2> ] … ] ) _As <тип результата><блок предложений, включая Return>End FunctionSub <имя> ( [ <параметр1> [ , <параметр2> ] … ] )<блок предложений>End SubИ подпрограммы, и функции являются записями отдельных вычислительных алгоритмов, исполнение которых может зависеть от конкретных значений параметров. Принципиальное отличие состоит в том, чтоосновная задача функции – вычисление одного значения, результатафункции, а для подпрограммы такого единственного выделенного значения не определено.
Подпрограмма обычно осуществляет специфический ввод или вывод, преобразование значений общих переменныхи подобные, так называемые «побочные действия».И подпрограммы, и функции обозначаются именем. В приведеннойвыше на странице 89 программе имя функции – f, имя подпрограммы1В состав модуля могут входить описания вложенных классов (Class), структур (Structure), свойств (Property), событий (Event).
Но в данном пособии эти конструкции мы не рассматриваем.90– Main. В рамках одного модуля можно пользоваться этими сокращенными именами. Полные имена включают имя модуля: Module1.f иModule1.Main, соответственно. Это становится существенно, когдапрограмма состоит из нескольких модулей.Одним и тем же именем можно обозначать несколько функций илиподпрограмм, если они отличаются сигнатурой – параметрами и/илитипом результата. Функции с одинаковым именем являются своего рода вариантами одной и той же функции. Скажем, один из вариантовболее общий, с большим числом параметров, а другой - более частный.Например, тот факт, что круг является частным случаем эллипса, можноотразить одинаковым именем функций, вычисляющих площади этихфигур:Function Ellipse(ByVal a As Double, _ByVal b As Double) As DoubleReturn Math.PI * a * bEnd FunctionFunction Ellipse(ByVal r) As DoubleReturn Ellipse(r, r)End FunctionОбе функции можно поместить в один модуль, несмотря совпадение ихимен.
Это допустимо, так как функции отличаются числом параметров.И функции, и подпрограммы могут иметь один или несколько параметров. Параметры должны быть перечислены через запятую в предложении Function или Sub, соответственно. При этом список параметров располагают в круглых скобках сразу за именем функции илиподпрограммы. Параметры аналогичны простым переменным. Отличие лишь в том, что перед каждым исполнением функции или подпрограммы всем параметрам присваиваются начальные значения в соответствие с фактически указанными аргументами. Более подробно об91инициализации параметров в момент вызова рассказывается в следующем подразделе.Описание каждого параметра, так же, как и описание переменной,включает имя параметра и тип значения, только вместо вводного словаDim ставят либо ByVal, либо ByRef.
Таким образом, синтаксическоеправило для описания каждого параметра таково:[ ByVal | ByRef ] <имя параметра> As <имя типа>Сравните его с правилом на странице 20.Внутри конструкций Function … End Function и Sub … End Subпомещают соответствующий вычислительный алгоритм. Из чего конкретно может состоять описание алгоритма, мы достаточно подробнообсуждали во всех предыдущих главах. Несмотря на то, что до сих поралгоритм всегда помещался внутрь подпрограммы Main, те же правила относятся к любой другой подпрограмме или функции.
Здесь мыдополним список допустимых предложений лишь предложениемReturn, завершающим исполнение подпрограмм и функций.Внутри подпрограммы Sub … End Sub предложение Return необязательно. Вызов подпрограммы в этом случае приводит к последовательному исполнению всех предложений тела подпрограммы от первого до последнего, после чего осуществляется возврат к вызвавшемукоду. Предложение Return (в данном случае состоящее из одногоединственного слова) позволяет «досрочно» завершить исполнениеподпрограммы, например, при достижении в алгоритме того или иногоусловия. Отметим, что любой алгоритм можно записать без предложения Return, и практически всегда запись алгоритма без Return болееясная. Поэтому в подпрограммах использовать это предложение мы нерекомендуем.92В функциях предложение Return необходимо для того, чтобы передзавершением алгоритма указать, какое значение должно быть возвращено в качестве результата.