24-й час

Другие средства Python

  В предыдущей главе Вы познакомились с наборами Мандельброта и Джулия, с математическими принципами их расчётов и программами вывода этих наборов на экран. В этой главе мы изучим некоторые оставшиеся средства и возможности Python. начнём мы с краткого введения программирования интерфейса CGI для Web, а затем рассмотрим некоторые теоретические вопросы о том, как правильно писать программы на Python.

  *Прим. В. Шипкова: глава достаточно условно даёт понять, что может Питон.

Интерфейс CGI

  Как уже говорилось в предыдущих главах, CGI — это интерфейс общего шлюза (Common Gateway Interface). Парадигмы программирования Web CGI несколько отличаются от принципов обычного интерактивного программирования. В случае программирования для Web приложения на языке Python должны размещаться в опредёленном каталоге, доступ к которому открыт для броузеров. Выводимые данные программа должна преобразовывать в формат HTML (Hypertext Markup Language — язык разметки гипертекста). В файлах формата HTML может содержаться не только текст с гиперссылками, но и заполняемые пользователем формы, данные из которых автоматически направляются одной или нескольким программам сервера, ответственным за их обработку. В этой книге не предполагалось подробно разъяснять, что такое Web-сервер, броузер, формат HTML и программирование CGI. Некоторые полезные ссылки на страницы, где эти вопросы раскрыты более глубоко, приведены в конце главы, в разделе "Примеры и задания".

  На моём компьютере запущен достаточно медленный Web-сервер на базе операционной системы Windows NT 4.0 с SP5 и Web-сервер Apache 1.3.9 для Win32. Более надёжным решением было бы использование Linux вместо NT с той же версией Apache Web-сервера для Linux. Но для меня работать с NT гораздо привычнее. Услуги Web-сервера в качестве бесплатного приложения предоставляют своим клиентам многие провайдеры Internet. Правда, не многие из них поддерживают выполнение программ на языке Python. Наиболее распространённым языком программирования для Internet является Perl, но Вы можете позвонить своему провайдеру и спросить его о возможности поддержки приложений на Python. Вполне возможно, что Ваш провайдер согласится установить на своём сервере ещё один язык программирования CGI. После установки Python на сервере Вам необходимо знать, где именно он находится. По крайней мере эта информация необходима Web-серверу Apache. На моём компьютере все сценарии на языке Python помешаются в папку c:\inetpub\cgi-bin — стандартное место размещения программ CGI. Приложение Web-сервера Apache установлено в папке C:\Apache, а интерпретатор Python находится в папке C:\Python. Все сценарии на языке Python, помещённые в папку cgi-bin для Web-сервера Apache, должны начинаться со строки #!С:\Python\Python.exe, иначе они не будут выполняться. В главе 15 мы рассматривали программу fixhash.py, которая автоматически контролирует правильность пути в первой строке всех файлов заданных каталогов. Таким образом, эта программа может оказаться теперь для Вас весьма полезной, иначе Вам вручную приходилось бы проверять все файлы программ CGI на правильность установки пути к интерпретатору.

  *Прим. В. Шипкова: на самом деле приведённый ранее пример не является самым эффективным. По крайней мере - в интернете можно найти решения и по эффективней.

  Если Вы не запускаете Web-сервер на своём компьютере, а убедили в этом своего провайдера, узнайте у него, в какой папке на сервере установлен Python. Кроме того, проследите, чтобы на компьютере провайдера все стандартные модули были установлены таким образом, чтобы Python имел к ним доступ. Наиболее важным для Вас сейчас будет модуль cgi.py. Если Ваш провайдер использует систему Windows NT, то установить Python не составит труда. Достаточно только запустить стандартную процедуру установки и указать папку инсталляции Python. С Linux выполнить установку сложнее, но если Ваш провайдер будет чётко следовать прилагаемой к Python инструкции по установке, то проблем не должно возникнуть. Если же проблемы возникнут, то о путях их решения можно узнать на домашней странице Python или специализированной телеконференции.

  В листинге 24.1 показан код традиционной программы "Hello, World!", измененной для запуска на Web-странице.

Листинг 24.1. Программа hellocgi.py

#!c:\Python\python.exe

def print_content():
  print "Content-type: text/html"
  print

def print_header():
  print "<html><title>Pythonic Hello World</title><body>"

def print_footer():
  print "</body></html>"

print_content()
print_header()
print "Hello, World!"
print_footer()

  Я проверил эту программу на Web-серверах Apache, запущенных в системах Linux и NT. В обоих случаях программа выполнялась успешно. Убедившись в работоспособности программы, я поместил её на Web-странице англоязычного издания этой книги по адресу http://www.pauahtun.org/cgi-bin/hellocgi.py. Введите этот адрес в поле адреса своего броузера, чтобы увидеть ту же картинку, что показана на рис. 24.1.

Рис. 24.1. Программа "Hello, World!" для запуска в Internet

  Строка адреса состоит из зарегистрированного адреса сервера в Internet (http://www.pauahtun.org/), имени каталога сценариев cgi-bin и имени файла сценария hellocgi.py. Программа hellocgi.py достаточно простая. Наиболее важной частью кода является функция print_content(). Независимо от того, насколько сложным будет Ваш сценарий CGI, он всегда должен начинаться с вызова этой функции. Эта функция выводит для Web-сервера строку Content-type: text/html, которая сообщает серверу о том, что содержимое файла предназначено для показа пользователям.

  Давайте создадим что-то более сложное, например форму для регистрирования программного продукта, которую пользователь сможет заполнять в окне своего броузера. После щелчка на кнопке Подача запроса программа, показанная в листинге 24.2, посылает электронное сообщение по адресу владельца формы.

Листинг 24.2. Программа register.py

#!c:\Python\python.exe
import os
import sys
import cgi
import SMTP

thisscript="http://www.pauahtun.org/cgi-bin/register.py"
mailhost="mail.callware.com"
mailfrom="ivanlan@callware.com"
mailto="ivanlan@callware.com"
ccto="ivanlan@callware.com"

def print_content():
  print "Content-type: text/html"
  print

def print_header():
  print """<html><title>Pythonic Registration Form</title>
    <body bgcolor=white text=black>"""

def print_footer():
  print "</body></html>"

def print_form():
  print """

    <h1 align=center>Please Register!</h1>
    <p>Please register software before downloading it. Thank

    you,
    %s
    <hr>
    <p>
    <form name=form action=%s method=post>
    Enter your name:
    <input type="edit" name="name"><br>
    Enter your email address:
    <input type="edit" name="email"><br>
    Enter the name of the software you are downloading:
    <input type="edit" name="software"><br>
    Enter the amount you are willing to pay in dollars for the

    software:
    <input type="edit" name="pay"><br>
    <hr>
    <input type="submit">
    <input type="reset">
    </form>
    """ % (os.environ["SERVER_NAME"],thisscript)

print_content()
print_header()

form = cgi.FieldStorage()
if form.has_key("name") and form.has_key("email"):
  nm = form["name"].value
  em = form["email"].value
  sw = form["software"].value
  mn = form["pay"].value
  print """

    <p align=center><font size=7>Thank you!
    </font><font size=3><br>
    <hr>
    """
  print """<p align=left>Thank you %s. Your email address, %s,

    will shortly receive an invoice for %s dollars, for

    downloading
    the %s package. If you do not pay up within 5 business

    days,
    all your files will be erased.<br>
    <hr>
    <p align=right>Have a nice day

    <img src="../Gif/smiley.gif">
    """ % (nm,em,mn,sw)
  msg="""

    Hello, %s (%s):
    You recently downloaded %s from %s.
    Please send %s dollars immediately to:

    Ransom
    PO Box 6969
    Washington DC 55512

    If you do not send money, we have a catapult.
    Thank you for your attention to this matter.

    Anonymous
    """ %(nm, em, sw, os.environ["SERVER_NAME"],mn)

  s=SMTP.SMTP(mailhost)
  s.send_message(mailfrom, em,
    "Software Registration", msg)
  s.send_message(mailfrom, ccto,
    "Software Registration", msg)
  s.close()
else:
  print_form()
  print_footer()

  Эта версия программы настроена на пересылку сообщений автору книги. Чтобы перенастроить её для себя, измените строки 7-11, подставив имя своего Web-сервера, каталога сценариев и свой адрес электронной почты. Затем введите правильный URL в поле адреса Вашего броузера, и Вы получите на экране форму, как на рис. 24.2.

Рис. 24.2. Форма, создаваемая программой register.ру

  Заполните форму, введя своё имя и адрес электронной почты, после чего щелкните на кнопке Подача запроса. Откроется новая страница, показанная на рис. 24.3.

Рис. 24.3. Данные формы отправлены успешно

  Но прежде чем выполнять эту программу на своём Web-сервере, нужно установить модуль SMTP.py, разработанный Владимиром Улоговым (Vladimir Ulogov). Этот модуль можно загрузить с сервера, расположенного по адресу http://starship.python.net/crew/gandalf /SMTP.py. Модуль SMTP.py является оболочкой стандартного модуля smtplib и существенно упрощает его использование.

  Если все установки адресов в программе выполнены правильно, то она будет успешно выполняться в Linux, Windows 95/98 и Windows NT. Скорее всего, она также будет работать и в Macintosh, хотя я не испытывал этого.

  Чтобы разобраться, что происходит в программе регистрации, посмотрите на строку 44 листинга 24.2. Ядро программы начинается с вывода строки типа содержимого для Web-сервера, после чего выводится строка заголовка. Эти данные остаются общими для обоих окон (см. рис. 24.2, и 24.3). В строке 48 реализуется объект класса FieldStorage из модуля cgi (этот модуль импортируется в строке 4). Программы CGI удобны для создания форм, но с их помощью сложно считывать данные, которые пользователь ввёл в поля формы на своём компьютере. Все сложности этого процесса берут на себя методы модуля cgi. В строке 49 проверяется, был ли заполнен данными объект form. Если метод has_key() возвращает false, значит, пользователь не ввёл данные в поля формы. В этом случае выполнение программы переходит к строке 86, где вызывается функция print_form(). Эта функция вновь выведет на экран пустую форму, которую Вы видели на рис. 24.2. (Определение функции print_form() дано в строках 24-44.) Если же строка 49 возвращает true, то начиная со строки 50 происходит обработка данных, введённых пользователем. В результате обработки данных в строках 65-77 формируется сообщение, а для пользователя открывается Web-страница, (см. рис. 24.3), в которой пользователю сообщается, что его информация успешно принята. После завершения формирования сообщения для передачи в строках 79—84 создаётся объект SMTP, который связывается с вашим почтовым сервером и посылает копию сообщения по назначению (строки 80, 82). Соединение, открывшееся для передачи электронного сообщения, закрывается в строке 84.

  Ниже, на примере листинга 24.2, представлена в общем виде последовательность задач, которые Вам нужно выполнить при создании программы CGI на языке Python.

  Вооружившись этими знаниями и средствами Python, Вы сможете реализовать любую задачу по программированию интерфейса CGI.

Отладка программ

  На мониторе моего компьютера расположилась парочка пластмассовых насекомых. Когда меня спрашивают, зачем тебе эти жучки, я говорю, что это мои тотемы. "А что такое тотемы?" — спрашивают меня. Тогда я объясняю, что тотемы — это головы представителей клана животных. Например, для некоторых племен индейцев Северной Америки тотемными животными были бизоны. Тотемные животные дают позволение охотникам убивать отдельных представителей клана, без которых племя не смогло бы выжить. В замен тотемные животные требуют уважения к себе, почитания и выполнения целого ряда ритуалов, исполняемых вокруг несъедаемых частей животного, например голов. Эти ритуальные церемонии исполнялись с целью выпросить у животного прошения за то, что у него была отнята жизнь. Если охотники не следовали традициям, они могли обидеть тотемное животное, в результате чего клан мог отказаться посылать этому племени своих членов на пропитание. (Если Вы думаете, что поклонение тотемным животным ушло в прошлое с племенами североамериканских индейцев, посмотрите вокруг себя. Задумайтесь на рекламой, показываемой по телевизору, о всех этих счастливых коровках, пляшущих свинках, поющих цыплятах и прочих животных, которые из кожи вон лезут, чтобы предложить Вам: "Скушай меня!".)

  Отладка программ — это работа, которая часто занимает большую часть времени работы программиста над проектом. В действительности, если бы в программах не водились "жучки", число программистов уменьшилось бы, начались бы сокращения, безработица и прочий кошмар. Вот почему у всех на виду я держу своих тотемных жучков. Я не хотел бы, чтобы клан жучков перестал посылать мне своих представителей, которых бы я отлавливал в джунглях программного кода, получая за это зарплату. Вам может показаться это глупым, кто знает? Я не обижусь. Во всяком случае, это напоминает мне, что в мире все взаимосвязано, а в программных кодах особенно.

  *Прим. В. Шипкова: надо будет завести себе "жучка". ;)

  С учётом вышесказанного, простота отладки программ в Python выглядит пугающе. Какая бы проблема не возникла с программой, интерпретатор Python тут же предельно точно сообщит Вам, в какой именно строке кода произошел сбой. Кроме того, в интерпретатор встроено множество классов исключений, показывающих довольно подробные и исчерпывающие окна сообщений. Конечно, чтобы разбираться в них, Вам потребуется некоторая практика. Но допустив пару раз одну и ту же ошибку, сообщение о ней будет восприниматься Вами уже как родное.

  Но, кроме того, в Python существуют специальные средства, позволяющие сделать процесс отладки программ ещё проще и нагляднее. Самым простым средством является инструкция print, с помощью которой из любой точки программы Вы можете выводить на экран текущие значения переменных. Для Вас существует опасность скорее недооценить, чем переоценить это средство, особенно в Python. Пожалуй, это самый эффективный по простоте скорости выполнения метод выявления сбойных строк в программе. В других языках программирования, таких как С, где каждую новую версию программы нужно компилировать, использование функции printf() не столь эффективно. В Python, как только Вы ввели новую строку с инструкцией, сразу же можно выполнить код программы. Ну а добавление и удаление по ходу программы инструкций print не займет у Вас много времени.

  Другое мощное средство — возможность выполнения программы в пошаговом режиме. Для этого программу нужно запустить на выполнение не обычным путём, а в окне специального приложения отладки. При работе с Python в режиме командной строки программа отладки вызывается командой pdb. Документацию по программе отладки можно загрузить с Web-страницы по адресу http://www.python..org/doc/current/lib/module-pdb.html. В этой документации всё достаточно подробно описано. По личному опыту работы с pdb я знаю, что наиболее эффективно в пошаговом режиме выполнять в программе функции и методы, чтобы убедиться, что они возвращают именно то, что требуется. Иногда вместо программы отладки также успешно можно использовать функцию assert(), которая запускает исключение, если принимает не то значение, которое предполагал программист. Но в других случаях программа отладки незаменима, например, когда нужно выполнить программу до вызова опредёленной функции, а потом продолжить выполнение в пошаговом режиме, контролируя значения всех переменных функции.

  И всё же я считаю, что внимательное изучение кода самим программистом — это наиболее эффективный метод отладки программ в Python. Исследуя код, постарайтесь отвлечься от человеческой логики и представить себя на месте компьютера, выполняя шаг за шагом все строки программы. Чётко представьте себе, какое именно значение должна возвратить каждая функция и что ей для этого нужно передать. И если Вы обнаружите часть кода, вызывающую у Вас сомнение, используйте инструкцию print, чтобы визуализировать ваши сомнения. Не используйте одновременно слишком много инструкций print, иначе Вам сложно потом будет разобраться на экране, какая инструкция что выводит. Лучше всего ограничиться одной-двумя инструкциями. Если ситуация не прояснится, тогда самое время воспользоваться программой отладки, чтобы в пошаговом режиме пройти наиболее сложные участки программы. К сожалению, мы не можем позволить в себе в этой книге детально остановиться на изучении работы с программой отладки. Вам следует сделать это самостоятельно, воспользовавшись справочной системой, прилагаемой к Python.

Искусство программирования

  Сейчас мы отвлечемся от изучения конкретных средств программирования и обсудим абстрактные вопросы об искусстве и красоте программирования. Слишком часто программисты при написании своих программ не задумываются о выборе элегантных решений и красоте кода. Они думают таким образом: "Кому какое дело до моего кода, главное, чтобы программа работала правильно". Другие поддерживают их: "Мы понятия не имеем, что такое красиво и что такое не красиво". Ну а кто сможет определить, что красиво и что есть искусство вообще, как научиться творить искусство? Единственный способ познать красоту — делать её. Сначала работа, потом анализ. Если Вы внимательно читали эту книгу, изучали средства программирования, выполняли упражнения, то Вам уже больше ничто не препятствует начать создавать программы прямо сейчас. В философии Дзен-буддизма об этом говорят так: "Когда Вы едите клубнику — ешьте её", т.е. наслаждайтесь вашей клубникой, вместо того чтобы ломать голову над тем, в какую тарелку её положить и какой вилкой её подцепить.

  Точно так же, приступая к программированию, отвлекитесь от средств и теперь впервые задумайтесь над программой.

  Интересные наблюдения были проведены мною на ежегодных конференциях майянистов (специалистов по культурному наследию индейцев Майя), проходящих в Остине (Austin), штат Техас. В течение шести дней конференции проходят практикумы для начинающих. Практикумы эти состоят в том, что новичкам раздают таблички с иероглифами Майя и предлагают перевести их. Интересно было проследить, как менялось отношение учеников к этой задаче в течение недели. Сначала ты чувствуешь себя потерянным, но видишь, что также растеряны и все окружающие тебя. К середине недели накапливается ненависть к себе и к работе. Хочется разбить вдребезги эти таблички, сесть на самолет и улететь домой. Но к концу недели наступает момент истины, когда Вы ещё не знаете, как выполнить работу, но Вас озаряет понимание того, что Вы делаете. Это переломный момент, поскольку осознание задачи позволяет найти правильные решения. Мне кажется, это очень важный момент подготовки специалиста: сместить центр внимания с обучения на самостоятельную практику, от переживаний и неуверенности — к упорному труду.

  Популярность и продуктивность ежегодных рабочих встреч майянистов на протяжении вот уже 25-ти лет во многом была обусловлена убеждением основателя этих конференций Линды Шел (Linda Schele) в том, что любой начинающий может внести свой весомый вклад в развитие науки наряду с признанными профессионалами. Многие иероглифы никогда не были бы расшифрованы, если бы не свежий взгляд новичка. С самой первой конференции Линда придерживалась принципа, что участником может стать каждый желающий. Не требовалось предъявлять справки и рекомендации, подтверждающие твою учёность в этой области. Единственным требованием был живой интерес к проблеме. Увлечённость, граничащая с наваждением, приветствовалась больше всего.

  Именно такая увлечённость необходима всем тем, кто хочет овладеть программированием.

  Ещё один абстрактный пример. Представьте себе, что Вы разговаривайте с кем-то. Думать так: "Скорей бы он уже замолчал, чтобы я смог рассказать о себе" будет не только неприлично, но и не умно. Лучше подумайте следующим образом: "Пока я слушаю его, вместо того чтобы говорить о себе, я узнаю что-то интересное, удивительное. Мои знания расширяются". Подумайте о программировании как о Вашем диалоге с компьютером. Не задумывались ли Вы, что компьютер сможет Вас чему-то научить, если Вы прекратите хоть на минутку свой монолог.

  Шанрай Судзуки (Shunryu Suzuki), один из признанных духовных лидеров Дзен-буддизма, писал в своей книге Zen Mind, Beginner's Mind ("Мышление Дзен, мышление новичка") следующее: "Дзен — это не вид откровения, а способ сосредоточения на ежедневных рутинных процессах. Наше понимание буддизма базируется не на интеллектуальных выкладках, а на ежедневной практике". Чуть выше я говорил Вам, что переломным этапом в обучении начинающих майянистов является "момент истины", когда студентам начинает казаться, что они понимают, над чем работают. В действительности до понимания ещё далеко, это лишь момент пробуждения сознания. Понимание наступит значительно позже, когда Вы пройдете через сотни, если не тысячи подобных моментов истин. В действительности нет абсолютного понимания истины, поэтому и нет конца открытий, которые каждый человек делает для себя.

  Много лет тому назад я пытался писать научную фантастику. Меня поражало, где "настоящие" писатели берут свои идеи. Я мучительно искал идеи, которые мог бы реализовать в своём произведении. Я записывал отдельные мысли на карточки (тогда ещё не было персональных компьютеров) и тщательно сортировал их в картотеке, но ничего хорошего из этого не получилось. Я был слишком занят поиском идей, записью их и сортировкой, поэтому на написание произведения не хватало времени. И слава Богу, поскольку все мои идеи были старыми избитыми клише и история, которая могла бы быть написана на основе этих идей, не стоила того, чтобы её писали.

  У "настоящих" научных фантастов часто спрашивают: "Где Вы берёте свои идеи?" Это, пожалуй, один из наиболее часто задаваемых вопросов. Один из авторов обычно отвечал: "Я их ворую". И этот ответ глубже, чем Вам может показаться. Это действительно то, что делаем мы все. На Земле нет ничего нового. Никакая идея не может родиться в вакууме, как элементарная частица. Поэтому никакая идея не может быть абсолютно оригинальной. Все идеи вырастают под влиянием воздействующей на нас информации из окружающего мира, т.е. под влиянием других идей. "Новая" идея — это всегда гибрид существующих концепций. Заботясь о том, что происходит вокруг Вас, фокусируя своё внимание на ежедневных рутинных процессах, Вы рано или поздно ощутите (это слово даже лучше подойдёт сюда, чем осознаете), что вокруг Вас полно задач, требующих Вашего решения. Правильнее сказать — нескончаемый поток задач. Некоторые из этих задач происходят от ваших потребностей. Их можно решить написанием небольших утилит, таких как наша программа fixhash.py. Другие проблемы исходят от компании, в которой Вы работаете. Для их решения Python можно использовать как совершенный язык написания прототипов программ, программ-связок и даже отдельных проектов. Но практически всегда при написании программы можно найти код другой программы, который с минимальными изменениями можно приспособить для решения ваших задач. После чего Ваш код послужит базой другому программисту для написания другой программы. Таким образом, мы все "воруем" свои коды у других, и в этом нет ничего зазорного. Напротив, это верная практика программирования, направленная на решение ежедневных задач, а не на удовлетворение собственного тщеславия.

  Таким образом, обучаясь программированию, Вы проходите следующие этапы: практика без понимания; практика с пониманием целей и убежденностью в том, что если Вы постараетесь, то сможете выполнить поставленную задачу; наконец, понимание того, что ваша практика программирования — это искусство, которое и есть высшая цель. Где же сейчас на этом пути находитесь Вы? Вы достаточно напрактиковались в программировании на Python и должны быть уверены в том, что любая задача Вам по плечу. А дальше — действуйте, ешьте Вашу клубнику, не задумываясь о тарелке; катайтесь на велосипеде, не задумываясь о том, как он сохраняет равновесие на двух колесах; пишите программы, не задумываясь о средствах программирования, а просто пишите. Python — это Ваш язык, поскольку мышление Python — это мышление начинающего. Эта мудрая истина, записанная в оригинале, показана на рис. 24.4.

Рис. 24.4. Хухуа Лин (Xuhua Lin), мастер коми-графического написания китайских иероглифов, сделал эту надпись, "Мышление Python — мышление начинающего", специально по моей просьбе для данной книги

Резюме

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

— Ричард Бейкер (Richard Baker), предисловие к книге Zen Mind, Beginner's Mind.

Практикум

Вопросы и ответы

  Ну и как, удалось ли Вам продать издателям хотя бы один фантастический рассказ?

  Нет. Но я насобирал огромную коллекцию уведомлений об отказе, которую храню до сих пор. Одно из них самое примечательное, где на кусочке бумаги размером 2x2 дюйма просто карандашом написано "Нет".

  По вашим рассуждениям можно сделать вывод, что для программиста важнее не сама программа, а процесс её написания и полученная при этом практика. Так ли это?

Так считает Будда.

Примеры и задания

  Чтобы узнать больше о формате HTML, обратитесь к книге HTML: The Definitive Guide, 3rd Edition, by Chuck Musciano and Bill Kennedy, from O'Reilly. Также полезной будет книга Dynamic HTML: The Definitive Reference, by Danny Goodman, from O'Reilly.

  Обширный обзор всех средств отладки программ представлен в книге Code Complete: A Practical Handbook of Software Construction by Steve C. McConnell. Microsoft Press; ISBN: 1556154844.

  Если Вы ещё не насытились моей философской "клубникой", посетите Web-страницу по адресу http://www.pauahtun.org/vietnam/abacus.html.

Назад  Оглавление  Дальше