Предисловие второе, методическое

Программирование: введение в профессию. 1: Азы программирования - 2016 год

Предисловие второе, методическое

Это предисловие я адресую, как ни странно, не тем, кого считаю основной аудиторией моей книги, то есть не тем, кто решил изучать программирование; для них у меня припасено ещё одно предисловие, которое я назвал “напутственным”. Что до методического предисловия, то оно предназначено скорее для тех, кто программировать уже умеет, а также и для моих коллег-преподавателей; здесь я попытаюсь объяснить мой подход, а заодно и причины появления этой книги.

Можно ли выучить программиста

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

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

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

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

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

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

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

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

Выход есть, или “Почему Unix”

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

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

При внимательном рассмотрении именно системы семейства Unix оказываются не только подходящим, но и (в современных условиях) единственным возможным вариантом, коль скоро речь идёт об обучении программированию. Я позволю себе выделить здесь четыре причины.

Причина первая — математическая

Любая компьютерная программа есть, как известно, запись некоторого алгоритма на избранном языке программирования. Что такое алгоритм — никто в действительности не знает и знать, что интересно, не может, иначе пришлось бы выбросить на свалку всю теорию вычислимости заодно с теорией алгоритмов, забыть тезис Чёрча-Тьюринга и вообще отказаться от теоретической составляющей computer science. Тем не менее принято считать, что всякий алгоритм выполняет преобразование из множества слов (цепочек символов) над некоторым алфавитом в само это же множество. Конечно, не всякое такое преобразование может быть выполнено алгоритмом, ведь преобразований таких, как несложно показать, континуум, тогда как алгоритмов — множество не более чем счётное; более того, алгоритм сам по себе не есть такое преобразование, ведь речь иногда может идти об эквивалентных алгоритмах, то есть таких, которые из одного и того же входного слова всегда “делают” одно и то же выходное; иначе говоря, для одного и того же преобразования может существовать больше одного алгоритма (если быть точным, для каждого преобразования алгоритм либо не существует, либо их существует бесконечное количество). Тем не менее, всякий алгоритм выполняет именно такое преобразование, и только этим он, вообще говоря, интересен. Если угодно, алгоритм — это такая штука, которая берёт некое входное слово (“прочитывает” его), что-то там такое конструктивное делает и выдаёт другое слово; неопределённость понятия здесь заключена в слове “конструктивное”, которое тоже, разумеется, невозможно определить.

Многие программы в системах семейства Unix работают именно так: читают данные из стандартного потока ввода и записывают их в стандартный поток вывода. Такие программы имеют даже своё название — фильтры. Благодаря развитым средствам командной строки такие программы-фильтры можно комбинировать “на лету”, решая самые разнообразные задачи, сводящиеся к преобразованию текста. Поскольку текстовое представление практически универсально, алгебра консольных программ оказывается средством неожиданно мощным. Каждая новая консольная программа, сколь бы простой она ни была, становится частью этой системы, делая саму систему ещё немного мощнее, а диапазон решаемых задач — ещё немного шире. При этом программы-фильтры полностью соответствуют пониманию алгоритма как преобразования из входного слова в выходное.

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

Причина вторая — психологическая

Программирование — это, в конечном счёте, не более чем ремесло, а ремесло выучить невозможно, ему можно только научиться. Прежде чем новичок превратится в программиста, ему необходимо сделать ряд очень важных шагов. Первый шаг — это переход от задач из учебника к задачам, поставленным самостоятельно, при этом не вымученным, не делаемым “потому что надо”, а таким, которые делаются, потому что данному конкретному субъекту показалось интересно подчинить себе компьютер и заставить его решить именно такую задачу.

Второй шаг — переход от этюдов к реальному решению реально вставшей перед учеником проблемы, пусть сколь угодно простой, но настоящей. Это может быть календарь или записная книжка, напоми- налка о днях рождения друзей, какой-нибудь простой преобразователь текстов (а хоть бы и для удаления лишних пробелов), всё что угодно. Для автора этих строк в своё время такой программой стала “ломалка” для игры “F-19”, которая подправляла байтик в файле списка пилотов, “оживляя” тех, кто помечен как погибший. Найти нужный байтик оказалось существенно сложнее, нежели потом написать программу на Паскале, которая в нужные позиции нужного файла записывает нули, и тем не менее именно эта примитивная, на один экран программка позволила прыгнуть, как говорят, на следующий уровень, о чём сам ваш покорный слуга догадался лишь лет через десять.

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

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

Так вот, обучая человека программировать с использованием систем семейства Windows, мы тем самым лишаем его возможности сделать все три вышеперечисленных шага. Настоящую программу под Windows, под каковой можно понимать исключительно оконное приложение, можно написать, только уже будучи программистом; текстовые программки, про которые ученикам никто даже не объясняет, как их правильно запускать (ага, привет reading в конце каждой программы), настоящими не выглядят ни анфас, ни в профиль, а потому у такой программы никогда не появится стороннего пользователя, да и сам автор пользоваться этим непонятно чем не станет. Больше того, если результат столь убог и нет никаких шансов сделать его сколько-нибудь похожим на нечто настоящее, вряд ли нашего обучаемого заинтересует перспектива потратить несколько часов оставшейся ему жизни, чтобы сделать вот такое вот никуда не годное решение пусть даже очень интересной задачи.

Именно поэтому обучаемому жизненно важно не просто программировать под Unix, ему необходимо жить под Uniх’ом, то есть именно Unix (будь то Linux или любая другая Unix-система) использовать в повседневной работе, для прогулок по Интернету, общения по электронной почте и через разнообразные мессенджеры, для работы над текстами, для просмотра фильмов и фотографий, вообще для всего, для чего обычно используют компьютеры. Только в этом случае у нашего обучаемого на более-менее ранних стадиях развития может возникнуть такая потребность из повседневной жизни, для которой может понадобиться написать программу.

Причина третья — эргономическая

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

Не представляя своей жизни без GUI, вы никогда не поймёте, как на самом деле должна выглядеть работа с компьютером. Большинство современных программ с точки зрения usabilityпредставляет собой уродливых монстров, на борьбу с изъянами которых у пользователей уходит девять десятых всех сил, при этом пользователи ухитряются этого положения в упор не замечать, так как просто не знают, что может быть как-то иначе. Именно поэтому необходимо освоить командную строку (пусть даже не строя планов по прекращению использования GUI: это произойдёт само собой), и сделать это нужно как можно раньше, пока мозг не потерял способности к быстрому обучению и мгновенной адаптации к непривычным условиям: после 25 лет изучать нечто принципиально новое становится настолько сложнее, что мотивация для этого требуется качественно более высокая.

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

Причина четвёртая — педагогическая

Если среди учеников, пытающихся освоить программирование в нечеловеческих виндовых условиях, всё же окажется будущий программист, страшные псевдо- и недопрограммы, которые его заставляют писать (чаще всего в какой-нибудь безнадёжно мёртвой среде вроде Turbo Pascal), очень быстро перестанут такого субъекта удовлетворять, и ему захочется чего-то настоящего. Учитель вряд ли станет объяснять продвинутому ученику, как писать оконные программы под Windows (большинство учителей не умеют этого сами), но будущего программиста такие мелочи не остановят. Взяв в руки первую попавшуюся книжку, он освоит рисование окошек самостоятельно.

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

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

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

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

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

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

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

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

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

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

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

Итак, если иметь в виду в будущем изучение Си, то предварительно нужно освоить какой-то язык, в котором а) есть указатели, причём в полный рост, без всякой сборки мусора; б) без указателей можно обходиться, пока обучаемый не окажется более-менее готов к их восприятию; и в) начав использовать указатели, обучаемый расширит свои возможности, то есть в указателях должна быть реальная потребность. Замечу, без пункта в) можно было бы использовать C++/STL, но когда в распоряжение ученика попадают всякие vector, list и пр., стимул для применения низкоуровневых указателей неизбежно пропадает вместе с возможностью когда-нибудь научиться самостоятельно создавать сложные проблемно-ориентированные структуры данных. А вот всем трём пунктам одновременно удовлетворяет только Паскаль; этот язык позволяет подойти к указателям плавно и издали, не используя и не вводя их до тех пор, пока уровень обучаемого не станет для этого достаточным; в то же время с момента их введения указатели в Паскале проявляют практически все свойства “настоящих” указателей, за исключением разве что адресной арифметики. Поиск другого языка с аналогичными возможностями по изучению указателей оказался безрезультатным; похоже на то, что Паскалю просто нет альтернативы.

С другой стороны, если мы рассматриваем изучение Паскаля как подготовительный этап перед Си, можно для экономии времени оставить за кадром часть его возможностей, таких как тип-множество, оператор with и вложенные подпрограммы. Следует помнить, что целью изучения здесь является не “язык Паскаль”, а программирование. Нет совершенно никакого смысла в настойчивом вдалбливании ученику формального синтаксиса, таблиц приоритетов операций и прочей подобной ерунды: на выходе нам нужно получить не знание языка Паскаль, который, возможно, ученику никогда больше не понадобится, а умение писать программы. Наиболее высокими барьерами на пути ученика здесь оказываются, во-первых, всё те же указатели, и, во-вторых, рекурсия, с которой тоже можно научиться работать на примере Паскаля. Отметим, что модуль CRT, нежно любимый нашими педагогами1, во Free PascalA под Linux и FreeBSDзамечательно работает, позволяя создавать полноэкранные терминальные программы; на Си это сделать гораздо труднее, автор этих строк в своё время, будучи уже опытным программистом, потратил несколько дней на то, чтобы более-менее разобраться с библиотекой ncurses.

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

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

Коль скоро изучать программирование на ассемблере всё равно придётся, логично это сделать до изучения Си, поскольку это во многом помогает понять, почему язык Си именно таков, каков он есть. Концепция “Си как заменитель языка ассемблера” очень сильно помогает в изучении этого достаточно странного языка. Адресная арифметика, присваивание как операция, отдельные операции инкремента и декремента и многое другое — всё это гораздо легче понять и принять, если уже знать к этому времени, как выглядит программирование на уровне команд центрального процессора. С другой стороны, идею начать обучение программированию с языка ассемблера не хочется даже обсуждать, это заведомый абсурд.

С учётом сказанного выстраивается довольно однозначная цепочка языков для начального обучения: Паскаль, язык ассемблера, Си. В эту цепочку практически в любом месте можно что-нибудь добавить, но ни убирать из неё элементы, ни переставлять их местами, судя по всему, нельзя.

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

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

Именно такова традиционная последовательность программистских курсов на факультете ВМК: в первом семестре курс “Алгоритмы и алгоритмические языки” поддерживается практикумом на Паскале, во втором семестре лекционный курс так и называется “Архитектура ЭВМ и язык ассемблера”, а лекционный курс третьего семестра — “Операционные системы” — предполагает практикум на Си.

Несколько сложнее с четвёртым семестром; лекционный курс там называется “Системы программирования” и представляет собой довольно странное сочетание введения в теорию формальных грамматик и объектно-ориентированного программирования на примере Си++. Если говорить о моём мнении, то Си++ вряд ли можно назвать удачным языком для первичного освоения ООП, да и вообще этому языку не место в программах основных курсов: те из студентов, которые станут профессиональными программистами, этот язык могут освоить (и осваивают) сами, тогда как тем, кто будет работать по родственным или вообще другим специальностям, поверхностное знакомство с узкоспециальным профессиональным инструментом, каким, несомненно, является язык Си++, не прибавляет ни общего кругозора, ни понимания устройства мира.

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

Как испортить хорошую идею и как её спасти

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

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

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

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

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

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


1Настолько, что сакраментальное “uses crt;” часто можно увидеть в программах, не использующих никакие возможности из него, причём даже в учебниках.






Для любых предложений по сайту: [email protected]