Предыдущая часть: Проектирование
Как бы ни хотелось сразу начать писать код, сначала нужно пояснить моменты, без понимания которых двигаться дальше невозможно.
В первой части я написал, что роботы должны расставляться на поле случайным образом. Если размер поля это 22*22 клетки с краями, куда нельзя вставать, то координата X и Y каждого робота должна получить случайное значение от 1 до 20 (считая с нуля, конечно). Как именно планируется получить это значение?
В программировании широко используются методы вычисления псевдослучайных чисел. Псевдо, потому что они лишь имитируют случайность, но для большиства случаев это годится. Устроены они так: есть некая математическая формула, которая при каждом вычислении выдает новое, как бы случайное, число.
Можно написать такую формулу, но дело в том, что языки программирования имеют готовые средства для получения случайных чисел и многого другого.
Это функции. Они получили такое название из математики, так как самое первое, что требовалось – это, например, вычисление синусов и косинусов, которые являются математическими функциями f(x).
В программировании функции приобрели гораздо более широкое значение. Они не только вычисляют синусы и косинусы. По сути это просто самостоятельные куски кода, которые могут делать что угодно – рисовать на экране, записывать файлы, открывать интернет-соединения и т.д.
Каждый раз, когда нам требуется требуется что-то сделать в программе, мы или пишем это сами, или ищем – есть ли в языке такая функция? Если она там есть, то будет гораздо быстрее и проще использовать её.
Давайте посмотрим две строчки на Питоне:
a = 5
a = randint(1, 20)
Первая строчка должна быть понятна от и до. Как это происходит в памяти и программе, уже неоднократно проходили.
Во второй строчке написано нечто странное. Что такое randint? Почему там скобки? Что такое "1, 20"?
Это мы обратились к функции, которая есть в языке. Обращение делается по имени. В данном случае её имя randint, сокращение от "random integer", то есть "случайное целое число". Скобки служат для передачи функции параметров от нас. Сейчас мы передаем ей два числа: 1 и 20. Мы информируем функцию, что она должна вычислить случайное целое число от 1 до 20.
Опять уточню, что функция – это всего лишь машинные инструкции, которые находятся где-то в памяти. У них даже нет имени. Имя существует только в языке, для нас. Транслятор языка в курсе этого, поэтому при трансляции программы в машинный код делает следующее:
"Я вижу, что переменной a нужно присвоить значение randint(). Это выглядит как функция. Я поищу, есть ли она в языке... Нашел! Вот её адрес в памяти. Программист хочет передать туда числа 1 и 20. Я возьму эти числа, запишу их в служебный буфер памяти, и отправлю процессор на адрес этой функции."
То есть как только в тексте встретилось randint(1, 20), код нашей программы перестал выполняться – процессор перешел на другой адрес с условным именем randint – и начал выполняться код, расположенный по этому адресу.
Как выглядит исполнение этого кода:
"В служебном буфере памяти мне должны передать два числа. Я нашел там число 1 и число 20. Я генерирую случайное число, чтобы оно было не меньше 1 и не больше 20. На этот раз у меня получилось 13. Теперь я тоже положу это число в служебный буфер памяти, чтобы его мог увидеть тот, кто меня вызвал. И верну управление назад."
После возврата из функции программа продолжает работать с того места, где прервалась:
"В служебном буфере памяти должен лежать результат работы функции. Это число 13. Я беру это число и записываю в переменную a."
Таким образом, через все эти манипуляции переменная a получит значение 13. При следующем вызове randint() получится другое число, и т.д.
На бытовом уровне функцию можно представить как окошко в стене, типа кассы. Над окошком написано название функции. Когда вам нужна какая-то функция, вы выбираете окошко с нужным названием и подходите к нему. Дальше требуется передать в это окошко какие-то бумажки. Квитанцию и деньги, например. В обмен из этого окошка вам отдают справку, например. Таким образом, вы вызвали функцию, передали в нее данные и получили результат. Что там внутри этого окошка и как оно работает – вы не знаете. Вы знаете только то, что если передать туда нужные им бумажки, то в ответ вернется нужная вам бумажка. И пока не получен результат, вы стоите и не можете даже моргнуть – управление передано в окошко.
Если посмотреть на такое окошко изнутри, то там сидит служащий, который ничего не знает о внешнем мире. У него есть только окошко. Когда в это окошко ему передают бумажки, он обрабатывает их и отдает назад другую бумажку. При этом порядок передачи бумажек строго определен. Допустим, сначала нужно передать квитанцию, а потом деньги. На квитанцию служащий поставит печать, а деньги положит в кассу. Если вы передадите сначала деньги, а потом квитанцию, то он поставит печать на деньги, а квитанцию положит в кассу. Функция отработает неправильно, вы получите неправильный результат, а возможно даже обрушите целую организацию. Поэтому правила передачи данных в каждую функцию определены заранее (нужно читать документацию по языку) и должны строго соблюдаться.
Теперь посмотрим код на языке JavaScript:
var a = Math.random();
Уже легко понять, что здесь переменной a присваивается случайное число. Мы вызвали функцию Math.random(). Но есть отличия. Во-первых, почему имя пишется через точку и что такое Math?
Объяснение этого момента в полном объеме будет слишком длинным, но обязательно будет. Пока можно просто считать, что функции объединяются в библиотеки для удобства. Например, математическя библиотека содержит все математические функции, графическая библиотека – все графические, и т.д. И поэтому мы обращаемся к функции через имя библиотеки. Опять же, по аналогии с окошками в стене, вы сначала выбираете отдельный зал для приема граждан, а затем в этом зале выбираете уже окошко. То есть Math.random означает "функция random из библиотеки Math".
Замечу, что такая запись возможна и в Питоне:
a = random.randint(1, 20)
Теперь это значит "функция randint из библиотеки random". Почему я так не написал в первый раз? Это зависит от контекста, про который я расскажу уже непосредственно в комментариях к коду Питона. Ну и отмечу, что имя библиотеки random в Python не имеет никакого отношения к имени функции random в JavaScript. Это просто совпадение.
Ладно, но теперь есть кое-что ещё. Если в функцию randint требовалось передать два числа, то в функцию Math.random не требуется передавать вообще ничего – скобки пустые. Как же так и что делать?
Функция Math.random() всегда возвращает число от 0 до 1. Например, 0.001, 0.5, 0.99. Как я узнал? Прочитав документацию к этой функции. И вы всегда читайте. Выходит, что это число нужно дополнительно преобразовать, чтобы получить нужный результат. Так как мне нужно получить от 1 до 20, формула будет выглядеть так:
a = 1 + Math.random() * 19;
Проверка: если Math.random() вернет 0, получится 1 + 0 * 19 = 1. Если вернет 1, получится 1 + 1 * 19 = 20. Все результаты между 0 и 1 уложатся в числа между 1 и 20, что и требуется.
Но есть ещё нюанс. Мне нужны только целые числа. А Math.random() умножить на целое число даст скорее всего дробный результат, типа 13.58. Поэтому нужно ещё и преобразовать этот результат в целое число. На помощь спешит другая функция, Math.floor()! Она получает любое число и возвращает это же число, только без дробной части.
Итак, полный код, который мне нужен на JavaScript:
a = Math.floor(1 + Math.random() * 19);
Заметьте, что в скобках к функции Math.floor() я написал не одно число, а целое математическое выражение, которое ещё и содержит другую функцию. В этом нет ничего страшного. В функцию попадает не само выражение, а его результат. Транслятор языка сначала вычислит выражение 1 + Math.random() * 19, а результат вычисления подаст в Math.floor() уже как обычное число.
Я воспользуюсь этим функциями в следующих частях, чтобы расставить роботов на поле в случайном порядке. Также будут использоваться и другие функции, но теперь вы знаете, что это такое.
Читайте дальше: Инициализация