| Роботландский Университет © А.А.Дуванов |
В программировании часто бывает нужно одну и ту же группу команд повторять несколько раз. Если повторение происходит на месте используют цикл (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)
{
...
}
|
Функция должна строить код клетчатого поля по следующей схеме:
Границы поля задаются блоком
Затем программа отгадывает задуманное число. На малышей это производит большое впечатление.
|
|