Объясняем код с помощью ASCII-арта

Автор оригинала: John Regehr
  • Перевод
Примечание от переводчика: типично пятничная статья во вторник утром… почему бы и нет?



Большинство людей — визуалы. Они используют изображения, чтобы вникнуть в суть проблемы. А вот мэйнстримные языки программирования, напротив, основаны на текстовом представлении. Возникающую пропасть между текстом и графикой заполняют ASCII-изображения, нарисованные с помощью текстовых символов и вставленные в исходный код программы. Я их обожаю! Как-то раз я кинул клич в Twitter и мне прислали гораздо больше примеров, чем я ожидал. Спасибо всем участвовавшим. В этой теме попалось несколько прекрасных примеров, которые я собрал и разложил на категории. Для каждого изображения дается ссылка на соответствующий репозиторий.

Структуры данных


Одно из самых распространенных применений ASCII-арта в коде — наглядно показать структуру данных.

Первый пример из исходного кода LLVM:


Исходник

Расположение полей в структуре данных в Jikes RVM:


Исходник

Поворот дерева в Musl:


Исходник

Двусторонняя очередь в Rust:


Исходник

Внутренности компилятора Swift:


Исходник

Расположение элементов в заголовке Malloc:


Исходник

Конечные автоматы


Профилирование Javascript:


Исходник

RPC в Cloud Spanner:


Исходник

Состояния потока ввода-вывода:


Исходник

Логика предметной области


Поток управления в декомпилируемой программе NWScript:


Исходник

Внутренности ECC:


Исходник

Форматирование чисел:


Исходник

Квантовый контур:


Исходник

Балансировка задач управления памятью в ядре операционной системы:


Исходник

Совместимость типов (это особенно впечатляющий случай, потому что здесь ASCII-арт является кодом!)


Исходник

Формат файла DBF:


Исходник

Lookup-таблица для обработки изображений:


Исходник

Форма цветовой функции:


Исходник

Структура URL:


Исходник

«Краткая» справка по отмене операций в emacs:


Исходник
Примечание переводчика: по ссылке еще очень много подобных графиков

Геометрия


Контроль высоты в бортовом компьютере Apollo (!!!):


Исходник

Разбитие изображения на сегменты:


Исходник

Траектории бумеранга в Nethack:


Исходник

Отрисовка границ элемента в CSS:


Исходник

Дерево квадрантов:


Исходник

Управление скоростью работы станка:


Исходник

Скроллинг веб-страниц:


Исходник

Надеюсь, вам понравилось!

Дополнительный пример от переводчика:

График интерполяции значения:


Исходник

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

А вы документируете свой код с помощью ASCII-арта?

Поделиться публикацией

Комментарии 23

    +1
    А чем можно рисовать такие диаграммы, как в примере с компилятором swift?
      +9
      Похоже на AsciiFlow.
        +2
        sudo apt install asciio && asciio
        0

        можно накидать схему в plantUml и сгенерировать ascii код по ней.

        +2
        ` /)/)
        =(';')=
        (").(")…
          +1
          Персонально я в своих комментариях использовал
          (_*_)
          и
          ¯\_(ツ)_/¯
          Как правило это было связано с форматом респонса API.
            0
            --MEOW--/>  フ
                 |  _  _|
                 /`ミ _x 彡
                 /      |
                /  ヽ   ノ
             / ̄|   | | |
             | ( ̄ヽ__ヽ_)_)
             \二つ
              +2
              обычно другого зайца применяют:
              (\__/)
              (='.'=)
              е[:]|||||[:]3
              (")_(")
              +1
              Аж слезу пустил. Моя первая (не первая по счету, но первая сделанная на персоналке) программа была предназначена для создания из ASCII символов заставок для использования их в других программах. ДВК-2М и Pascal. Называлась S.M.I.T. Управление клавиатурой, сохранение в готовый код на Паскале.
                0
                Аналогично, но для Robotron-1715. Преобразования в исходники не было, только рисование, сохранение, загрузка.
                0
                Давно думал, как элегантно внедрять схемы в текстовую документацию, а про ASCII совсем позабыл. Спасибо!
                  +2
                  Спасибо за популяризацию интересной идеи!
                  Но вот только зачем было в качестве КДПВ ставить первое изображение? И в оригинальной статье его нет.
                  0

                  Порой и сам код тянет на ASCII-арт. Чего хоть стоит паттерн для захвата двух последовательно идущих символов в lua: "(.)(.)" (пример настоящий).

                    0
                    Файл сжать и перевести в uue. Поместить в исходный код с некоторым текстом, плагин редактора заметит этот текст, достанет файл, развернёт и покажет.
                      0
                      О, сам пользуюсь такой методикой для описания формата хранения например или какого хитровыкрученного алгоритма. Очень удобно. Правда «рисую» ручками, а не софтом.
                        +1

                        Возникла идея. Графы можно описывать с помощью graphviz, а блок-схемы — с помощью mermaid. Ведь можно сделать поддержку рендера этих объектов прям внутри IDE! Описывать графы и блок-схемы нужно будет в комментах с помощью привычных для Markdown маркеров ```dot, ```mermaid. Да и вообще можно сделать рендер и маркдауна тоже.


                        Исходники останутся "диффабельным", но при этом в IDE будут рендериться наглядные картинки.

                          +1

                          Ну, добрая четверть примеров похожа на текстовый вариант выхлопа plantuml (поверх graphviz работает). Кмк, лучший uml формат для хранения в git. И плагин к Идее есть. И локально запускается, не только онлайн.


                          И если бы мне захотелось вставлять в код uml диаграммы в комментариях, я бы делал это автогенерацией из исходника на plantuml

                          0
                          А то как же.
                          Вот иллюстрация работы класса с «устаревающим» набором данных:

                          { TSlinky }
                          
                          //           mW0__ <-data items
                          //          _0_|3      ^
                          //       ___|2         |
                          //    ___|1            |
                          //  __|0               |
                          //t <------------------+
                          
                          

                          Вспоминается документация к СМ ЭВМ и программам для ОСРВ, которая была отпечатана, судя по всему, на «барабанных» АЦПУ и там было полно таких схем, как в статье, объясняющих структуры данных, программных и аппаратных комплексов, интерфейсов…
                            +1
                            В коде Netty (javadoc'ах если быть точным) это активно используется:
                            The following diagram describes how I/O events are processed by ChannelHandlers in a ChannelPipeline typically. An I/O event is handled by either a ChannelInboundHandler or a ChannelOutboundHandler and be forwarded to its closest handler by calling the event propagation methods defined in ChannelHandlerContext, such as ChannelHandlerContext.fireChannelRead(Object) and ChannelOutboundInvoker.write(Object).
                                                                             I/O Request
                                                                        via Channel or
                                                                    ChannelHandlerContext
                                                                                  |
                              +---------------------------------------------------+---------------+
                              |                           ChannelPipeline         |               |
                              |                                                  \|/              |
                              |    +---------------------+            +-----------+----------+    |
                              |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
                              |    +----------+----------+            +-----------+----------+    |
                              |              /|\                                  |               |
                              |               |                                  \|/              |
                              |    +----------+----------+            +-----------+----------+    |
                              |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
                              |    +----------+----------+            +-----------+----------+    |
                              |              /|\                                  .               |
                              |               .                                   .               |
                              | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
                              |        [ method call]                       [method call]         |
                              |               .                                   .               |
                              |               .                                  \|/              |
                              |    +----------+----------+            +-----------+----------+    |
                              |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
                              |    +----------+----------+            +-----------+----------+    |
                              |              /|\                                  |               |
                              |               |                                  \|/              |
                              |    +----------+----------+            +-----------+----------+    |
                              |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
                              |    +----------+----------+            +-----------+----------+    |
                              |              /|\                                  |               |
                              +---------------+-----------------------------------+---------------+
                                              |                                  \|/
                              +---------------+-----------------------------------+---------------+
                              |               |                                   |               |
                              |       [ Socket.read() ]                    [ Socket.write() ]     |
                              |                                                                   |
                              |  Netty Internal I/O Threads (Transport Implementation)            |
                              +-------------------------------------------------------------------+
                            
                              +1
                              У нас есть такая штуковина для определения видимости контролов =)
                              image
                                0
                                Да, было дело.
                                image

                                Хотя лично мне кажется, что некоторые из представленных в посте «комментариев» тянут на страницу в нормальной документации. ASCII-арт комментарий должен помочь быстро понять суть, если её можно передать простой картинкой, но не более. Имхо, конечно.
                                  +1
                                  В тестах RxJS используются не только для документирования, но и для входных и ожидаемых данных (событий в потоках).

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

                                  Самое читаемое