Роботландский Университет © А.А.Дуванов |
В программировании часто бывает нужно одну и ту же группу команд повторять несколько раз. Если повторение происходит на месте используют цикл (while, for). Если код нужно повторять в разных местах программы его оформляют в виде функции.
В самом деле, если уж нужно вычислить длину (количество цифр) целого положительного числа, то логичнее проделать это один раз, а затем спокойно писать как в математике F(num), не задумываясь над анатомией функции F и не повторяя ее кода заново. Пусть, например, пользователь последовательно вводит три числа, а программа вычисляет общее количество цифр:
<SCRIPT language=JavaScript> <!-- // Количество цифр в целом положительном числе num. // Вход: num (целое положительное число). // Выход: количество цифр в num. function F(num) { var len = 0; if (num < 10) len = 1; else while(num) { num = (num - num%10)/10; len++; } return len; } var sum = 0; var num1, num2, num3; num1 = prompt("Введите первое число", ""); if (num1) sum += F(num1); num2 = prompt("Введите второе число", ""); if (num2) sum += F(num2); num3 = prompt("Введите третье число", ""); if (num3) sum += F(num3); alert("Общее число введенных цифр: " + sum); //--> </SCRIPT> |
Браузер выполняет этот скрипт так. Запись:
function F(num) { var len = 0; if (num < 10) len = 1; else while(num) { num = (num - num%10)/10; len++; } return len; } |
принимается во внимание, но команды в ней не выполняются. Браузер делает пометку: было описание функции с именем F.
Так происходит потому, что ключевое слово function и
следующий за ним блок
Команды, следующие за описанием функции, выполняются как обычно. При этом каждый раз, когда в коде попадается обращение к F, браузер возвращается к описанию функции и выполняет ее команды, подставляя вместо формального аргумента num фактические переменные num1, num2, num3.
На самом деле, буквальной подстановки не происходит. Браузер перед вычислением функции F выполняет команду присваивания:
num=num1; при обращении F(num1);
num=num2; при обращении F(num2);
num=num3; при обращении F(num3).
Посмотреть код этой программы без использования функции F можно здесь.
В общем случае описание функции на языке JavaScript выглядит так:
function ИмяФункции (список формальных аргументов через запятые) { ... тело функции ... return значение; } |
Команда return, возвращающая значение функции, может быть не одна, а может и вовсе отсутствовать. В последнем случае функция не возвращает никакого значения, и ее вызов нельзя использовать в выражениях. В частности, вызов функции, не возвращающей значения, не может быть использован в команде присваивания.
Команда вызова функции имеет вид:
ИмяФункции(список фактических аргументов через запятые)
Фактическим аргументом функции может быть константа, переменная, выражение и, в частности, вызов другой функции.
Имена в программировании играют значительную роль. Они позволяют сделать код более понятным и, наоборот, способны запутать читателя, если выбраны как попало.
Имя F нельзя считать хорошим для описанной выше функции. В примере это имя использовано для смягчения перехода от математики к программированию. Ведь в математике функции обозначаются, как правило, одной буквой. Для программирования однобуквенное имя редкое исключение. По одной букве трудно догадаться о назначении функции. В нашем случае имя LenOfNumber (длина числа) вместо F было бы гораздо лучше.
Длина имен функций и переменных в JavaScript не ограничивается, но имя должно быть одним словом (без пробелов). Имя строится из латинских букв, арабских цифр, знаков подчеркивания и некоторых других (не рекомендуемых) специальных знаков. Русские буквы использовать нельзя. Первым символом имени должна быть латинская буква или знак подчеркивания. Регистр букв имеет значение. Например, имена LenOfNumber и LenOfnumber считаются разными.
Замечание. Приведенный выше скрипт не проверяет правильность данных, которые вводит пользователь. В реальных программах проверка данных перед обработкой обязательна. Об оборонительном стиле программирования рассказано ниже в этом уроке.
Ранее говорилось, что функция используется тогда, когда нужно повторять один и тот же код в разных местах программы. Понятно, что функция удобна и тогда, когда предполагается использовать ее в разных программах.
В программировании популярны наборы библиотечных функций. Ввод и вывод данных, математические вычисления, создание интерфейсных окон, обработка мышиных щелчков и перемещений все это требуется практически в любой прикладной программе. Ясно, что есть смысл хорошо запрограммировать эти функции один раз. Так и делают. Некоторые наиболее популярные функции становятся даже элементами систем программирования (как функции alert и prompt они закодированы в самом браузере).
Однако самая важная область применения функций технология построения программ, при которой сначала проектируется иерархическая схема решения поставленной задачи, а каждому иерархическому узлу сопоставляется отдельная функция. Программирования начинается с корня иерархии и постепенно продвигается к ее листьям.
Такой способ проектирования называется нисходящей технологией или методом сверху вниз. Он следует за особенностью человеческого мышления: мы не можем продумать проект сразу во всех тонкостях и деталях.
Написать программу, которая формирует HTML-код клетчатого поля, в котором чередуются темные и светлые клетки:
Построим изображение поля при помощи HTML-таблицы. Код таблицы будем формировать как значение строковой переменной.
Сначала опишем функцию, которая должна решать поставленную задачу целиком.
// Функция field(row, column, bcolor, wcolor) // возвращает HTML-код таблицы, изображающей // клетчатое поле. // Аргументы: // row - число строк // column - число столбцов // bcolor - цвет темной клетки // wcolor - цвет светлой клетки function field(row, column, bcolor, wcolor) { // Формируем код начала таблицы. var str = "<TABLE border=1 cellspacing=0 cellpadding=10>"; // Добавляем коды табличных строк. str += lines(row, column, bcolor, wcolor); // Добавляем код конца таблицы. str += "</TABLE>"; return str; }
Подумаем о том, как должен быть устроен формирователь табличных строк.
// Формирователь табличных строк. // Функция lines(row, column, bcolor, wcolor) // возвращает HTML-код всех табличных строк. // Аргументы: // row - число строк // column - число столбцов // bcolor - цвет темной клетки // wcolor - цвет светлой клетки function lines(row, column, bcolor, wcolor) { var str = ""; // Накопитель кода. var numpair = Math.floor(row/2); // Число пар строк. // Строим коды строк таблицы парами. for(var i = 0; i < numpair; i++) { // Формируем код нечетной табличной строки. str += lineOdd(column, bcolor, wcolor); // Формируем код четной табличной строки. str += lineEven(column, bcolor, wcolor); } // Строим дополнительно "нечетную" строку, // если число строк нечетно. if(row%2) str += lineOdd(column, bcolor, wcolor); return str; }
Теперь можно заняться проектированием функций lineOdd и lineEven, которые строят соответственно нечетную и четную табличные строки. Функция lineOdd должна начинать строку с темной клетки, а функция lineEven со светлой.
// Функция lineOdd(column, bcolor, wcolor) // возвращает HTML-код нечетной строки таблицы. // Аргументы: // column - число ячеек в строке // bcolor - цвет темной клетки // wcolor - цвет светлой клетки function lineOdd(column, bcolor, wcolor) { // Формируем код начала табличной строки. var str = "<TR>"; var numpair = Math.floor(column/2); // Число пар клеток. // Формируем коды клеток парами. for(var i = 0; i < numpair; i++) { // Строим код нечетной табличной клетки. str += cell(bcolor); // Строим код четной табличной клетки. str += cell(wcolor); } // Строим дополнительную клетку, если число клеток нечетно. if(column%2) str +=cell(bcolor); // Формируем код конца табличной строки. str += "</TR>"; return str; } // Функция lineEven(column, bcolor, wcolor) // возвращает HTML-код четной строки таблицы. // Аргументы: // column - число ячеек в строке // bcolor - цвет темной клетки // wcolor - цвет светлой клетки function lineEven(column, bcolor, wcolor) { // Формируем код начала табличной строки. var str = "<TR>"; var numpair = Math.floor(column/2); // Число пар клеток. // Формируем коды клеток таблицы парами. for(var i = 0; i < numpair; i++) { // Строим код нечетной табличной клетки. str += cell(wcolor); // Строим код четной табличной клетки. str += cell(bcolor); } // Строим дополнительную клетку, если число клеток нечетно. if(column%2) str +=cell(wcolor); // Формируем код конца табличной строки. str += "</TR>"; return str; }
Остается только описать функцию cell, которая строит HTML-код отдельной табличной клетки.
// Функция cell(color) // возвращает HTML-код табличной клетки. // Аргументы: // color - цвет клетки function cell(color) { return "<TD bgcolor="+color+"> </TD>"; }
Замечание. Функции lineOdd и lineEven, формирующие код цепочки табличных клеток, легко заменяются одной универсальной функцией line. Читателю предлагается получить удовольствие от такого улучшения кода в рамках первой задачи блока заданий урока. Задачи 2, 3 и 4 также посвящены разным модификациям этого упражнения.
Построенная программа описывается иерархической схемой, которая соответствует этапам нисходящей разработки:
Решение построено, и его можно использовать. Например, вызов
var kodField = field(3, 2, "black", "white");
запишет в переменную kodField HTML-код такой таблицы:
Построенный код будет записан в одну строку и без лишних пробелов, но браузер, конечно, на это не обидится!
Остается интересным вопрос: а как показать изображение, соответствующее этому коду на гипертекстовой странице?
Как будет показано в уроке 7, это можно сделать при помощи функции document.write (метод объекта document).
Если описанный выше скрипт записан в файле field.js, то изображение двух таблиц на экране можно получить при помощи такого HTML-документа:
\n'+
' Клетчатые поля\n'+ 'Первое поле (3x6)\n'+ ' |
Любая система, рассчитанная на реальную эксплуатацию должна иметь защиту от дурака. Этим обидным словом программисты обозначают потребителей своей продукции. На самом деле, употребляется это слово в шутку. Пользователь имеет право на любую ошибку, и про грамма обязана сохранять работоспособность и правильно реагировать на любые действия потребителя, даже на такие, которые не предполагались постановкой решаемой задачи.
Петр Мячиков с удовольствием запрограммировал Угадайку (смотрите урок 4) и принес показать работу Ивану Сидорову.
А заботится ли твоя программа о правильности данных, которые ввел пользователь? спросил его Сидоров.
Загрузили программу в браузер и стали проверять.
Первое, что может сделать пользователь, сказал Сидоров, это нажать Enter, не введя никакого числа. Как реагирует программа?
Угадайка говорит, что теперь число находится в интервале [,50]. Странное заявление! Так кто здесь больше не прав? Пользователь или программист?
Запуск Угадайки (досрочный выход из игры Esc)
Можно ввести оборонительную проверку на ввод пустой строки:
if(answer == "") alert ("Ошибка при вводе числа");
Понятно, что пользователь может также:
Все это надо проверить и не пропустить неверные данные в алгоритм, который их обрабатывает.
Напишем специальную функцию для проверки правильности числа, полученного от пользователя:
// Проверка num на целое число из [a,b]. // Возвращает: true - num хорошее // false - num плохое function IsNum(num, a, b) { var ret = true; // возвращаемое значение // проверка на пустую строку if (num == "") ret = false; else // проверка на цифры for(var i=0; i<num.length; i++) if(num.charAt(i) < "0" || num.charAt(i) > "9") {ret = false; break;} // проверка на интервал ret = ret && (a <= parseInt(num,10) && parseInt(num,10) <= b); return ret; } |
Запуск исправленной Угадайки (досрочный выход из игры Esc)
Программа исправленной Угадайки
Замечание. Стандартная функцияparseInt(num,10) преобразует явным образом значение переменной num в десятичное число. Второй аргумент этой функции указывает основание системы счисления: 2, 8, 10 или 16. Так, вызовparseInt(num,16) будет преобразовывать значение переменной num в 16-ричное число. Если функция parseInt сталкивается с неверным символом, она возвращает числовое значение, основанное на части строки до этого символа. Например, вызовparseInt("48 попугаев",10) вернет значение 48. Если неверный символ в строке самый первый функция возвращает NaN специальную константу (от Not a Number).
Плохо разбирается, очень плохо! Не полагайтесь на автоматическое преобразование типов. Используйте функцию parseInt или аналогичную ей функцию parseFloat (преобразование в десятичную дробь).
Петр написал первую Угадайку без использования явных преобразований типа. Сначала он дал этой программе на пробу число 49. Угадайка сообщила, что теперь задуманное число находится в интервале [1,49]. Петр ввел число 222. К его удивлению Угадайка не выдала сообщения об ошибке, а вместо этого сказала, что, задуманное число находится в интервале [1,222]!
Ниже приводится программа Угадайки без использования явных преобразований типа. Попробуйте ввести первым числом 49, а затем 222. Что у Вас получится?
Запуск Угадайки без parseInt (досрочный выход из игры Esc)
Программа Угадайки без parseInt
Почему так произошло? Почему число 222 было пропущено, и интервал из [1,49] превратился в [1,222]?
Дело в том, что проверка
Такое странное поведение браузера объясняется очень просто: он сравнивал не числа, а строки. Строка "49" действительно больше строки "222" в смысле лексикографического порядка.
Проверяя строки, браузер сравнивает их посимвольно. Сначала сравнивает первые символы, потом вторые и так далее. Если при очередном сравнении символ из первой строки располагается в кодовой таблице дальше символа из второй проверка заканчивается, и браузер считает первую строку больше второй.
Так, у него и получилось, что
Ниже предлагается небольшой испытатель. Проделайте опыты по сравнению числа с числом, строки со строкой и числа со строкой.
В каждом задании Зачетных классов нужно записать сообщение, которое появится в окошке alert после выполнения скрипта.
Сначала нажмите кнопку Сброс, затем приступайте к решению.
Сначала нажмите кнопку Сброс, затем приступайте к решению.
// Функция field(row, column, bcolor, wcolor, size) // возвращает HTML-код клетчатого поля. // Аргументы: // row - число строк // column - число столбцов // bcolor - цвет темной клетки // wcolor - цвет светлой клетки // size - размер клетки в пикселах function field(row, column, bcolor, wcolor, size) { ... } |
Функция должна строить код клетчатого поля по следующей схеме:
Границы поля задаются блоком
Затем программа отгадывает задуманное число. На малышей это производит большое впечатление.
![]() |