В этой статье я хотел бы осветить особенности обучения программированию людей, отличающихся по складу мышления от обучающего, на основе своего небольшого опыта. Специфика этого опыта состоит в том, что у меня не было возможности всесторонне исследовать разные типы гуманитариев и подходы к ним. В основном я работал с типом мышления, стезёй которого была выбрана философия. Преимущественно в разрезе философов дальше и пойдёт детализация.
Сразу отмечу, что ни в коей мере не претендую на истину как в последней, так и в предпоследней инстанции, либо широкий, обобщённый подход: всё, что я расскажу — просто изложение наиболее эффективного найденного мной способа научить гуманитария, в основном философа, программировать. Разные люди имеют свои отличия, в статье будут перечислены только основные, характерные для подавляющего большинства обучающихся-гуманитариев, с которыми я сталкивался.
Мой опыт, на основании которого я сделал описываемые выводы, возник из примерно шестилетней в общей сумме работы с людьми, желавшими или думавшими, что желают, обучиться программированию. В процессе работы велись как групповые, так и индивидуальные занятия с обучающимися (возрастом от 16 до 26 лет), величина групп была в среднем около 11 человек, по несколько групп параллельно. Группы были поделены по способам мышления и в среднем имели сравнимый уровень интеллекта. Это дало мне возможность наблюдать живьём, как сильно отличается скорость восприятия в разных областях программирования, что укрепило меня в подозрениях, что при вариации путей обучения скорость отстающих можно заметно повысить, если использовать базис, понимаемый в их системе координат и на уровне способа обработки информации. Попытки нащупать правильный подход к людям гуманитарного склада ума осуществлялись постепенно, без использования методических и психологических пособий, ориентированных конкретно на гуманитариев. Большинство прошедших мой курс в конце успешно ориентировались в подходах к решению некоторых задач, возникающих у них в хобби, и к которым применение программирования имеет (во всяком случае, видимый мне) смысл.
Стоит отметить, что решение задачи «сделать из гуманитария программиста» я разделяю на 3 больших вопроса.
1. Вопрос мотивации, или «зачем мне это надо?»
2. Вопрос способа обучения, или «как мне сделать так, чтобы компьютер меня понял?»
3. Вопрос поиска точки входа, или «с чего начать, когда передо мной встала конкретная задача?»
В основном в статье я постараюсь раскрыть именно второй вопрос, но кратко пройдусь и по остальным.
Вопрос мотивации
Думаю, не ошибусь, если скажу, что подавляющее большинство активных программистов, равно как и тех, кто этому обучает, — это люди технического склада ума. Среди технарей вопрос о мотивации — зачем им нужно учиться программировать? — особо болезненно не стоит: многие из них понимают выгоду умения читать, понимать или уметь писать код, применимость программирования в их увлечениях или предполагаемой будущей работе. В принципе, по опыту, лишь небольшое число технарей от этого занятия испытывает сильный негатив.
Иначе обстоит дело с гуманитариями.
Вопрос терминологии «кто есть гуманитарий, и чем он отличается от математиков/программистов/физиков-теоретиков» предлагаю оставить в стороне. В Интернете достаточно много определений, как правило, основанных на практических способах отличить один тип от другого. На интуитивном уровне по тому, как человек мыслит и выражает свои мысли, можно понять за довольно короткое время, к какому лагерю принадлежит этот человек. Предлагаю пока на таком интуитивном способе определения и остановимся.
Мотивация людей гуманитарного склада ума значительно осложняется тем, что, как правило, в их сфере деятельности и увлечений применение программирования (во всяком случае, не высших порядков) крайне ограничено, и если задачи, которые можно было бы решать с помощью программы, и появляются, то они совсем не просты и требуют хорошей подготовки, например, в области генетических алгоритмов.
Мотивировать с точки зрения расширения возможных сфер приложения в будущем, чтобы быть более востребованными в случае чего? Программирование в любом случае останется для них чуждой дисциплиной, нарушающей мирное течение Силы привычные им способы мышления и образ действий. Тратить серьёзные усилия на то, что делать будет в самом лучшем случае не очень неприятно, сложно, не приносить удовольствия от жизни и за разумные сроки не способствовать обретению гармонии с миром — не самая радужная перспектива.
Мотивировать с точки зрения расширения кругозора? На самом деле, способов расширить кругозор у гуманитариев не сильно меньше, чем у технарей, просто эти способы лежат не в настоящем-будущем, а в прошлом. История, религии, исследования мыслителей различных направлений — информации очень много. Так что наше с вами программирование не воспринимается как какой-либо идол, на который надо молиться, просто совсем. Учитывая частоту нападок на «тупых гуманитариев» со стороны причисляющих себя к человеческо-технарской элите человекообразных, такая попытка мотивации обречена на провал очень быстро.
Любая попытка мотивировать с помощью ориентирования на практическое применение — так сказать, хоть что-то материальное, что будет являться непосредственным продуктом их деятельности — тоже потерпит неудачу. Это непосредственное практическое применение им даром не сдалось…
Единственным способом мотивировать гуманитариев на изучение программирования, чтобы он давал хотя бы минимальную положительную тягу, для меня стал призыв к их универсальности. Гуманитарии, как правило, не любят быть очень узкими специалистами в своём деле. Круг их образования, в котором они считают, что должны быть выше среднего, довольно широк и не всегда лежит только в смежных с их профилем областях. Например, лингвисты часто любят самообразовываться в вопросах религии, историки — в искусствоведении (это, конечно, почти смежная дисциплина, но всё же), и так далее. На этом можно сыграть как на подложке для обоснования того, что им вообще это может быть интересно. Ведь занятие программированием сформирует им ещё один тип мышления, пусть и не на глубинном уровне, это сильно ново и сильно нестандартно для них, что, в свою очередь, рождает — поначалу, конечно же — неплохую тягу к борьбе с «этими ненавистными командами».
Разумеется, нельзя скидывать со счетов и возможность достижения в результате более хорошего взаимопонимания с людьми технического склада ума. Но до этого гуманитарии и сами додумываются.
Вопрос способа обучения
Итак, гуманитарий собран, готов к обучению и жаждет знаний. С чего же начать?
Здесь сразу следует сделать отступление в связи с качественно иным уровнем уровнем ожиданий от предмета. Технарь понимает, что результат не достигается за пару занятий, и что сложные программы собраны из кирпичиков, и что для того, чтобы получить конечный результат, нужно суметь собрать каждый кубик. Гуманитарий это тоже понимает, правда, не очень сильно об этом изначально задумывается — в гуманитарных дисциплинах такой ярко выраженной лестницы из навыков нет. При постановке нескольких задач из N кирпичиков технарь ищет возможность переиспользовать уже написанное, чтобы как можно меньше кирпичиков пришлось реализовывать в дальнейшем. Гуманитарий падает духом, потому что до его осознания очень, очень быстро докатывается, сколько мелкой, кропотливой, скучной работы нужно сделать, чтобы на выходе получить даже незначительный результат. При неправильном подходе убить любую готовность гуманитария всё-таки делать эту кропотливую работу очень легко, и потом его пряником не заманишь. С технарями легче — их дольше нужно мучить, чтобы они возненавидели программирование.
Хочу сделать ещё одно отступление. Дальнейший рассказ в большом количестве содержит указания на то, что привычный и понятный основному большинству технарей способ понимания определённой вещи для гуманитариев не подходит. Да, возникает большое желание схватиться за голову и возопить — как так, это же элементарно?! Я постарался максимально акцентировать то, что их способы понимания в базисе отличаются от технарного, что не делает эти способы глобально хуже — делает только восприятие программирования как дисциплины, придуманной, бесспорно, технарями, гораздо сложнее для них. При одинаковом результате на понимание определённого приёма программирования им потребуется приложить больше усилий, чем среднестатистическому технарю, вследствие другого склада ума. Давайте попробуем хотя бы в рамках этой статьи жить в мире, «где мерилом работы считают усталость».
Посмотрим на то, как в стандартной методике обучают человека, зачем нужен цикл. Например, надо вывести числа от 1 до 30.
Сначала предлагается выполнить это для 1 числа. Потом для 2. Потом для 3, и так далее. Понятно, что изначально люди просто будут писать в столбик команду на выведение очередного числа. До обучающегося быстро доходит, что данная стратегия не выигрышна в случае большого количества повторений. После чего делается переход к общему виду — вывести числа от 1 до N, где N будет, например, равен 30. Обучают соответствующей конструкции — и на этом цикл, можно сказать, усвоен.
Так вот, так работает — разумеется, с технарями — и только в лучшем случае с третьей частью гуманитариев.
Практика показывает, что гуманитарии очень чётко делятся как минимум по двум признакам. Это изначальная доступность метода экстраполяции для мозга и категория размера кирпичика, который человек может воспринять. Под кирпичиком во втором признаке подразумевается общность задачи, некая абстрактная мера абстракции, извините за тавтологию. Величина абстракции в попугаях, в общем.
Может показаться, что из наличия или отсутствия первого признака может достаточно логично следовать второй признак. Однако на практике такого не происходит. Как минимум, экстраполяция не всегда рождает абстракцию. Впрочем, оставим выяснение причин этого тем, кому это интересно; я лишь перечислю, на какие категории можно разделить людей по этим признакам:
а) Человек может хорошо понимать, что такое экстраполяция, и хорошо воспринимать только кирпичики небольшого объёма.
б) Может иметь большие проблемы с применением экстраполяции, и хорошо воспринимать только небольшие кусочки.
в) Или — третий вариант — может не иметь проблем с экстраполяцией, и при этом понимать только сразу большие кирпичики знаний.
Конечно, четвёртое сочетание встречается — не понимать экстраполяции и мочь воспринимать только большие куски знаний. Но оно редко встречается, значительно реже, чем остальные три.
В дальнейшем я буду касаться в основном вариантов, когда человек может воспринимать только сразу большие кирпичики знания, потому как именно с ними у меня было больше всего опыта — с философами. В подавляющем большинстве у них атрофируется способность понимать и — в том числе как следствие — экстраполировать мелкие куски, и задача, лишённая большой абстракции, становится с трудом преодолимой. Попробую показать, какой подход в задаче с циклом оказывается более легко усваиваем.
Итак, нам нужно научить человека циклу. Возьмём подход: у нас есть массив (не посчитать сумму чисел от 1 до скольких-нибудь, что вводит пользователь, потому что это — математическая задача, гуманитариям она банально неприятна). Массив объясняется как абстрактный тип данных, некий контейнер с кучей элементов одинакового типа, например, чисел, внутри. Нам нужно получить их сумму. Как это сделать?
Задача №1: придумать не математическую задачу, которая будет интересна обучающемуся, в которой это может понадобиться очевидным образом. Например, подсчёт количества публикаций конкретного автора в домашней библиотеке книг. Ни в коем случае нельзя оперировать понятиями «вот надо будет посчитать среднее, надо будет сумму посчитать, чтобы потом на неё разделить»… Это путь к провалу. Любая связь с математическими вычислениями, если она не заобфусцирована, будет рождать критическое внутреннее напряжение у обучающегося.
Задача №2: предложить решить эту задачу на понятийном уровне без рисования точного алгоритма или, упаси Боже, блок-схем. Достаточно того, чтобы обучаемый дошёл до стадии «смотрим на шкаф, перебираем книги подряд. Каждый раз, когда находим книгу этого автора, загибаем палец». Дальше мгновенно (мы всё-таки говорим о взрослых людях) приходит мысль о том, что можно рисовать палочку на листе бумаги вместо загибания пальца для учёта сколько угодно больших количеств книг.
Задача №3: спроецировать решение задачи, которое уже придумано на понятийном уровне, на… не алгоритм. Не блок-схему. Сразу на язык, на котором человек учится программировать.
Забудьте о том, что программирование в истинной сущности своей — умение сочинять алгоритмы. Оставьте это тем из технарей, у кого голова именно под это заточена. Сейчас у нас задача — научить человека делать то, к чему его образ мышления совершенно не готов.
Важно не пытаться научить «закоживать алгоритмы». Для того, чтобы это было можно сделать, гуманитарию нужно сначала создать алгоритм, что всегда сложно, неприятно и неинтересно. Если такой алгоритм уже есть — он найдёт программу, где алгоритм будет реализован, или посчитает на калькуляторе. Если алгоритм слишком сложен, чтобы его можно было посчитать на калькуляторе и бумажке — гуманитарий просто не станет решать такую задачу, если совсем не припрёт, и будет несчастлив всё это время. И вообще слово алгоритм лишает их душевного спокойствия в общем и желания заниматься программированием в частности.
В такой проекции лист бумаги станет переменной, хранящей число элементов, а проход по книжным полкам подряд — просмотр элементов массива один за другим. Важно не упрощать задачу так, чтобы она решалась в результате за одно действие — например, сразу переключиться на ООП и сказать, что это будет наш список, а вот этот метод вернёт количество элементов по фильтру в параметрах — и не болей. Всё-таки задачей является научить человека программированию, а это значит, что со всеми особенностями устроения его мозга ему придётся учиться складывать дом из кирпичей, и проще строить дом, чем небоскрёб из растущих панельных плит изначальной высотой в десять метров и саморазворачивающихся лестниц.
Очень важно после рассказа об очередном приёме программирования указать на то, с чем может такой приём использоваться. Для многих технарей интуитивно понятно, что в тело цикла можно поместить ещё один цикл или, на худой конец, вызов процедуры, в которой будет ещё что-нибудь. Большинство гуманитариев имеет базисную ассоциацию инструментов для программирования с обычной, к примеру, домашней техникой, в которую можно помещать только определённые предметы, и на выходе будет получаться один из тех предметов, который указан в инструкции в перечне возможных результатов этой техники. Необходимо показать, что инструмент программирования — тоже абстрактная вещь, которая имеет некоторые рамки, но в качестве того, с чем этот инструмент будет работать, может использоваться более широкий класс предметов, чем просто перечень конкретных примеров. Неплохо работает принцип ограничения «от противного», в духе запрета класть кошку в микроволновку, но всегда нужно точно указывать, с каким классом, типом, формой входных данных инструмент может работать, а также на то, что его можно сочетать с другими инструментами программирования. Фраза «это будет работать с чем угодно» некорректна, потому что они туда попробуют положить какой-нибудь вопрос экзистенциальности в общем виде и не смогут понять, какого результата ждать.
Также я очень редко наблюдал у обучающихся желание попробовать, как поведёт себя какой-либо инструмент, если в него положить что-нибудь неожиданное/другой тип данных и так далее. Ведь правила, по которым работает инструмент, продиктованы алгоритмами, то есть пытаться их понять — больно, долго и сложно, поэтому это не делается, во всяком случае, поначалу. Частично этот недостаток любопытства, назовём его так, восполняется за счёт умения воспринимать типы входных данных абстрактно, но всё же это накладывает некоторые дополнительные условия на преподавателя, когда начнётся обучение динамическим типам данных и объектам. В этом вопросе, как это ни парадоксально, гуманитарии куда более конкретны, чем технари.
Больным местом при обучении гуманитариев с проблемным восприятием небольших кусочков знаний становятся частные случаи и граничные условия. К сожалению, ничего лучше, чем дать запомнить, какие самые распространённые граничные условия для каждого типа связи между хранимыми данными стоит рассматривать, я не нашёл. Дальнейшие проверки будут устраиваться по принципу наработки опыта и набивания шишек, настолько мало и медленно, насколько возможно, и это хорошо — каждая такая проверка является серьёзным испытанием целеустремлённости гуманитария.
Набивание шишек, естественно, происходит за счёт получения ошибок в программе. Здесь ждёт засада: приходится обучать начинающего программиста читать на языке компилятора/интерпретатора (в дальнейшем просто компилятор, так как практика показывает, что интерпретатор приводит гуманитариев в задумчивость по нескольким причинам, одна из которых — «если он делает всё по шагам, зачем нам напрягаться и писать программу, когда мы сами можем выполнить нужные шаги?» Кроме того, в языках с компиляторами проще искать ошибки: гуманитарию в голову не придёт засунуть в переменную значение не того типа, например; любопытство их имеет совсем другую природу). Компилятор, как правило, не слишком дружелюбен в демонстрации истинной ошибки в программе, гораздо чаще это ошибки в духе «неожиданная запятая». В таких случаях помогает давать обучаемому смотреть примеры такого же по структуре кода, написанного ранее, и работающего. Важно, чтобы код писался всегда в едином стиле. Я не встречал у гуманитариев с этим проблем. Совсем. Сказано писать в таком стиле — будут писать в таком. Потом поправят, если им как-то иначе будет удобнее.
Важно!
Как и большинству людей, гуманитариям важно видеть результат как можно меньшего количества подряд кода. Нет, не как большинству людей — гораздо сильнее. Это даёт им столь необходимую мотивацию, чтобы продолжать. При этом — парадокс! — они очень не любят делать лишнюю работу, то есть писать дополнительные печати, которые для получения готовой программы придётся удалить. Хорошим способом является дать им уже заранее написанную мелкую функцию, которая будет записывать эту строку, которую они хотят вывести, не на экран, а в файл, и чтобы файл был открыт у них всё время на заднем плане (или втором мониторе). Процесс созерцания результатов работы значительно подкрепляет их веру в свои силы.
Практика показывает, что абстрактные вещи, которые нельзя «пощупать» (или которые на базовом уровне для этого не предназначены), гуманитарии воспринимают хорошо и как будто легко. Например, так обстоит дело с указателями в Си. Главное — не вдаваться в подробности использования операторов, унарных, бинарных и представление данных внутри компьютера. Это всё очень конкретное и не представляет интереса, как и помощи в будущем программировании на том уровне, на котором им хотелось бы использовать его в принципе.
Повышение уровня абстракции представления данных также даётся им легко. Так что если обучение идёт тяжело, всегда можно временно приподняться над уровнем элементарных типов данных, снизить стресс, получаемый при обучении тому, что не получается, и аккуратно вернуться после этого.
Проблемы возникают тогда, когда с этими абстрактными вещами нужно конкретно работать. То есть тут две проблемы: первая проблема — от некой задачи в довольно общем виде перейти к взаимодействию с частностями (трансформация частного в общее), а потом, поняв, какие инструменты нужны для работы с этими конкретными составляющими задачи, снова подняться на уровень унификации этих инструментов (то есть трансформация частного + методов работы с ними в общее). Гуманитарии боятся инструментов языка, воспринимаемых ими, в общем-то, совершенно правдиво, как имеющих некоторую техническую основу, и не могут ими пользоваться вне рамок, которые им ставятся при подавании этого инструмента. Нужно многократно подчёркивать, что от их действий ничего глобально не сломается, и от того, что они не уверены, какое действие нужно выполнить дальше, необходимо перейти к можно пытаться по-разному, и ничего плохого не случится. В их представлении задача представляется как некое глобальное облако, и задача его сгущения для конкретизации и вычленения предметов, с которыми можно взаимодействовать с помощью инструментов, которые им давались, для такого типа мышления очень сложна.
К сожалению, сложность этой задачи ещё в том, что попытки разбиения и, таким образом, упрощения задачи наталкиваются на непринятие в качестве доступного действия. Ведь в их мышлении, если абстракцию разделить на две части, всё равно получится абстракция (а не две абстракции). В моей практике это было критическим моментом образования другого типа мышления: если обучающийся создавал у себя в мозгу ещё одну операцию, которая говорит, что эти программистские абстракции — не такие, как все, а делимые, и их можно и нужно делить, то дальше шло гораздо легче.
Попытки привить обратный, пошаговый метод написания программ к успеху не привёл, посколько он идёт вразрез с методами работы с гуманитарными дисциплинами. Кроме того, большая трудность была встречена ещё в одном аспекте: целостность решения. Мне оказалось очень сложным научить записывать то, что обучаемые уже придумали, как сделать: если они это придумали, то чего там сложного, зачем записывать? С проблемой нехватки «оперативной памяти» обучаемых я столкнулся довольно быстро, и необходимость целостности придуманного решения предстала передо мной во всей красе. Побороть это я смог только методом дачи сильно последовательного возрастания «лестничности» предполагаемых решений задач, тогда способ реализации каждой ступени занимает совсем мало памяти в процессе разработки решения, и задачи успешно решаются.
Вопрос поиска точки входа
Это один из самых сложных вопросах на первых порах обучения.
Во-первых, потому что задачи должны быть поначалу очень сильно детерминированы. Не должно допускаться вещей, которыми я сам при постановке задач грешу довольно сильно: недостающие условия заполняются по усмотрению обучающегося. Например, способ ввода исходных данных. Такие вещи должны быть строго определены.
Ключевым моментом здесь для меня явилось то, что необходимо тщательно рассказать, каким образом делается экстраполяция имеющихся типов данных на те абстракции, с которыми им может придтись работать. Что аналогией клетки является объект, хранящий параметры и другие объекты. Что ДНК можно рассматривать как множество элементов, то есть редуцировать до массива. В данном случае основным направлением объяснения для тех гуманитариев, кто хорошо понимает экстраполяцию, должна быть экстраполяция типы данных на типы, с которыми работать потом по жизни, а для остальных — редукция из вещей, с которыми придётся работать, к некоему фиксированному формату типов данных и инструментов для программирования.
Во-вторых, опять вылезает некая обязательность целостности решения в голове у обучаемого. Необходимо делать ударение на том, что нет ничего зазорного или плохого в том, чтобы разбираться с некими проблемными местами потом, когда остальное решение уже готово. Проблема упихать имеющийся объект в тип данных (или натянуть тип данных на имеющийся объект) — часто возникающая проблема, и чем раньше обучаемые знакомятся со объектными типами данных (структурами), тем легче им потом обучаться ставить в соответствие программные термины и термины из реальной жизни.
В-третьих, когда гуманитарий думает, как он будет решать поставленную задачу, частой практикой у них в голове является воссоздание «как бы я стал решать такую задачу?» — и поэтому при начале написания программы первым же вопросом оказывается «а где всё то, с чем я работал только что в голове?» Необходимо чётко и несколько раз, с введением каждого нового типа данных, объяснять, откуда могут появиться данные в этой переменной/в этом объекте, и давать примеры заполнения их данными. Тогда в голове гуманитария родится ответ в духе «раз я собрался решать задачу через массив, способ его заполнения подходит тут следующий...» — и написание программы пойдёт уже своим нормальным путём.
В заключение хочу сказать, что, хоть обучение программированию и довольно тяжело поначалу людям с гуманитарным складом ума, в дальнейшем они начинают применять методы, которые они опробовали, в различных комбинациях хоть и без изворотливости и любимой многими программистами утончённой изощрённости, но вполне уверенно. А кроме того, они получают удовольствие от осознания того, что получили возможность заглянуть в мир непривычных импликаций и преобразований глазами технарей и получить, таким образом, новый опыт.
И в качестве совсем уже итогов — краткий список «хинтов», до которых я дошёл. Вдруг пригодится кому, если срочно нужно будет подготовиться.
только не математические задачи;
точность определений рамок, в которых могут использоваться методы и инструменты программирования;
«разделяй и властвуй» в отношении программистских абстракций, которые не такие, как все;
разделение способов объяснения применимости методов программирования к реальным задачам — по типам мышления, хорошо владеющим экстраполяцией и плохо;
точная постановка задач;
пошаговый метод решения задач успеха не даёт;
сильно ступенчатое наращивание количества связей, которые нужно построить для решения очередной задачи, несмотря на лёгкость восприятия обучаемым очередной темы, из-за необходимости построить сразу целостную модель решения;
«нет» реализациям алгоритмов (математических).
Удачи всем, кто будет заниматься подобным. Надеюсь, эта статья чем-нибудь поможет и подскажет в такой ситуации.
И выражаю большую признательность всем своим «подопытным».
P.S. Если кто-то нашёл ещё способы хорошо мотивировать гуманитариев на изучение этой дисциплины — буду рад услышать.
Гениальный курс “Программировать может каждый” (http://pmok.ru) помог мне многое переосмыслить