|
|
||
Пятница, утро, и на вас падает задача в проекте, где основной язык вы никогда не писали. Может это Go, может Rust, может встроенный DSL, который в компании зовут "конфигом", хотя это полноценный язык с компилятором и ошибками. Дедлайн через две недели. Первый порыв понятен: открыть учебник с первой страницы и начать проходить синтаксис по параграфам, как в школе проходили иностранный. Этот порыв и есть главная ошибка, потому что школьная методика рассчитана на годы и на отсутствие дедлайна.
Хорошая новость в том, что мозг уже располагает готовым аппаратом для такой работы. В 2020 году группа нейробиолога Шантель Прат из Университета Вашингтона опубликовала в Scientific Reports эксперимент, где взрослые люди без опыта программирования за десять сессий осваивали Python. Предиктор успеха искали среди разных кандидатов: числовой интеллект, рабочая память, логика. Сильнейшим оказался лингвистический потенциал, измеренный классическим тестом MLAT, который применяют при отборе на изучение иностранных языков. Математика проиграла языковому чутью. Рисунок активности мозга в покое, снятый до начала обучения, позволял заранее выделить тех, кто выучится быстро. Программирование оказалось для мозга чем-то куда более похожим на второй язык, чем принято думать.
Из этого факта следует целая стратегия. Если язык программирования осваивается теми же механизмами, что и иностранная речь, то к нему применимы методы, отработанные десятилетиями исследований усвоения второго языка. Закономерности забывания и эффекта тестирования, механика имитации образцов, роль чтения до начала письма. Эти методы почти никто не использует, потому что культура обучения программированию застряла в парадигме "сначала весь синтаксис, потом проекты".
Дальше разберём, как устроено чтение кода глазами эксперта, что именно стоит заучивать, а что вечно держать в справочнике, и как выстроить первые сорок часов работы, чтобы не узнать сто слов из учебника, а начать писать своими руками.
Свидетельств того, что код обрабатывается речевыми механизмами, накопилось достаточно. В 2020 году лаборатория Марины Бедни из Университета Джонса Хопкинса опубликовала в eLife фМРТ-исследование, где участники читали и оценивали короткие программы на Python. Задействовались как классические "языковые" зоны коры, так и лобно-теменная сеть, которую нейробиологи называют multiple demand, "многозадачной". Она же включается при логическом выводе и решении новых задач. Метаанализ фМРТ-исследований понимания программ, опубликованный группой Ёсихару Икутани в 2021 году, подтвердил картину: понимание кода опирается одновременно на "речевые" и "математические" сети.
Практический вывод двойственный. С одной стороны, к языку программирования применимы приёмы из освоения иностранного: словарь заучивают порциями, чтение предшествует письму, имитация предшествует свободной речи. С другой стороны, код требует трассировки, то есть прокрутки состояния в голове. Это отдельный навык, и он тренируется отдельно. Частая ошибка новичка в том, что он пытается совместить обе нагрузки сразу: одновременно разбирать незнакомый синтаксис и следить за данными. Рабочая память захлёбывается, и ни одна из задач не выполняется.
Стратегия, вытекающая из нейробиологии, проста. Разнесите две нагрузки по времени. Сначала только парсинг: научиться узнавать конструкции и угадывать их смысл без трассировки. Потом только трассировка на уже знакомом синтаксисе, когда глаза не спотыкаются о слова. Так читает ребёнок: пока он занят дешифровкой букв, смысл произведения тонет. Фаза беглого чтения должна прийти раньше фазы глубокого анализа.
Классическая цифра Джорджа Миллера из статьи 1956 года: семь плюс-минус два элемента удерживаются одновременно. Современные оценки скромнее. По данным Нельсона Кована (2001), рабочая память вмещает около четырёх смысловых единиц, чанков. Фелине Херманс, исследователь из Утрехтского университета, в книге The Programmer's Brain (2021) показала, что чтение кода упирается именно в этот сосуд. Программист забывает значение переменной к моменту, когда она возникает в двадцатой строке, и стоимость забывания измеряется минутами перечитывания.
Отсюда жёсткое следствие для планов обучения. Заучивать весь словарь языка бессмысленно и даже вредно, потому что он вытесняет из ограниченной памяти то, что обязано быть под рукой. Разделите лексику на ядро и справочник. Ядро извлекается мгновенно: переменные и присваивание, условный переход, циклы, функции и возврат значения, базовые коллекции, обработка ошибок, импорты. Восемь тем, на освоение которых активным письмом уходит несколько дней. Справочник - всё остальное: стандартная библиотека, сигнатуры, флаги форматирования, тонкости синтаксиса.
Проверьте себя на бумаге без редактора. Выпишите столбцом эти восемь тем и против каждой поставьте плюс только в том случае, если конструкция пишется из памяти за пять секунд. Всё, что не получило плюса, идёт в план ближайших дней, остальное вычёркивается из списков на заучивание. Человек, честно проведший такой аудит, обычно обнаруживает, что заученное им и нужное ему пересекаются процентов на тридцать. Остальные семьдесят усилий ушли бы в зону справочника, куда можно заглянуть и без зубрёжки.
Заглядывать в справочник - не признак слабости, а часть профессии. Сеньор с пятнадцатилетним стажем не помнит порядок аргументов у редкой функции, и это нормально. Асимметрия информации давно решена в пользу "знать, где искать": помнить надо карту местности, а не каждый дом. Катастрофа случается, когда человек не помнит даже карты и ищет каждый раз с нуля. Тест на зрелость в новом языке звучит так: можете ли вы без подсказок написать цикл по коллекции, условный выбор и функцию с двумя параметрами. Если да, ядро собрано и можно двигаться дальше.
Есть нюанс, о котором почти не говорят на курсах. Трудность освоения - не всегда ваша заслуга, часто это свойство конструкции самого языка. Андреас Стефик и Андреа Сиберт на конференции OOPSLA в 2013 году опубликовали жёсткий по методике эксперимент. Новички без опыта писали программы на разных языках, включая специально сконструированный Randomo. Его синтаксис генерировался случайно: ключевые слова заменялись произвольными символами. Ожидалось, что любой нормальный язык окажется сильно лучше бессмыслицы. Ожидали зря. Точность задач на Perl оказалась статистически неотличима от Randomo. А вот язык Quorum, чей синтаксис годами проверялся на живых людях эмпирически, дал заметно лучший результат.
Вывод стоит проговорить спокойно. Значительная часть боли при освоении популярных языков - не тупость учащегося, а исторический багаж дизайна, который никто не тестировал на новичках. Церемония Java из пяти строк на один вывод в консоль, трёхэкранные диагностические сообщения компилятора C, перегруженный синтаксис C++ не измерялись экспериментально, они просто достались в наследство. Осознание этого факта экономит эмоциональные ресурсы. Вместо самобичевания "у меня не получается" появляется точная формулировка: "этот интерфейс спроектирован плохо для моей задачи".
Практических правил три. Если выбор первого языка за вами, берите тот, где пишут коротко и сообщения об ошибках человеческие. Если язык навязан работой, снизьте планку на первые две недели и не судите свой интеллект по скорости. И третье: читайте сообщения об ошибках целиком, как читали бы незнакомое предложение на иностранном. Компилятор - единственный носитель языка, доступный бесплатно круглосуточно, который никогда не устаёт объяснять грамматику.
Тереза Бушан с коллегами опубликовала в 2015 году на конференции ICPC исследование движений глаз с говорящим названием "Расслабление линейного порядка". Новичкам и экспертам давали короткие тексты и фрагменты Java, записывая траекторию взгляда. Читая английский текст, обе группы двигали глазами слева направо и сверху вниз одинаково. На коде картина раскололась. Новички продолжали читать программы как прозу, строка за строкой. Эксперты ходили по коду прыжками: к первым строкам объявлений, назад к вызову, вперёд к закрывающей скобке. Линейность чтения падала, а понимание росло.
Механизм такого чтения описан ещё в работе Чейза и Саймона о шахматистах (1973). Гроссмейстер восстанавливает по памяти позицию на десятки фигур лучше новичка, но только пока позиция осмысленная. Из случайной расстановки он запоминает не больше любителя. Значит дело не в объёме памяти, а в единицах восприятия. Мастер видит не тридцать две фигуры, а пять-семь смысловых блоков: "структура королевского фланга", "висячая пешка", "вилка". Точно так же опытный программист видит не девять операторов, а один чанк: "чтение файла с защитой от отсутствия".
Это меняет задачу обучения радикально. Учить надо не строки, а идиомы: устоявшиеся конструкции из трёх-семи элементов, которые читаются целиком. Списковое включение, безопасное открытие файла, цепочка проверок через ранний выход, обход словаря в цикле. Вот две записи одной и той же операции на Python, сначала буквально, потом чанками.
result = []
for item in orders:
if item.is_paid:
result.append(item.total * 0.8)
paid_discounted = [item.total * 0.8 for item in orders if item.is_paid]
Новичок в первой записи прочтёт пять операторов и загрузит рабочую память на пять единиц. Читающий во второй записи увидит один чанк с подписью "отфильтровать и пересчитать" и освободит память для смысла задачи. Скорость чтения растёт не потому, что глаз научился бегать быстрее, а потому, что единицы восприятия укрупнились.
Теперь алгоритм чтения незнакомого файла в чужом проекте. Первый шаг - поведение, а не текст: найдите тесты или запустите программу, чтобы понять, что она делает. Второй - точка входа: функция main, обработчик маршрута, колбэк кнопки, но не зависимости сразу. Третий - карта: пробегитесь только по именам функций и классов, игнорируя тела, оцените рельеф модуля. Четвёртый - данные: найдите, откуда вход приходит и где превращается в выход, и двигайтесь по этой линии. Пятый - укол инструментом: вместо многострочной трассировки глазами поставьте точку останова в отладчике и шагните пять шагов. Глаза дешевле паники, но отладчик дешевле глаз.
Самый дорогой по неэффективности способ учить - перечитывание, самый выгодный - припоминание. Родигер и Карпике в Psychological Science (2006) показали эффект тестирования. Студенты, прочитавшие текст один раз и потом дважды себя проверявшие, через неделю удержали заметно больше тех, кто перечитал материал четыре раза. В перечитывании есть ловушка беглости: текст знаком, глаз скользит, уверенность растёт, а знаний нет. Для иностранной лексики существует обширный пласт результатов по интервальному повторению. Свежий пример - исследование Марианы Макис и коллег (2021) о разнесённых повторах при заучивании коллокаций второго языка: интервалы стабильно выигрывают у повторов подряд.
Что из этого следует для программиста. Сделайте маленький набор карточек с идиомами, но с важным отличием от школьных карточек. На одной стороне не вопрос из серии "как называется декоратор", а микро-задача: "напиши открытие файла с гарантией закрытия при ошибке", "переверни список", "проверь ключ в словаре и дай значение по умолчанию". Припоминание должно требовать производства текста, а не узнавания. Пять карточек по десять минут в день на протяжении месяца дают больше, чем выходные с учебником, потому что интервалы сильнее плотной зубрёжки.
#!/usr/bin/env python3 # десять минут разминки перед работой: # 1) напиши из памяти dict.get с дефолтом # 2) напиши списковое включение с условием # 3) напиши with open(...) не заглядывая в IDE # сверился, закрыл, пошёл работать
Похожая ловушка устроена вокруг автодополнения. Сламека и Граф описали ещё в 1978 году эффект генерации: слово, которое человек произвёл сам, запоминается лучше прочитанного в готовом виде. Когда среда подсказывает метод за две буквы, она оказывает обучению медвежью услугу: код пишется быстро, а словарь не закрепляется. На время интенсивного освоения языка автодополнение уместно глушить хотя бы разминки, чтобы рука сама вспоминала конструкцию целиком.
В методике обучения письму на иностранном языке давно отработана лестница: имитация, адаптация, порождение. Сначала переписываешь чужой образец, потом меняешь в нём элементы под свою задачу, потом пишешь своё с опорой на накопленные шаблоны. Код подчиняется той же лестнице. Сначала вы руками перепечатываете короткий рабочий фрагмент из документации или живого проекта, добиваясь прохода без ошибок. Потом меняете в нём условия: другой формат входа, другой критерий фильтрации. Потом пишете похожую утилиту с нуля, заглядывая только в сообщения об ошибках.
Каждая ступень обязана даваться с трудом определённой интенсивности. Андерс Эрикссон, автор концепции осознанной практики (deliberate practice, 1993), формулирует требование точно: задача строго чуть выше текущего уровня, немедленная обратная связь, повторный заход с поправкой. В программировании обратная связь доступна бесплатно и мгновенно: интерпретатор, REPL, тесты, линтер. Ученик иностранного языка узнаёт о своей ошибке от собеседника через неловкое молчание, а вы - через полсекунды от компилятора.
Конкретная форма практики - портирование собственной утилиты. Возьмите маленький скрипт, который сами написали раньше на знакомом языке: парсер лога, генератор отчёта, переименование файлов. Размер в сто-двести строк, не больше. Перепишите его на новом языке, не подсматривая в оригинал и держа логику в голове. Задача кормит три канала сразу. Вы производите код, что включает эффект генерации. Вы ограничены рамками уже решённой задачи, то есть остаётесь в зоне "чуть выше уровня". Вы собираете личный словарь идиом, потому что каждая недостающая фраза встаёт на своё место в реальном тексте, а не в абстрактном упражнении.
Одно предостережение по календарю. Не начинайте два похожих языка в один месяц. Там, где совпадает половина синтаксиса, идиомы путаются, и интерференция бьёт по обоим словарям сразу. Классическая ловушка - параллельное штудирование Java и C# или Java и JavaScript в надежде "закрыть сразу два требования вакансии". Исследования усвоения второго языка фиксируют, что интерференция сходных форм отнимает больше сил, чем даёт экономия на общих корнях. Осваивайте языки последовательно с промежутком в два-три месяца, когда первый уже держится на чанках, а не на сознательном вспоминании.
Отдельного упоминания заслуживает работа с ошибками как учебный режим. Исследования студентов в курсах программирования фиксируют связь эпизодов путаницы с ростом понимания: кто периодически тонул в непонимании и выбирался, удерживал больше тех, кто ехал по гладкой дорожке. Свои ошибки стоит вести списком с формулировкой "какое правило языка я выучил за счёт этого стектрейса". Через неделю журнал превращается в персональный конспект, составленный не методистом, а самим языком через синяки.
Соберём механику в расписание. Первые три часа проходят без проекта и кодовой базы: интерактивная среда, арифметика, переменные, условия, циклы, простейшая функция. Правило жёсткое: каждую конструкцию не прочитать, а воспроизвести из памяти трижды в разных вариациях. С четвёртого по десятый час - письменная кампания по ядру: коллекции, функции с параметрами и возвратом, обработка ошибок, импорты, чтение и запись файла. К концу блока вы держите ядро в руках и читаете без справочника цикл по словарю.
С одиннадцатого по двадцать пятый час фокус смещается на чтение и только на чтение. Открываете один небольшой проект с живой кодовой базой, лучше консольную утилиту на пару тысяч строк, и проходите её по алгоритму из раздела про взгляд эксперта: тесты, точка входа, карта имён, линия данных, отладчик. Цель блока - наращивание чанков. Встреченные идиомы выписываете в набор карточек и разгоняете их интервалами по одной-две минуты в перерывах. Двадцать пятый час - контроль чтения: открываете новый модуль проекта и объясняете его вслух за пять минут.
Оставшиеся пятнадцать часов - режим порождения. Первые пять уходят на портирование своей маленькой утилиты. Следующие пять - небольшая собственная штука строк на двести: скрипт, бот, генератор данных, то, что лично вам нужно ежедневно. Последние пять можно перемежать с настоящим контекстом: первый рефакторинг в чужом коде, одна добавленная функция, первый pull request. На этом рубеже вы уже читаете язык не со словарём, а со справочником. Восемьдесят процентов глаголов живого языка покрываются тремястами корнями, и ядро программного языка ещё компактнее.
Сведём линию воедино. Мозг читает код тем же аппаратом, что и естественную речь, а значит осваивать язык надо как язык: сначала беглое чтение, потом порождение, с имитацией посередине. Память конечна и вмещает четыре чанка единовременно, поэтому заучивать надо только карту и ядро, остальное оставляя поиску по требованию. Скорость чтения растёт не от количества просмотренных строк, а от укрупнения единиц восприятия, которые глаз опознаёт пятнами, а не побуквенно.
Перечитывание обманывает счётчиком беглости, поэтому его заменяют припоминанием с интервалами, а автодополнение на время обучения глушится в пользу медленного письма. Трудности синтаксиса возвращаются к проектировщикам, а не к вашему интеллекту. Обратная связь бесплатна и мгновенна, а путаница при ведении журнала ошибок конвертируется в знание по фиксированному курсу.
И последнее. Язык программирования - искусственная конструкция, придуманная людьми для людей. Он заключает с вами негласный договор: не оценивать ваше достоинство, а просто выполнять прочитанное. Это соображение ставит страхи на место. Скорость чтения на третьей неделе - не приговор и не уровень, а стадия того же процесса, который прошёл любой носитель этого языка. Язык не судья. Язык - инструмент, и обязанности у инструмента ровно те, что человек сам в него заложил.
|