Зачем нужна эта книга

Текст этой страницы с небольшими изменениями лёг в основу одного из предисловий, напечатанных в первом томе книги (см. стр. 12–28). На сайте я сохраняю оригинальный текст этой страницы, как говорится, для истории.

Проблемы с обучением программированию

Ситуация, сложившаяся сегодня с подготовкой новых программистов, при первом взгляде вызывает ощущение изрядного абсурда. С одной стороны, именно программист — это одна из самых востребованных, высокооплачиваемых и при этом дефицитных специальностей: кадровый голод в этой сфере не исчезает даже во время самых суровых кризисов. Зарплаты квалифицированных программистов ныне сравнимы с зарплатами топ-менеджмента средних, а иногда и крупных компаний, и даже на такую зарплату кандидата приходится подолгу искать. С другой стороны, фактически программированию нигде не учат. Большинство преподавателей высших учебных заведений, ведущих «программистские» дисциплины, сами никогда не были программистами и имеют об этом виде деятельности весьма приблизительное представление (оно и понятно, большинство тех, кто может программировать за деньги, в современных условиях именно программированием и зарабатывают). В единичных «топовых» ВУЗах среди преподавателей всё же встречаются бывшие, а иногда и действующие программисты, но ситуацию в целом не спасает даже это. Люди, одновременно умеющие и программировать, и учить, встречаются довольно редко, но и если рассмотреть только этих людей, среди них окажется не так много тех, кто способен адекватно представить себе общую методическую картину становления нового программиста; судя по результатам, наблюдаемым на выходе, даже если такие люди есть, воплотить своё видение программистского образования в конкретный набор читаемых дисциплин им не удаётся, слишком велико сопротивление среды.

С обучением в ВУЗах есть и другая проблема. Абитуриенты поступают на «программистские» специальности, имея в большинстве случаев весьма приблизительное представление о том, чем им там предстоит заниматься. Программирование — это отнюдь не такой вид деятельности, которому можно научить кого угодно; здесь необходимы весьма специфические способности и склонности. По большому счёту, все программисты — изрядные извращенцы, поскольку ухитряются получать удовольствие от работы, от которой любой нормальный человек бежал бы без оглядки. Но распознать будущего программиста на вступительных экзаменах в ВУЗ или даже на собеседовании (какого нигде никто не проводит) совершенно нереально, особенно в условиях, когда в школе программирование либо вообще не изучается, либо изучается так, что лучше бы оно не изучалось. Выйдет из человека толк или нет, становится видно ближе ко второму курсу, но ведь в имеющихся условиях (в отличие, заметим, от большинства западных университетов) сменить выбранную специальность если и возможно в теории, то на практике почти нереально. В итоге даже среди студентов ВМК МГУ, где имеет честь преподавать автор этих строк, будущих программистов наблюдается в лучшем случае треть, а будущих хороших программистов — процентов десять.

Больше того, есть основания предполагать, что создание программиста в рамках ВУЗа вообще принципиально невозможно: ремесло не передаётся в стенах учебных заведений, ремесло передаётся только в мастерской — от действующего мастера к ученику, причём непосредственно в процессе работы, и касается это отнюдь не только программирования.

Вывод из вышесказанного, возможно, неутешителен, но несомненен: стать программистом человек может только, и исключительно, в результате самообучения. Заметим, это подтверждается и личным опытом автора этих строк, и опытом других программистов: на вопрос, сам человек учился программированию или его научили в ВУЗе, второго ответа пока что не дал ни один из знакомых автору профессионалов.

Самообучение — это тоже не так просто

Автору этих строк довелось стать программистом примерно четверть века назад — именно тогда, когда эта профессия вдруг перестала быть уделом узкого круга никому не известных людей и превратилась в массовое явление. Но в те времена мир был устроен несколько иначе. Господствующей платформой (если вообще возможно так выразиться в применении к реалиям того времени) была система MS-DOS и её многочисленные клоны, а типичный внешний вид экрана компьютера образовывали синие панельки Norton Commander. Написать программу для MS-DOS было несложно, средств для этого было — хоть отбавляй, так что на первую половину девяностых пришелся уникальный расцвет любительского программирования. Многие из любителей того времени стали потом профессионалами.

Современные условия качественно отличаются от эпохи начала девяностых. Все господствующие ныне платформы делают акцент на графический интерфейс пользователя; создание программы с GUI требует понимания принципов событийно-ориентированного построения приложений, умения мыслить в терминах объектов и сообщений, то есть, попросту говоря, чтобы сделать программу, снабженную графическим интерфейсом пользователя, необходимо УЖЕ быть программистом, так что варианты «попробовал — понравилось» или «попробовал — получилось» отсекаются сугубо технически. Более того, начать освоение программирования с рисования окошек в большинстве случаев означает необратимо травмировать собственное мышление; такая травма полностью исключает в будущем достижение высокой квалификации.

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

Выход есть

Всякий раз, когда положение начинает казаться безвыходным, есть смысл поискать выход там, где его ещё не искали. В данном конкретном случае выход из положения немедленно обнаруживается, стоит только сделать шаг в сторону от современного компьютерного мейнстрима. Операционные системы семейства Unix на протяжении всей истории сети Интернет прочно и незыблемо удерживали за собой сектор серверных систем; начиная с середины девяностых, Unix'ы проникли на компьютеры конечных пользователей, а сегодня их доля на настольных компьютерах и ноутбуках такова, что игнорировать её больше не получается. Особенно интересной становится эта ситуация, если учесть, что MacOS X, используемая на «роскошных» мак-буках, представляет собой ничто иное как Unix: в основе MacOS X лежит система Darwin, которая относится к семейству BSD.

Несмотря на наличие в юниксоподобных системах графических интерфейсов, по своей развесистости часто превосходящих их аналоги в системах мейнстрима, основным инструментом профессионального пользователя этих систем всегда была и остаётся командная строка — просто потому, что для человека, умеющего с ней обращаться, правильно организованная командная строка оказывается многократно удобнее «менюшечно-иконочных» интерфейсов. Возможности графического интерфейса ограничены фантазией его разработчика, тогда как возможности командной строки (разумеется, при грамотной её организации) ограничены только характеристиками компьютера; работа в командной строке проходит быстрее (иногда в десятки раз); наконец, руки, освобождённые от необходимости постоянно хвататься за мышку, устают существенно меньше, перестаёт болеть правый плечевой сустав и всё в таком духе. В системах семейства Unix командная строка организована столь грамотно, что её господствующему положению в качестве основного интерфейса ничто не угрожает. В контексте нашей проблемы здесь важен тот факт, что написать программу, предназначенную для работы в командной строке, многократно проще, чем программу с GUI; наличие в Unix-системах командной строки в качестве основного инструмента работы делает возможным то самое любительское программирование, которое казалось безвозвратно утраченным с тех пор, когда в мейнстриме системы линейки Windows сменили «старый добрый» MS-DOS.

Есть и другие причины, по которым именно системы семейства Unix оказываются предпочтительны в качестве учебного пособия для начинающих программистов; эти причины я ранее подробно рассматривал в статье «Почему Unix».

Увы, большинство учителей и преподавателей с упорством, достойным лучшего применения, продолжает использовать в учебном процессе компьютеры под управлением ОС Windows; нельзя не отметить, что на самом деле при этом учеников и студентов учат программировать под MS-DOS — тот самый MS-DOS, о котором сейчас, спустя почти двадцать лет после его окончательной смерти, даже вспоминать как-то неловко. В таких условиях любые аргументы, высказываемые в пользу сохранения Windows в качестве системы на учебных компьютерах, заведомо оказываются лишь отговорками, а реальная причина здесь одна: иррациональный страх перед освоением всего нового. Windows не годится на роль учебного пособия; продолжать использовать эту систему при наличии заведомо лучших (причём лучших по всем параметрам; Windows не имеет абсолютно никаких достоинств в сравнении с nix-системами) альтернатив — это, мягко говоря, странно. Для человека, взявшегося учить кого-то программированию, не должно быть проблемой освоение непривычной операционной среды.

Язык определяет мышление

Кроме операционной среды, используемой для обучения, важнейшую роль играет также выбор языков программирования. Времена бейсика с пронумерованными строками, к счастью, прошли; к сожалению, часто (особенно в спецшколах) встречается противоположная крайность. Несостоявшиеся программисты, решившие попробовать себя в роли школьных учителей, пытаются обучать ни в чём не повинных школьников «профессиональным» языкам, таким как Java, C# и даже C++. Конечно, пятиклассник, которому пытаются впихнуть в мозги C++ (реальный случай в реально существующем учебном заведении), в результате не поймёт абсолютно ничего, разве что запомнит «волшебные слова» cin и cout (отметим, что как раз это к реальному программированию на C++ отношение имеет весьма сомнительное), но таким «гениальным учителям» возможности аудитории совершенно не указ, тем более что способы контроля, естественно, выбираются такие, при которых ученики без особых проблем «проскакивают», так ничего и не поняв из выданного им материала. Автору этих строк встречались школьники, не понимающие, что такое цикл, но при этом получающие у себя в школе пятёрки по информатике, где им «преподают C++».

Учителям такой категории вообще, судя по всему, всё до лампочки: в C++ используется библиотека STL, а значит, надо рассказать ученикам STL; разумеется, дальше vector'а и list'а обучение никогда не заходит (при этом эти два контейнера, пожалуй, самые бесполезные из всего STL), но самое интересное, что ученики, разумеется, так и не понимают, о чём идёт речь. В самом деле, как можно объяснить разницу между vector и list человеку, который никогда в жизни не видел ни динамических массивов, ни списков и вообще не понимает, что такое указатель? Для такого ученика list отличается от vector тем, что в нём нет удобной операции индексирования (почему её там нет? ну, нам что-то объясняли, но я ничего не понял), так что вообще-то всегда надо использовать vector, ведь он гораздо удобнее. Что? Добавление в начало и в середину? Так оно и для вектора есть, какие проблемы. Ну да, нам говорили, что это «неэффективно», но ведь работает же! Переучить такого ученика практически нереально: попытки заставить его создать односвязный список вручную обречены на провал, ведь есть же list, а тут столько ненужного геморроя! Собственно говоря, всё: если нашему обучаемому дали в руки STL раньше, чем он освоил динамические структуры данных, то знать он их уже не будет никогда; путь в серьёзное программирование ему, таким образом, закрыт.

Не менее часто встречается и другой вариант: учить пытаются, судя по всему, чистому Си (тому, который без плюсов), то есть не рассказывают ни классы, ни контейнеры, ни STL (что, в общем, правильно), ни ссылки, но при этом невесть откуда выскакивают пресловутые cin/cout, тип bool (которого в чистом Си отродясь не было), строчные комментарии и прочие примочки из C++. Объяснение тут довольно простое: стараниями Microsoft с их VisualStudio в сознании виндовых программистов, особенно начинающих, разница между чистым Си и C++ временами совсем теряет очертания. В мире Unix с этим всё гораздо лучше, во всяком случае эти два совершенно разных языка никто не путает; но, как уже говорилось, Unix в нашей школе днём с огнём не найдёшь, учителя предпочитают платить государственные деньги за коммерческий софт, бороться с вирусами путём еженедельной переустановки всех компьютеров (опять же реальная ситуация в реальной школе), уродовать мозги учеников, лишь бы только не изучать ничего за пределами мейнстрима.

Впрочем, даже чистый Си в качестве первого языка программирования — это откровенный нонсенс. Причины этого я уже несколько лет назад подробно расписывал в эссе «Язык Си и начальное обучение программированию», повторяться здесь не буду, отмечу только главное: к изучению Си нужно подходить, уже понимая указатели и умея с ними обращаться; более того, желательно иметь уже некоторый опыт написания программ, чтобы отличать хорошее от плохого, ведь сам язык Си развивает что угодно, кроме культуры программистского мышления.

Довольно удачной представляется (во всяком случае, на первый взгляд) последовательность, традиционная для ВМК МГУ: в первом семестре студенты осваивают Паскаль, причём к концу семестра доходят до динамических структур данных, вплоть до хеш-таблиц; второй семестр посвящается архитектуре ЭВМ, в том числе изучению языка ассемблера и программирования на нём; в третьем семестре в рамках курса «Операционные системы» изучается чистый Си; наконец, в четвёртом семестре делается некая попытка донести до студентов объектно-ориентированное программирование и язык С++.

К сожалению, мелочи и частности могут напрочь испортить даже самую лучшую идею. Так, на лекциях первого семестра студентам зачем-то вдалбливают так называемый «стандартный Паскаль», который представляет собой монстра, годного разве что для устрашения, и в природе не встречающегося; при этом на семинарах тех же студентов заставляют программировать на ТурбоПаскале под всё тот же MS-DOS — на мёртвой системе в мёртвой среде, вдобавок не имеющей никакого отношения к «стандартному» Паскалю, который рассказывается на лекциях. Больше того, лекции построены так, будто целью является не научиться программировать, а изучить в подробностях пресловутый (и несуществующий) Паскаль: тратится море времени на формальное описание его синтаксиса, многократно подчёркивается, в какой конкретно последовательности должны идти секции описаний (любая реально существующая версия Паскаля позволяет расставить описания в произвольной последовательности, и уж точно нет ничего хорошего в том, чтобы, следуя соглашениям стандартного Паскаля, описывать в самом начале программы метки, которые будут использоваться только в головной программе, то есть в самом конце текста программы, но видны при этом будут зачем-то во всех процедурах и функциях).

Во втором семестре язык ассемблера, что уже, увы, ожидаемо, демонстрируется тоже под MS-DOS, то есть изучается система команд 16-битных процессоров Intel (8086 и 20286). Программирование под заведомо мёртвую систему расхолаживает студентов, культивирует презрительное отношение к предмету (и это отношение часто переносится на преподавателей). Впрочем, в действительности нет особой разницы, какой конкретно ассемблер изучать, главное — поймать логику работы с регистрами и областями памяти; но как в этом курсе расставляются акценты — со стороны понять очень трудно, во всяком случае, на выходе большинство студентов не понимает, что такое прерывание, и практически никто не знает, как выглядит стековый фрейм.

Всё более-менее приходит в норму лишь на втором курсе, где используется Unix (FreeBSD) и чистый Си изучается именно в этой, идеально подходящей для Си среде. Однако это не отменяет того факта, что перед этим целых два семестра используются, мягко говоря, с сомнительной эффективностью.

Несколько лет назад на ВМК на одном из трёх потоков начался эксперимент по внедрению новой программы; надо отдать должное авторам эксперимента, гнилой труп MS-DOS они из учебного процесса устранили. Впрочем, на этом достоинства новой программы заканчиваются: вместе с откровенной мертвечиной экспериментаторы выкинули и язык Паскаль, начиная обучение прямо с Си. Это можно было бы делать, если бы все поступающие на первый курс абитуриенты уже имели некий, хотя бы зачаточный опыт программирования: для человека, уже видевшего указатели, Си не представляет особых сложностей. Увы, даже ЕГЭ по информатике не может обеспечить наличие хотя бы и самых примитивных навыков программирования у поступающих: сдать его на положительный балл вполне можно, совершенно не умея программировать. Большинство первокурсников приходят на факультет с абсолютно нулевым уровнем понимания, что такое программирование и как это всё выглядит; Си становится для них первым в жизни языком программирования, а на выходе мы получаем откровенную катастрофу.

Если бы мир был идеален

С моей точки зрения, идеальная последовательность этапов в обучении будущего программиста могла бы выглядеть так. Прежде всего ученика следует убедить в том, что с компьютером следует по возможности общаться через команды, а не через иконки и кнопочки. Это можно было бы сделать, например, предоставив удалённый терминальный доступ к серверной Unix-машине с возможностью сделать там свой веб-сайт, работать с почтой и т.п. Делать это следует как можно раньше, во время учёбы в школе, причём желательно не тянуть с этим до выпускных классов.

Научившись пользоваться компьютером так, как это должно быть, а не так, как нас заставляет коммерческий мейнстрим, можно начать обучение программированию — с Паскаля (например, использовать FreePascal), при этом ни в коем случае не использовать никакие «интегрированные среды», скрывающие от пользователя как раз всё то, что следует знать и понимать. Тексты программ следует набирать в каком-то из множества доступных в Unix'е редакторов, для начала это может быть nano или joe, позже, возможно, vim; компилятор следует запускать вручную, дав соответствующую команду. Что касается самого Паскаля, то изучать следует не «язык Паскаль», а программирование. Нет совершенно никакого смысла в настойчивом вдалбливании ученику формального синтаксиса, таблиц приоритетов операций и прочей подобной ерунды: на выходе нам нужно получить не знание языка Паскаль (который, возможно, ученику никогда больше не понадобится в жизни), а умение писать программы. Наиболее высокими барьерами на пути ученика здесь оказываются, во-первых, пресловутые указатели (и автору этих строк не известно никакого другого языка, который был бы столь же удачен как учебное пособие на эту тему), и, во-вторых, рекурсия, с которой тоже можно научиться работать на примере Паскаля. Отметим, что модуль CRT (нежно любимый нашими педагогами настолько, что uses crt; встречается в программах, не использующих никакие возможности из него, причём даже в учебниках) во FreePascal'е под Linux и FreeBSD замечательно работает, позволяя создавать полноэкранные терминальные программы; на Си это сделать гораздо труднее, автор этих строк в своё время, будучи уже опытным программистом, потратил несколько дней на то, чтобы более-менее разобраться с библиотекой ncurses.

Прежде чем переходить к Си, стоит спуститься уровнем ниже и попробовать освоить язык ассемблера. Заодно можно осознать, что такое ядро операционной системы, зачем оно нужно и как с ним взаимодействовать; системный вызов перестаёт быть чем-то магическим, когда делаешь его вручную на уровне машинных команд. Поскольку цель здесь — опять же, не освоение конкретного ассемблера и даже не программирование на уровне ассемблера как таковое, а исключительно понимание того, как устроен мир, не следует, разумеется, снабжать ученика уже готовыми библиотеками, которые сделают за него всю работу (в частности, по переводу числа в текстовое представление); наоборот, написав на языке ассемблера простенькую программу, которая считывает из стандантного потока ввода два числа, перемножает их и выдаёт полученное произведение, ученик поймёт и прочувствует гораздо больше, чем если ему предложить написать на том же ассемблере что-то сложное и развесистое, но при этом перевод из текста в число и обратно выполнить за него в какой-нибудь библиотеке макросов. Здесь же следует посмотреть, как организуются подпрограммы с локальными переменными и рекурсия (нет, не на примере факториала, который высосан из пальца и всем уже надоел, скорее на примере сопоставления строки с образцом или ещё на чём-то подобном), как строится стековый фрейм, какие бывают соглашения о связях.

Следующим этапом может стать изучение Си (разумеется, чистого Си, причём даже не C99, а скорее ANSI C; всё, что было позже, со всеми VLA и _Complex — это результат террористической деятельности так называемого комитета по стандартизации, и влиять на образование вся эта муть не должна). Наконец, когда ученик окажется к этому готов, можно попытаться рассказать ему объектно-ориентированное программирование на примере C++, но для начала — строго без использования средств стандартной библиотеки этого языка, в противном случае как раз ООП и вообще сам язык C++ (при всех его странностях — один из самых интересных языков программирования среди всех существующих) останется за кадром.

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

При чём тут книга

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

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

Учебники для ВУЗов иногда (но далеко не всегда) пишутся людьми, предмет понимающими. К сожалению, в большинстве своём эти учебники несут на себе «проклятье мейнстрима» — они написаны в предположении, что в учебном процессе используются компьютеры c Windows, причём до сих пор издаются учебники, ориентированные на программирование под MS-DOS. Немногие ВУЗовские учебники, ориентированные на Unix, посвящены отдельным дисциплинам и не создают целостной картины предмета. Все прочие книги по программированию, не являющиеся ни школьным, ни ВУЗовским учебником, обычно расчитаны на профессионалов и для начального самообучения годятся слабо.

Книга под рабочим заглавием «ПРОГРАММИРОВАНИЕ: ВВЕДЕНИЕ В ПРОФЕССИЮ» должна, согласно замыслу автора, заполнить эту брешь.

[<<< НА СТРАНИЦУ ПРОЕКТА]