Научная статья на тему 'JavaScript. Занятие 7. Метод рекурсивного спуска'

JavaScript. Занятие 7. Метод рекурсивного спуска Текст научной статьи по специальности «Математика»

CC BY
488
71
i Надоели баннеры? Вы всегда можете отключить рекламу.

Аннотация научной статьи по математике, автор научной работы — Дмитриева Марина Валерьевна

Данная публикация продолжает новый курс в Заочной школе современного программирования курс по языку JavaScript. Хотя курс проходит под лозунгом "Создаем наш сайт", язык JavaScript рассматривается не только как инструмент для оформления web-страниц, но и как средство обучения программированию. На диске, прилагаемом к журналу, размещены тексты программ к этой статье.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «JavaScript. Занятие 7. Метод рекурсивного спуска»

Дмитриева Марина Валерьевна

JAVASCRIPT. ЗАНЯТИЕ 7. МЕТОД РЕКУРСИВНОГО СПУСКА

Мой учитель Святослав Сергеевич Лавров многие годы заведовал кафедрой МО ЭВМ СПбГУ и являлся руководителем лаборатории НИИММ (научно-исследовательский институт Математики и Механики), в котором работали и работают в настоящее время его ученики. По инициативе Святослава Сергеевича в университете быили начаты1 работы1 по разныим научныш направлениям, подготовлены1 новы1е курсы1 лекций. Святослав Сергеевич впервые в университете прочел курсы1 лекций «Методы1 реализации языков искусственного интеллекта», «Автоматическое доказательство теорем», «Теория программирования». Для молоды1х сотрудников, которыми мы1 быили в то время, Святослав Сергеевич организовал семинары1, на которым, в частности, обсуждались вопросы1 преподавания программирования. Он щедро делился всем тем, что знал и умел. Поражает работоспособность Святослава Сергеевича. Иногда при чтении диссертаций или научном редактировании книг учеников объем его замечаний, пояснений, дополнений быт соизмерим с размером рукописи. Я очень благодарна Святославу Сергеевичу за поддержку и в годы1 учебы1, и во время работы1.

Во время нашего сотрудничества важное место в программировании занимала теория трансляции. Многие ученики С.С. Лаврова разрабатывали трансляторы1 с разным языков для различных ЭВМ.

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

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

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

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

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

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

соединенных знаками «+» или «-». Будем называть термом либо один множитель, либо последовательность множителей, соединенных знаками «*». Назовем множителем либо константу (от 0 до 9), либо формулу, заключенную в скобки.

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

Опишем функцию valform (), которая анализирует формулу и, если формула построена правильно, вычисляет ее значение. По данному определению, формула всегда начинается с терма. Будем считать, что функция valterm() вычисляет значение для подформулы, которую мы определили как терм. После вызова функции valterm () будет вычислено значение первого терма. Если далее в формуле следует знак плюс или знак минус, то за знаком в формуле, согласно нашему определению, должен опять следовать терм. Вычислим значение и этого терма, воспользовавшись процедурой valterm(), далее можно вычислить значение подформулы с двумя термами. Процесс продолжается до тех пор, пока после анализа очередного терма текущий символ станет отличным от знака плюс или знака минус. Описание функции valform представлено в листинге 1.

Напомним, что при вычислении значения формулы мы воспользовались функцией вычисления значения терма. При описании функции опираемся на определение терма. Терм состоит либо из одного множителя, либо из множителей, соединенных знаком умножения. Поступим так, как поступили при описании функции valf orm (). Будем считать, что функция valmn () вычисляет значение формулы, определенной как множитель. Для того чтобы вычислить значение терма, следует сначала вычислить значение множителя. Анализируя знак операции, следующий за первым термом, мы либо прекращаем анализ формулы, либо вычисляем значение следующего множителя

Листинг 1. Вычисление значения формулы

function valform () { var t1 = valterm () var t2

while ((c == "+")|| (c == "-")) { var h = c c =cursym () t2 = valterm () if (h=="+") t1 = t1 + t2 else t1= t1 - t2

}

return Number(tl)

}

Листинг 2. Функция вычисления значения терма

function valterm () { var tl = valmn () var t2

while (c == "*") { c = cursym() t2 = valmn() tl = tl * t2

}

return Number(tl)

}

и т. д. Функция valterm () очень похожа на функцию valform (), ее описание дано в листинге 2.

Далее следует описать функцию valmn (). Множителем может быть константа, ее значение нам известно. Множителем может быть формула в скобках, то есть множитель мо-

жет иметь вид (F). Если же очередной символ не является ни числом, ни открывающей скобкой, то формула содержит ошибку.

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

Будем использовать логическую переменную er, значение которой изменится на true в случае определения ошибки в формуле. Приведем в листинге 3 описание функции valmn ().

Заметим, что функция valf orm () содержит вызов функции valterm (), которая содержит вызов функции valmn (), последняя, в свою очередь, содержит вызов valform (). При анализе формулы осуществлен рекурсивный спуск.

Напомним, что рассмотренная нами ранее функция eval(s) рассматривает строку s как выражение и вычисляет ее значение.

В программе для контроля работы описанных функций используется функция valtest (),

Рисунок 1. Пример работы сценария вычисления значения формулы.

которая обращается к функции eval и вычисленное с ее помощью значение запи-

Листинг 3. Вычисление значения множителя

( c <= "9"))

function valmn () { if ((c >= "1") && { var h=Number (c) c = cursym (); return h

}

else { if (c == "(" ) { c = cursym()

var t = valform()

if (c != ")") er= true else

{ c = cursym(); return Number(t)

}

else

er = true

}

сывает в соответствующее

поле формы (рисунок 1).

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

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

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

Листинг 4. Построитель формул

<HTML> <HEAD> <Т1ТЬЕ>Построитель формул </TITLE> <script>

var s=""

//функция вычисляет значение формулы function val(obj)

{ obj.m1.value=s; obj.res.value=eval(s)} //функция добавляет символ к формуле function valbut (c)

{s+=c; document.form1.m1.value=s} //-- >

</scriptX/HEAD>

<body bgcolor="#FFDCD">

<Center>

<Ь4>Построитель формул</Ь4>

<table border=0 cellspacing=5 cellpadding=5> <tr valign=top> <td align=center > <form name ="form1">

<textarea name="m1" cols=30 rows=3X/textareaX/td></tr> <tr valign=middle><td >

<input type =button value= 1 onclick= 'valbut( '1') ">

<input type =button value= 2 onclick= 'valbut( '2') ">

<input type =button value= 3 onclick= 'valbut( '3') ">

<input type =button value= 4 onclick= 'valbut( '4') ">

<input type =button value= 5 onclick= 'valbut( '5') ">

<input type =button value= 6 onclick= 'valbut( '6') ">

<input type =button value= 7 onclick= 'valbut( '7') ">

<input type =button value= 8 onclick= 'valbut( '8') ">

<input type =button value= 9 onclick= 'valbut( '9') ">

<input type =button value= 0 onclick= 'valbut( '0') ">

<input type =button value= + onclick= 'valbut( ' + ') ">

<input type =button value= - onclick= 'valbut( l-1) ">

<input type =button value= * onclick= 'valbut( ">

<input type =button value= / onclick= 'valbut( ">

<input type =button value= ( onclick= 'valbut( ">

<input type =button value= ) onclick= 'valbut( " ><br>

<input type =button value= Вычислить onclick= " val (forml) "Xbr>

3Ha4eHne:<input type=text name="res" size=20><br> <input type=reset value=OTMeHHTb onclick="s=''" > </formX/tdX/trX/BODYX/HTML>

Рисунок 2. Построитель формул.

В листинге 4 приведен ИТМЬ-код документа с построителем формул.

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

Задания.

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

2. Пусть формула строится из логических констант, круглых скобок, логического отрицания, логического И, логического ИЛИ. Напишите сценарий, который вычисляет значение логической формулы. Для облегчения ввода формулы создайте «построитель формулы».

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

4. Напишите сценарий, вычисляющий значение формулы, содержащей целые константы, операции +, -, *, / и представленной в префиксной нотации (прямая польская запись). Напомним, что формула в префиксной нотации выглядит так ©АБ, где © - знак операции, А и Б - формулы в префиксной нотации. Формула а + Ь*с в префиксной нотации запишется так + а*Ьс, а формула (а + Ь)*с - (й + е)/к следующим образом: -*+ аЬс/+ йек. Скобки при таком представлении формулы не требуются, так как для каждой операции известны операнды. Формула состоит из переменных (представленных однобуквенными идентификаторами), знаков операций: плюс, минус, умножение и деление.

5. Напишите сценарий, который по произвольной формуле в инфиксной нотации строит формулу в постфиксной нотации. Напомним, что при записи формулы в постфиксной нотации (обратной польской записи) знак операции ставится непосредственно после операндов. Формула в постфиксной нотации имеет вид АБ©, где © -

знак операции, А и В - формулы в постфиксной нотации. Формула состоит из переменных (представленных однобуквенны-ми идентификаторами), знаков операций: плюс, минус, умножение и деление.

6. Напишите сценарий, который определяет полное сопротивление параллельно-последовательной (П.-П.) схемы. Формально, понятие П.-П. схемы можно представить следующим образом:

• Один резистор является П.-П. схемой. Сопротивление схемы равно сопротивлению единственного входящего в нее резистора.

• Последовательное соединение П.-П. схем является П.-П. схемой. Сопротивление схемы равно сумме сопротивлений последовательно соединенных схем.

• Параллельное соединение П.-П. схем является П.-П. схемой. Сопротивление схемы вычисляется по формуле

*=т-Ч-— +... + —

При решении задачи рекомендуется изображение электрической схемы записать по определенным правилам в виде строки символов (формулы). Если схема состоит из одного проводника, то задается число - величина сопротивления. Последовательное соединение схем можно задать строкой вида (А1 + А2 + ... +Ап), где А1 - либо один проводник, либо электрическая схема. Параллельное соединение можно задать строкой вида (А1*Л2*...*Ал), где А1 - либо один проводник, либо электрическая схема.

© Наши авторы, 2003. ОигаиЦюгз, 2003.

Дмитриева Марина Валерьевна, доцент кафедры информатики математико-механического факультета Санкт-Петербургского государственного университета.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.