Эффективное программирование 3D-приложений с помощью Irrlicht и Jython. Часть 4

Продолжим начатое в предыдущей статье серии рассмотрение возможностей python. В прошлый раз мы дошли только до условного оператора, позволяющего делать выбор из некоторого количества альтернатив развития алгоритма. На основании определенного условия мы совершаем одно действие, если условие выполнилось или истинно, и другое — если это не так.

Итак, давайте рассмотрим более сложный пример — это будет игра "угадай число". В ней машина загадывает некоторое число и предлагает пользователю угадать это число за некоторое количество попыток — всякий раз, когда пользователь ошибается, программа сообщает ему, больше или меньше введенное им число, чем то, которое загадала машина. Этот пример пройдет почти через весь оставшийся материал, отведенный для изучения собственно средств python, до перехода к irrlicht. Вам будет нужно дорабатывать и развивать задачу, добавляя новые функции, демонстрирующие разные приемы программирования и культуры разработки:

>>> import sys
>>> secret_num = 1
>>> while 1:
... print 'guess secret num — угадай, какое число я задумал'
... num = int ( sys.stdin.readline () )
... if (num == secret_num):
... print 'yes — да, ты угадал — это действительно число', num
... break
... elif num > secret_num:
... print 'less — число должно быть меньше'
... else:
... print 'bigg — число должно быть больше'
...
guess secret num — угадай, какое число я задумал
12
less — число должно быть меньше
guess secret num — угадай, какое число я задумал
23
less — число должно быть меньше
guess secret num — угадай, какое число я задумал
1
yes — да, ты угадал — это действительно число 1

Начинается все с подключения или импорта содержимого стандартного для python модуля sys. В этом модуле находится множество функций в том числе, что нужно для нас, отвечающих за задачи ввода данных с клавиатуры. Затем присвоим переменной secret_num значение 1 — его и нужно будет угадывать. Следующий шаг — организация цикла. Дело в том, что почти любая самая сложная программа определяется как некоторая комбинация или чередование элементарных шагов, которые бывают линейными — это когда есть несколько действий, выполняемых друг за другом, и именно в таком порядке, именно эти действия, и эти шаги неизменны, как налоги и смерть. Что более часто бывает — так это выбор из двух, трех, ста и более вариантов возможных алгоритмов расчета. В примерах выше мы рассчитывали количество дней в високосном году на основании делимости номера года на 4. У нас были две возможные ситуации: либо номер года поделился на 4, либо нет. Какая из этих двух ситуаций сработает, в общем случае неизвестно до тех пор, пока программа не получит от пользователя все необходимые для работы данные. Третий типовой прием — это цикл, многократное выполнение некоторого набора действий — его мы называем телом цикла. Если есть тело, то должна быть и голова — под головой принимают то, что определяет или принимает решение, сколько раз или до какого момента следует повторять те действия, которые и организуют тело цикла. В простейших случаях мы можем легко вычислить, сколько раз нужно выполнить тело цикла, в более сложных этот момент нам неизвестен. Например, в примере ниже я точно знаю, сколько раз нужно выполнить тело.

>>> i = 0
>>> while (i < 10) :
... print i
... i = 1 + i
...

Результатом работы будет список чисел от нуля до 9 включительно. В условии (голове цикла) проверяется то, что переменная i не превзошла собой 10 — если это так, делаем два действия, образующих тело цикла: распечатываем текущее значение переменной на экран, а увеличиваем ее на единицу. Один из самых страшных врагов настоящего программиста — бесконечный цикл — называется он так, потому что никогда не заканчивается. Так, в примере выше, если бы я забыл присвоить переменной I значение на единицу более или же ошибочно присвоил ей значение I = I — 1, то условие головы цикла никогда бы не выполнилось, и заполнился бы ваш экран бесконечным потоком ползущих цифр — прямо "матрица дома" какая-то получается. Хотя рано или поздно вы все равно сделаете сами свой первый бесконечный цикл, давайте посмотрим, что делать в подобной ситуации. Итак, сделайте вышеозначенный бесконечный цикл, убрав увеличение, или, как говорят настоящие программисты, инкремент, переменной I (соответственно, термин декремент означает уменьшение значения переменной на единицу). И запустите скрипт заново. Давным-давно, когда я писал на c/c++, то при возникновении бесконечного цикла не только процессор загружался на все 100%, но ощутимо притормаживала система: мышь перемещалась медленно и степенно — иногда было проще нажать reset, чем дождаться реакции О.С. Время меняется — сейчас таких эффектов нет. Когда вам надоест бесконечное мельтешение цифр на экране, просто нажмите ctrl-break или ctrl-c, что скажет машине python: "стоп, хватит выполнять текущую программу". В отдельных случаях бесконечные циклы нужны, т.е., конечно, они не нужны, но прием их использования бывает полезен. В примере с игрой "угадай число" условие головы цикла записано как просто 1 — цифра "один". Те из вас, кто знаком с с/с++ и им подобными языками, скажет: "ага, здесь тоже истина — не ноль, а ноль — ложь". Что в переводе на понятный язык означает: когда вы записываете какое-либо выражение в условии оператора if или же while, то машина python все сводит только к двум полярным состояниям: ноль и не ноль. Например, while 1 — это не ноль, while 2+3 — тоже не ноль, и даже while 1 < 2 — также не ноль, а вот while 0, while 1 > 2 или while '' или же while "" — это ноль — примите это пока как аксиому. Так вот, если вычисленное выражение в заголовке оператора — это не ноль, то тело оператора if или while выполняется, если же это не так, и выражение в голове вычислялось как ноль, то тело соответственно не выполняется. Всегда можно прервать выполнение оператора цикла с помощью команды break — дословно "стоп, хватит выполнять данный цикл", и не важно, чему равно условие в его заголовке. Для чтения данных используется функция num = int ( sys.stdin.readline () ) — здесь идет обращение к импортированному нами (в первой строке программы) модулю sys — далее через точку. Точку воспринимайте как аналог слова "внутри". Итак, внутри sys есть stdin, внутри которого есть функция (почему функция — потому, что, когда функция вызывается, то после ее имени всегда пишутся круглые скобки, и иногда даже что-то внутри этих скобок — то, что называется хитрым словом "аргумент"). Так, стоп: что есть функция и модуль? Т.е., конечно, все мы интуитивно понимаем, что такое функция, но нам нужно четкое определение, и обязательно с учетом специфики программирования и конкретно python. Под функций понимают некоторое количество кода, строк, операторов простых, условных или операторов цикла, которые так часто используются, что мы решили дать им имя и получить возможность вызова этого набора команд только по его имени. Делаем это мы не только из-за лени — настоящий программист, как известно, должен быть ленив, чтобы сделать один раз так, и в последующие разы ничего не нужно было делать. Например, функция может, скажем, выводить на экран фразу "Здравствуйте, дорогой, Василий Пупкин" и ожидать нажатия любой клавиши для продолжения. Но зачем нам нужна функция, которая умеет приветствовать только одного Василия Пупкина? Это не универсально и, следовательно, не позволяет достичь нирваны, ничего не делания. Функции могут получать в качестве входных данных (или аргументов) некоторые переменные — например, переменную fio, хранящую ФИО пользователя. Функции в математике — это нечто другое. Да, у них тоже есть какие-то аргументы, но они и возвращают некоторую величину, рассчитанную на основании этих аргументов. Функции в python (равно как и в любом другом языке программирования) также что-то возвращают. Вот вам еще пример функции.

>>> import math
>>> print math.sin(1);
0.841470984808
>>> d = math.sin(1);
>>> print d;
0.841470984808

Функции позволяют решить еще одну важную проблему в программировании — проблему избыточности. Представьте, что у нас есть Василий Пупкин, который по непонятным соображениям не приемлет функции, считая, что настоящему кулхацкеру не чуждо использовать набор клавиш ctrl-c и ctrl-v для размножения повторяющихся кусков кода. Но в один прекрасный момент выясняется, что в раскопированном в количестве 100 экземпляров фрагменте кода закралась хитрая ошибка, или баг, а значит, необходимо повторить процедуру ctrl-c и ctrl-v еще сто раз. Конечно, если вам работодатель платит деньги на почасовой основе, то можете этим заниматься снова и снова. Мне же платят именно за конечный продукт. Запомните: централизация — это ключ к успеху: собрав в одном месте все потенциально повторяющиеся фрагменты кода и оформив их как функции, вы экономите свое время и нервы. Собрав же все функции в одном месте, почему бы не дать этой коллекции отдельное имя и не обозначить термином модуль (да, я знаю, уважаемые многоопытные программисты на python, что здесь я немного лукавлю, но нельзя же все делать сразу). Возвращаясь к примеру, мы прочитали строку с помощью sys.stdin.readline (), но теперь необходимо преобразовать ее к типу данных число — целое, если быть точнее. Для этого и служит оператор int (то, что нужно преобразовать к числу).

>>> print 1 == "1"
False
>>> print 1 == int("1")
True

В python у каждой переменной есть свой тип данных, есть числа и строки. Так вот, сравнение двух переменных разного типа данных всегда ложно. Вот цифра 1 не равна строке "1", а во второй строке после преобразования мы можем использовать бывшую строку уже в математических операциях и сравнениях как полноправное число. Последний момент: мы организовали с помощью операторов if проверку трех возможных ситуаций: число было угадано — в этом случае необходимо сообщить пользователю об успехе и прекратить цикл, а раз он был организован как бесконечный, то необходим способ досрочного прерывания цикла с помощью оператора break. Остальные две ситуации, когда число было не угадано, тривиальны: необходимо сообщить пользователю о неудаче и подсказать, больше или меньше оно числа, задуманного машиной. Теперь начнем развивать данную задачу для самотренировки (я буду ставить перед вами цель, код для достижения которой вам нужно писать самим, сам же ограничусь схожими примерами и разъяснениями теоретических моментов):

Развивающее задание №1. Загаданное машиной число должно быть не явно задано в теле программы, а генерироваться случайно, в некотором диапазоне от A до B. Вам будет нужна функция random.randint (0, 100), которая служит для генерации случайного числа в отрезке от и до. Только предварительно нужно проимпортировать модуль random. Два числа A,B, задающие диапазон, в котором будет генерироваться число, должны вводиться с клавиатуры пользователем. Не забудьте выполнить проверку введенных чисел на корректность — очевидно, что A должно быть менее, чем B. Если это условие не выполняется, то дальнейшее продолжение программы невозможно. Сообщите об ошибке пользователю и прервите выполнение программы досрочно. Вот вам маленькая подсказка (также не забывайте, что, если вы хотите использовать русские буквы, то файл программы python должен быть в кодировке utf8):

import random
import os

print 'hello user' #печатаем приветствие
num = random.randint (0, 100) # генерируем случайное число
print num # печатаем случайное число
if num < 50:# если это число менее 50, завершаем работу программы
print 'number to small'
raw_input ("press any key to close application")
os._exit (0)# завершаем работу программы
print num, ' * ',num,'= ', num*num # если же число достаточно велико, вычисляем значение его квадрата
raw_input ("press any key to close application")

В этой программе мы впервые стали использовать комментарии — это текст, который игнорируется машиной пролога, но содержит информацию в виде примечаний, замечаний и пояснений, написанных нами на естественном языке и позволяющих спустя некоторое время (достаточно длительное, чтобы в памяти сгладились не только детали, как, но и что именно делает созданный скрипт). Комментарии начинаются с символа # и продолжаются до конца строки — вы вольны писать в них, что угодно, но общей рекомендацией является не дублировать код — например, так:

print 'hello' # распечатываем на экран слово hello
Очевидно, что такой комментарий не несет никакой дополнительной смысловой нагрузки.
print 'hello' # начинаем программу с приветствия пользователю (см. тех. задание раздел 2.1).

При изучении функций python важным является наличие средств оперативно получить справку по аргументам функции и ее краткое описание. Листать толстый учебник в поиске, например, описания функции randint слишком долго. В python есть особая функция, которая получает в качестве параметра имя другой функции и возвращает краткую справку об этой функции, например, так (не забудьте только предварительно проимпортировать модуль, в котором находится интересующая вас функция):
>>> import random
>>> help(random.randint)
Help on method randint in module random:

randint(self, a, b) method of random.Random instance
Return random integer in range [a, b], including both end points.

Если текст справки достаточно велик и не умещается на одном экране, попробуйте, например, получить справку не по одной функции, а по целому модулю — help(math). С помощью пробела вы можете пролистывать эти страницы, для выхода из справочной системы используйте клавишу "q".

Развивающее задание №2. За каждую неудачную попытку должны будут начисляться штрафные очки по следующему правилу: с первой по третью неудачные попытки — 0 штрафных баллов, для 4-й неудачной попытки — 1 балл, для 5-й попытки — 2 балла, для 6-й — 3 балла и т.д. В конце работы программы должно выводиться число штрафных баллов.

Развивающее задание №3. Наша игра должна длиться не один раунд, как сейчас, а произвольное их количество. Так, после того как пользователь угадает число, необходимо спросить его, желает ли он продолжить игру. Если он ответит "да" (например, введя слово yes в ответ на наш запрос) — игра продолжается. В противном случае необходимо завершить работу программы, указав, сколько всего раундов было сыграно, а также среднее число попыток, потраченное во всех раундах для того, чтобы угадать число.
Вот вам еще маленькая подсказка:

import sys

print 'вводите числа, пока вам это'не надоест
count = 0
sum = 0
while 1:
print 'Введи очередное число или слово "stop" для того, чтобы прервать программу'
line = sys.stdin.readline ()
line = line.strip () # это очень важный момент в программе — дело в том, что функция readline читает строку с клавиатуры включая символ ввода, который завершает строку — если не вызвать функцию strip, то в следующей строке сравнение будет всегда неудачно, ведь слово 'stop' не равно слову stop с завершающим символом ввода.
if line == 'stop':
break
else:
count = 1 + count # подсчет количества введенных чисел
sum = float(line) + sum # накапливаем сумму вводимых чисел — важно: здесь необходимо выполнить преобразование введенной строки к вещественному числу

print 'среднее арифметическое всех введенных чисел: ', sum / count
# очень приятный прием, позволяющий выводить длинные строки текста,
# мы можем разбить одну длинную строку на несколько, используя для разделения
# символ обратного слэша перед началом второй строки текста
print 'очень длинная строка, содержащая много-много\
символов, так что они не умещаются ...'
raw_input ("press any key to close application")

В следующей статье серии мы сделаем небольшую рокировку. Мы временно отложим в сторону изучение средств только python и попробуем настроить и запустить тестовый пример на jython, подготовившись к вызову 3D-функций irrlicht. В дальнейшем изучение собственно средств python не останавливается, а просто переходит в режим совмещения. Так, я рассказываю о, например, списках в python, и тут же делаем задачу в irrlicht, которая демонстрирует их возможности.

black zorro


Компьютерная газета. Статья была опубликована в номере 07 за 2007 год в рубрике программирование

©1997-2024 Компьютерная газета