Представьте человека, который изучает алгоритмы. Чтобы понять как они работают, приходится разбираться в их коде и представлять, как компьютер будет его выполнять. Это странно — почему мы должны учиться думать как компьютер, вместо того, чтобы заставить его помогать нам? Какая-то сильная технозависимость.
На мой взгляд, потеть должна машина, а человек учиться, не выворачивая мозги наизнанку. Поэтому я подумал, а почему бы не визуализировать работу алгоритмов? Визуализации помогли бы не закапываться в код, а наглядно показали бы как работают алгоритмы и позволили бы понять их. Что у меня получилось — читайте в этой статье.
В результате родился проект «Объясняем код». Посмотреть, что это такое можно на code-explained.com. Код проекта выложен на Гитхаб.
Чем я вдохновлялся
Брет Виктор говорит, что творцам нужна мгновенная обратная связь с тем, что они делают. Когда вы создаете что-то или вносите изменения в свое творение, то должны сразу же видеть результат. Иначе, часть идей просто никогда не придут к вам в голову.
Например, если вы редактируете программный код, то результат должен отображаться в реальном времени, без повторной компиляции. Иначе, вы не увидите промежуточные состояния, и у вас не родятся связанные с ними идеи. Я не буду подробно пересказывать эту мысль, тем более, что на Хабре уже подробно о ней рассказали. Почитайте, если еще не успели.
Идея Брета Виктора о мгновенной обратной связи сильно меня вдохновила. Я подумал, что она могла бы изменить подход к изучению программирования, в частности алгоритмов и структур данных. Потому что сегодня этот процесс ужасно технозависим.
Как сегодня изучают алгоритмы
Преподаватель записывает на доске код, а потом по шагам объясняет, как он работает.
В видео на Youtube происходит нечто подобное — ведущий берет код алгоритма и отрисовывает этапы его работы
На ИТ-ресурсах создают анимации.
А кто-то даже пытается объяснить работу алгоритма через танец.
Почему эти подходы казались мне неэффективными
У всех перечисленных подходов есть огромный минус. Они пытаются заставить нас думать как компьютер, а не наоборот — подчинить железо нашим потребностям. Чтобы понять, как работает код алгоритма, нужно представлять, как машина будет его выполнять. Я решил, что хочу уйти от этого подхода.
Как я перешел от технозависимости к человечности
Моя идея заключалась в том, что компьютер будет выполнять и параллельно визуализировать код алгоритма. На визуализации можно будет в реальном времени видеть, как работает алгоритм и понять его суть, вместо того, чтобы выворачивать мозг наизнанку и думать как машина.
Вместе с командой мы разработали проект «Объясняем код». Это такой интерактивный учебник, который с помощью визуализаций объясняет, как работают алгоритмы.
Выглядит это как-то так. Можно вводить свои данные. А затем с помощью плеера запускать и останавливать исполнение кода, перематывать его вперед и назад. При этом визуализация в реальном времени показывает, что делает алгоритм.
Как это технически реализовано
Для кода на Python написан аналогичный код на JS, который после выполнения каждой строчки сохраняет состояние. Такой трюк помогает исполнять код на фронтенде без тяжелого интерпретатора. Чтобы убедиться, что код на JS делает то же самое, что и на Python, есть автотестер — он прогоняет код на обоих языках и сравнивает результаты.
Чтобы сэкономить на копировании данных, я применил Immutable.js. Immutable.js — это библиотека неизменяемых коллекций. Модификации таких коллекций всегда возвращают новую коллекцию, которую легко сохранить. Так я избегаю глубокого копирования. Не буду вдаваться в подробности, на Хабре про immutable.js уже писали. Для примера кусочек кода с итерированием по хеш-таблице:
while (true) { // Итерируемся по списку
this.addBP('check-not-found'); // Метод сохраняем состояние
if (this.newList.get(this.newListIdx) === null) {
// this.newList -- это немутабельный список
break;
}
this.addBP('check-found'); // Выполнена очередная строчка, сохраняем состояние
if (EQ(this.newList.get(this.newListIdx), this.number)) {
this.addBP('found-key');
return true;
}
this.fmtCollisionCount += 1; // Для динамических комментариев иногда нужно сохранять статистикуу
this.newListIdx = (this.newListIdx + 1) % this.newList.size; // Переходим к следующему индекксу
this.addBP('next-idx');
}
В любой момент пользователь может отмотать время слайдером, и мы должны быть готовы анимировать переход. Поэтому анимации сделаны декларативно на React и CSS. На React описываются нужное состояние, а CSS transitions автоматически высчитывают и анимируют переход. Это позволяет избежать совсем сложного императивного кода с запутанным состоянием и ручными вычислениями. Получается тоже не просто, и в итоге анимациями заведует огромный класс на 1000 строк. Но я уверен, что императивно реализовать то же самое у меня вообще не получилось бы.
Много строк возникает из-за особенностей браузерных API и производительности в разных браузерах. Например, большой проблемой оказалось сделать так, чтобы браузеры не склеивали последовательные изменения друг с другом. Если добавить div с определённой начальной позицией, и потом сразу же поменять координаты на конечные, то браузер склеит эти два изменения в одно. Div сразу окажется в конечной позиции без анимации. Чтобы такое не происходило, приходится вставлять задержку в два фрейма анимации с помощью window.requestAnimationFrame().
Если вам интересно подробнее про техническую реализацию, напишите об этом в комментариях. Я сделаю более подробный пост про техническую реализацию.
Что дальше?
Мы запустились недавно и пока рассказали только про хеш-таблицы. Проект растет и развивается и в нем будут появляться новые главы — в планах сортировки и другие алгоритмы. Нам очень нужна обратная связь — расскажите про ваши впечатления, что на ваш взгляд стоит изменить или доработать. Напоминаю, что проект можно посмотреть по ссылке.
stgunholy
Очень наглядно выглядит. :) Молодцы!
eleweek Автор
Спасибо! :)