Это продолжение работы над курсом основ программирования и робототехники, дополнение к первой главе.
В этом материале вводятся понятия случайных величин (математика) и массивов (программирование), показана работа с датчиком случайных чисел, освещается работа с массивами с помощью цикла.
Этот материал не привязан к роботу Maqueen, описываемые программы можно выполнить даже без платы микро:бит, пользуясь эмулятором (программой, заменяющей физическое устройство) в среде. программирования makecode.
Игральная кость
Возьмём игральную кость - кубик, с нанесёнными на грани точками. На каждой грани - своё число точек, от 1 до 6. Бросим кубик на поверхность: какое число точек окажется на верхней грани, когда кубик остановится?
Мы не знаем: кубик останавливается случайно.
Если мы будем много бросать кубик, то в среднем все грани будут выпадать с примерно одинаковой частотой, хотя, не исключено, мы сможем увидеть например, три "шестёрки" подряд или ни одной "четвёрки" за 10 бросков.
Случайные события нельзя предсказать, но их можно имитировать на компьютере. Для имитации случайных событий служит
Датчик случайных чисел
Датчик случайных чисел при каждом обращении к нему возвращает случайное число из заданного диапазона.
Если мы возьмём диапазон от 1 до 6, то случайные числа из этого диапазона могут заменить нам случайные события: результаты бросания кубика. Датчик вернул 3 - выпала "тройка", 5 - "пятёрка" - очень просто.
Действием, которое нам заменит бросок кубика, будет встряхивание платы.
Осталось научиться пользоваться датчиком случайных чисел.
- Из папки Ввод перенести блок "по жесту (встряхивание)"
- Из папки Основное перенести блок "показать число" в блок "по жесту (встряхивание)"
- Из папки Математика перенести блок "выбрать случайно от (0) до (10)" внутрь блока "показать число".
- 0 и 10 заменить на 1 и 6, соответственно
- получим следующую программу
Программу можно проверить на эмуляторе в левой части окна системы makecode: чтобы запустить выполнение программы на эмуляторе, нужно нажать треугольник слева под изображением платы микро:бит (подобно тому, как запускается видеоролик). Для имитации встряхивания платы нужно нажать на кнопку SHAKE на изображении платы.
Пользуясь датчиком случайных чисел, можно бросить жребий, чтобы выбрать кого-то группы, например, дежурным или водящим. Чтобы выбрать кого-то случайным образом из, скажем, 5 человек, нужно использовать датчик для диапазона от 1 до 5 (естественно, всем участвующим в жеребьёвке нужно присвоить номера до того, как датчик будет использован).
С помощью датчика случайных чисел можно сыграть в игру "Камень, ножницы, бумага".
Вопрос: какой диапазон датчика случайных чисел нужно использовать для игры "Камень, ножницы, бумага"?
Очевидно, нужно взять диапазон случайных чисел от 1 до 3. Поменяв в нашей программе диапазон с "от 1 до 6" на "от 1 до 3" мы сможем играть в "Камень, ножницы, бумага".
Правда, это будет не очень удобно: нужно договориться, какое число обозначает камень, какое - ножницы и какое - бумагу, и всё время "переводить" числа в предметы.
Задание: Разработать стилизованные изображения камня, ножниц и бумаги для дисплея микро:бит, чтобы показывать их с помощью блока "показать светодиоды" (папка Основное), пронумеровать изображения и показывать изображение с номером, полученным с датчика случайных чисел при встряхивании платы.
Получится, наверное, что-то вроде
Интересно, а почему первая программа состояла, по сути, из одного блока ("показать число"), а последняя - содержит несколько блоков "если"?
Принципиально вывод цифр и вывод стилизованных изображений ничем не отличается: это изображения, состоящие из определённых точек на дисплее. "Вывести число" для однозначного числа означает вывести изображение соответствующей цифры, для каждой цифры в недрах среды программирования есть описание картинки, число просто служит "номером" этой картинки, подобно тому как в последней программе пронумерованы изображения камня, ножниц и бумаги.
Можем ли мы
а) создать изображения отдельно от блока "показать светодиоды"?
б) вывести изображения по их номеру?
Эти две задачи нужно решить, чтобы иметь возможность сделать основной блок программы ("при жесте (встряхивание)") таком же компактным, как и в программа для игральной кости.
Ответ на оба вопроса - "да".
Здесь мы должны познакомиться с понятием
Массив
Массив - это хранилище пронумерованных объектов. Встречаются массивы чисел, массивы строк, массивы изображений. Что бы в массиве ни хранилось, массив готов отдать хранимое по команде "дай объект с номером таким-то": то, что нам инужно!
Сначала создадим массив изображений.
В колонке папок с блоками откроем Рассширенные: появятся папки Функции, Массивы, Строки, Игра, Изображения и так далее.
Из папки Массивы перенесём в блок программы "при начале" блок "задать для (список) значение (массив ("a", "b", "c") - +).
Последний внутренний блок ("массив") и есть заготовка нашего массива, куда мы хотим поместить изображения: для первых трёх элементов массива уже обозначены места.
Теперь из папки Изображения возьмём блок "создать изображение" и поместим его в массив на место "а".
Нанесём мышью нужное изображение и продолжим процесс для двух двух следующих элементов массива.
Получим что-то вроде
Теперь нужно научиться обращаться к элементу массива по его номеру:
Перенесём из папки Изображения блок "show image (myImage) at offset (0)" - это блок вывода изображения на дисплей.
myImage - это изображение, которое выводится: сюда надо поместить элемент массива "список", созданного ранее.
Для извлечения элемента массива перенесём из папки Массивы блок "(список) получить значение по индексу (0)" и поместим его на место меню myImage.
Индекс - то же самое, что номер элемента в массиве.
Внимание, тонкость! Нумерация элементов в массиве начинается не с единицы, а с нуля: нулевой элемент, первый и т.д. Это древняя традиция, сохранившаяся во многих языках программирования: так удобнее преобразовывать программу, написанную человеком, во внутреннее представление компьютера.
Это не всегда удобно для человека, но это надо учесть и запомнить.
Итак, наши картинки имеют номера 0, 1 и 2. Датчик случайных чмсел нужно будет настроить так, чтобы он выдавал числа в диапазоне 0-2, а не 1-3.
Теперь - последний шаг: в блоке получения элемента массива вместо индекса (номера) 0 нужно поместить случайное число из диапазона 0-2.
Ну, это мы уже умеем:
Итоги
мы научились пользоваться датчиком случайных чисел, а также научились создавать массивы.
Массив - это пронумерованное множество объектов. Нумерация объектов в массиве начинается с 0.
Стрелка часов
Помните "Пропеллер"? Мы имитировали на дисплее микро:бит вращение пропеллера, выводя в бесконечном цикле четыре линии для разных положений пропеллера. Видоизменим задачу: попробуем изобразить вращение стрелки часов на циферблате.
Вопрос: Сколько положений стрелки нам нужно будет показать?
В два раза больше, чем для пропеллера: каждую из четырёх линий для изображения пропеллера придётся "распилить" на две, поскольку у пропеллера есть одно вертикальное положение, а у стрелки часов - два:
на 12 часов и на 6 часов, и так с каждым изображением пропеллера.
Итого, нам нужно 8 изображений стрелок.
Задание: создать массив изображений стрелок.
С созданием массива из трёх картинок мы имели дело: заготовка массива содержит как раз 3 места. А если нужно больше?
Для увеличения числа элементов массива нужно нажать в блоке "массив" плюсик справа. (минус служит для уменьшения числа элементов, если мы случайно создали лишние, а также если нам в массиве вообще нужно меньше 3-х элементов).
Заметим, что в процессе заполнения массив изменит ориентацию с горизонтальной на вертикальную. и в итоги получим следующую картину:
Теперь надо последовательно выводить изображения в цикле.
Мы можем воспользоваться блоком "постоянно", чтобы часы работали всегда, пока микро:бит подключен к питанию.
Внутри блока "постоянно" мы можем поместить 8 блоков вывода изображения. Но это - не самый удобный и красивый способ выполнения большого числа операций.
8, конечно, не так уж и много, но программистам приходится иметь дело и с десятками, и с сотнями, и с даже миллионами единообразных операций, и расписать их по отдельности нет никакой возможности.
Вопрос: какие блоки служат для многократного выполнения операций?
Это блоки-циклы, блоки "для". Для работы с элементами массива по номерам есть специальный цикл в папке Циклы: "для (index) от (0) до (4) \ делать"
Перенесем это цикл в блок "постоянно".
index это номер элемента массива, который можно будет использовать внутри блока для обращения к очередному элементу массива.
Вопрос: на что нужно изменить 4 в блоке, чтобы индекс "пробегал" все номера элементов в массиве?
4 нужно поменять на 7, поскольку элементы в нашем массиве нумеруются от 0 до 7.
Теперь внутри цикла будем выводить элемент массива с номером index, по известным образцам.
Получаем следующую программу:
Итоги
Единообразную информацию (оценки, имена, изображения) для работы целесообразно объединять в массивы. Данные в массивах имеют "общую фамилию" - название массива, и свои номера. Это позволяет упорядочить информацию, с которой работает программа, облегчает написание правильных программ.
Для обработки всех элементов массива служит специальный цикл. Именно использование циклов и массивов позволяет программистам достаточно лаконично писать программы, обрабатывающие огромные объёмы информации. В сочетании со способностью компьютера очень быстро выполнять огромное число операций это открывает безграничные возможности в мире информации.
Небольшое дополнение: об ошибках
... Интересно, а что будет, если мы по ошибке напишем цикл не до 7, а до 8? (Это типичная, и, на первых порах, простительная ошибка, поскольку мы привыкли считать с единицы, а не с нуля).
Давайте посмотрим...
Стрелка пробежала круг и ... выполнение программы оборвалось, на дисплее появился грустный смайлик.
В этом есть своя логика: а что делать программе, когда идёт обращение к объекту с номером 8, тогда как в массиве последний элемент имеет номер 7?
Программа должна зафиксировать ошибку и прекратить выполнение, поскольку после ошибки продолжать работу бессмысленно: мы уже не можем полагаться на результаты работы программы.
Хуже было бы, если программа попыталась после ошибки продолжить работу: в этом случае неправильные результаты работы программы могут ввести нас в заблуждение, подтолкнуть к неправильным выводам и действиям.
Ошибки программирования время от времени встречаются: логические ошибки и опечатки трудно совершенно исключить из процесса программирования. Системы программирования совершенствуются, чтобы по возможности выявить попавшие в программу ошибки: как раз такой пример выявления ошибки системой программирования во время выполнения программы мы и видели. Ещё лучше, когда ошибка выявлена ещё до выполнения.
Используемая нами среда программирования makecode специально разработана так, чтобы исключить возможность совершения многих ошибок начинающих, а работа в ней позволяет не отвлекаться на многие технические проблемы.
И всё-таки, самым важным в предотвращении ошибок программирования являются усилия программиста: внимание, аккуратность и использование приёмов, которые уменьшают вероятность ошибки.
Например, мы можем изменить последнюю программу, чтобы в качестве максимального индекса в цикле указать не число, а длину массива без единицы:
- Из папки Математика перенесём блок "(0) - (0)" и поместим его на место максимального индекса
- Из папки Массивы перенесём блок "длина массива (список)" на место первого нуля
- Второй ноль заменим на 1:
Такое задание максимального значения индекса хорошо тем, что его правильность можно оценить, не пересчитывая число элементов в массиве. Такой фрагмент останется верным даже если мы, например, захотим изменить программу и оставить только вертикальные и горизонтальные стрелки.
Задание. Сократить массив до 4 элементов и проверить работу программы.
Хорошие профессиональные программисты стремятся к созданию логичных, понятных и надёжных программ.
Использовано изображение игральной кости из Википедии