Табы или пробелы для отступов? Статистика по 3.8 миллионам Perl файлов созданных за 24 года

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

Иногда выбора нет. Например, в Makefile обязательно нужно использовать табы. В языке программирования go существует официальная утилита gofmt которая форматирует код и эта утилита использует табы для отступов. B эзотерическом языке программирования Whitespace табы и пробелы нельзя заменять друг другом. Но многие языки программирования не навязывают выбор, а позволяют программисту самостоятельно решить какие символы использовать.

Существует достаточно популярное мнение какие символы использовать для отступов. Мнение следующие: не важно что использовать, главное консистентность. Если используешь табы, то нужно всегда их использовать. Если используешь пробелы, то нужно использовать только пробелы и не использовать табы.

Есть даже шуточный шуточный комикс на эту тему:

(два человека совершенно несогласны друг с другом нужно ли использовать табы или пробелы, но абсолютно согласны что нужно использовать только что-то одно):

А как обстоят дела в реальном мире? Что на самом деле используется?

Это достаточно просто выяснить. Нужно взять исходные коды программ, посчитать какие символы используются и посмотреть на результаты.

Этот статья — это результат маленького исследования про использование табов и пробелов в мире языка программирования Perl. Есть огромный репозиторий в котором хранятся Perl библиотеки — CPAN. Я загрузил все версии всех библиотек которые сейчас есть на CPAN (их оказалось около 135 тысяч) и посчитал какие символы используется для отступов.

Перед тем как читать дальше, я предлагаю вам на минуту задуматься и попробовать предположить что популярнее для отступов:

  • табы
  • пробелы
  • или смесь табов и пробелов

?

Написание кода

Итак, задача понятна. Нужно взять все библиотеки с CPAN и проверить что используется для отступов.

Для начала нужно скачать весь CPAN. Это делается с помощью одной команды:

time /usr/bin/rsync -av --delete cpan-rsync.perl.org::CPAN /project/CPAN/

3 часа и CPAN скачен. Он занимает около 27 GB.

CPAN это набор файлов которые организованы в определенную структуру. Вот фрагмент:

CPAN/authors/id
├── A
│   ├── AA
│   │   ├── AADLER
│   │   │   ├── CHECKSUMS
│   │   │   ├── Games-LogicPuzzle-0.10.readme
│   │   │   ├── Games-LogicPuzzle-0.10.tar.gz
│   │   │   ├── Games-LogicPuzzle-0.12.readme
│   │   │   ├── Games-LogicPuzzle-0.12.tar.gz

В данном примере AADLER — это логин автора, а Games-LogicPuzzle-0.10.tar.gz и Games-LogicPuzzle-0.12.tar.gz — это релизы.

Сейчас на CPAN есть более 7 тысяч авторов которые загрузили библиотеки на CPAN. Чтобы не хранить все 7 тысяч папок в одной папке, добавлено еще несколько уровней (система контроля версий git хранит свои данные похожим образом).

На CPAN можно загружать библиотеки, которые запакованы разными архиваторами.

Я начал с того что посчитал количество разных расширений файлов в папке CPAN/authors/id/. Вот скрипт и результат его работы . Топ расширений архивов:

  • .tar.gz 135571
  • .tgz 903
  • .zip 652
  • .gz 612
  • .bz2 243

.tar.gz побеждает с таким отрывом, что я решил что достаточно будет посчитать какие символы используются в отступах только в библиотеках, которые запакованы в .tar.gz

Дальше я написал несколько скриптов. Изначально мне не до конца было понятно в каком виде я хочу получить данные о табах и пробелах, поэтому я решил сделать систему состоящую из нескольких компонентов. Сначала предварительно обработать все 135 тысяч файлов с релизами и положить данные о табах и пробелах в базу данных. Ожидаю что это будет долго. А дальше использовать данные из базы данных для того чтобы быстро получать данные в разных форматах.

В итоге получился скрипт fill_db . Этот скрипт заливал данные в базу чуть больше пяти часов. Но эти пять часов это когда уже все было отлажено. Далеко не с первого раза скрипт отработал. Основные проблемы были с Unicode. Сначала была проблема с релизом μ-0.01.tar.gz автора APEIRON, потом были проблемы с файлами вида t/words_with_ß.dat из релиза Lingua-DE-ASCII-0.06 автора BIGJ. Но в конце концов все проблемы были решены и скрипт успешно прошелся по всем .tar.gz релизам.

Скрипт идет по всем .tar.gz файлам в CPAN. Распаковывает .tar.gz во временную папку. Находит в этой временной папке все файлы у которых расширения .pm, .pl, .t или .pod, читает все отступы и проверяет если в этих отступах пробелы и или табы. В релизах бывают и другие файлы, но я решил ограничиться только файлами которые явно относятся к Perl.

Результат работы этого скрипта — это 2 таблицы в базе данных. Вот пример данных:

mysql> select * from releases limit 1;
+------------+--------+---------------------------------------------------------------+------------+
| release_id | author | file_name                                                     | timestamp  |
+------------+--------+---------------------------------------------------------------+------------+
|          1 | RUFF   | /cpan/authors/id/R/RU/RUFF/DJabberd-Authen-Dovecot-0.1.tar.gz | 1359325895 |
+------------+--------+---------------------------------------------------------------+------------+
1 row in set (0.00 sec)

mysql> select * from files where release_id = 1;
+---------+------------+--------------------------------------------------------+------+---------------------+-------------------+
| file_id | release_id | file_name                                              | size | has_space_beginning | has_tab_beginning |
+---------+------------+--------------------------------------------------------+------+---------------------+-------------------+
|       1 |          1 | DJabberd-Authen-Dovecot/lib/DJabberd/Authen/Dovecot.pm | 2047 |                   1 |                 1 |
|       2 |          1 | DJabberd-Authen-Dovecot/t/compiles.t                   |   64 |                   0 |                 0 |
+---------+------------+--------------------------------------------------------+------+---------------------+-------------------+
2 rows in set (0.02 sec)

mysql> mysql> selec(*) from releases;
+----------+
| count(*) |
+----------+
|   135343 |
+----------+
1 row in set (0.04 sec)

mysql> select count(*) from files;
+----------+
| count(*) |
+----------+
|  3828079 |
+----------+
1 row in set (5.71 sec)

Только пробелы, только табы, табы и пробелы, и...

Итого в базе данных про каждый файл в релизе есть 2 флага:

  • используются ли пробелы в отступах
  • используются ли табы в отступах

Соответственно из двух флагов может быть 4 комбинации:

  • 11 — используются и пробелы и табы
  • 10 — используются только пробелы
  • 01 — используются только табы
  • 00 — не используются ни пробелы, ни табы

Первые три варианта — это совершенно ожидаемые ситуации, именно их я и хотел найти и узнать что популярнее. А вот вариант 00 — "не используются ни табы, ни пробелы" — это то о чем я совершенно не думал, но оказалось что так тоже бывает. "Как?" — спросите вы. Вот пример.

mysql> select releases.release_id, files.file_name, files.size, has_space_beginning, has_tab_beginning from releases join files on releases.release_id = files.release_id and author = 'KOHA';
+------------+---------------------------------------------------+------+---------------------+-------------------+
| release_id | file_name                                         | size | has_space_beginning | has_tab_beginning |
+------------+---------------------------------------------------+------+---------------------+-------------------+
|     118147 | Bundle-KohaSupport-0.31/lib/Bundle/KohaSupport.pm | 2169 |                   0 |                 0 |
|     118147 | Bundle-KohaSupport-0.31/t/Bundle-KohaSupport.t    |  487 |                   0 |                 0 |
|     118147 | Bundle-KohaSupport-0.31/t/pod.t                   |  130 |                   0 |                 0 |
+------------+---------------------------------------------------+------+---------------------+-------------------+
3 rows in set (0.05 sec)

У автора KOHA есть релиз Bundle-KohaSupport-0.31. В этом релизе есть 3 файла у которых расширения из списка .pm, .pl, .t или .pod. Про все эти файлы в базе написано что в их отступах нет ни пробелов, ни табов. Как так может быть?

Оказывается, все элементарно. Если если посмотреть на эти файлы, то в них просто напросто нет отступов. Вот, например, содержимое файла t/Bundle-KohaSupport.t:

# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl Bundle-KohaSupport.t'

#########################

# change 'tests => 1' to 'tests => last_test_to_print';

use Test::More tests => 1;
BEGIN { use_ok('Bundle::KohaSupport') };

#########################

# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.

Так что кроме трех совершенно ожидаемых ситуаций:

  • используются только пробелы
  • используются только табы
  • используются и пробелы и табы

еще и бывает ситуация:

  • не используются ни пробелы и не используются табы

Данные по авторам

После того как у меня появились обработанные данные в базе я решил посмотреть по каждому автору что он использует для отступов.

Я ожидал что самым популярным будет использование только пробелов, на втором месте по популярности будет использование только табов, а на третьем месте по популярности будет одновременное использование табов и пробелов.

Но оказалось что я был совершенно неправ.

Я написал скрипт . Этот скрипт проверил какие символы используются авторами в во всех файлах .pm, .pl, .t, .pod, которые есть во всех их релизах которые сейчас есть на CPAN.

Вот что получилось:

$ cat app/data/users.log | perl -nalE 'say if /^##/'
## 00 (nothing) - 50 (0.7%)
## 01 (only tabs) - 51 (0.7%)
## 10 (only spaces) - 1543 (21.9%)
## 11 (both) - 5410 (76.7%)

Данные совершенно не такие как я ожидал!

  • Больше 75% авторов используют смесь пробелов и табов для отступов.
  • Только пробелы на втором месте, чуть больше 20%,
  • а авторов которые используют только табы меньше процента.
  • Количество авторов, которые вообще не используют отступы практически такое же как количество авторов которые используют только табы.

Полный список всех авторов в разбивкой про группам есть в файле на GitHub .

А вот jupyter notebook  с помощью которого была построена эта круговая диаграмма.

Но это данные сформированные по всем релизам, которые сейчас есть на CPAN. Эти релизы создавались на протяжении последних 24 лет. Может быть быть с течением времени соотношение как-то меняется?

Данные по времени

У каждого файла с релизом на CPAN время модификации — это время когда этот релиз был загружен на CPAN. Эти данные загружены в базу данных. Сейчас на CPAN самый старый релиз — это Ioctl-0.5 — он был загружен в на CPAN 1995-08-20:

mysql> select author, file_name, from_unixtime(timestamp) from releases where timestamp = (select min(timestamp) from releases);
+--------+----------------------------------------------+--------------------------+
| author | file_name                                    | from_unixtime(timestamp) |
+--------+----------------------------------------------+--------------------------+
| KJALB  | /cpan/authors/id/K/KJ/KJALB/Ioctl-0.5.tar.gz | 1995-08-20 07:26:09      |
+--------+----------------------------------------------+--------------------------+
1 row in set (0.08 sec)

Причем в этом день было залито сразу 8 релизов:

mysql> select * from releases where from_unixtime(timestamp) < '1995-08-21' order by timestamp;
+------------+--------+--------------------------------------------------------------+-----------+
| release_id | author | file_name                                                    | timestamp |
+------------+--------+--------------------------------------------------------------+-----------+
|     112505 | KJALB  | /cpan/authors/id/K/KJ/KJALB/Ioctl-0.5.tar.gz                 | 808903569 |
|      23026 | TYEMQ  | /cpan/authors/id/T/TY/TYEMQ/FileKGlob.tar.gz                 | 808903636 |
|     134031 | WPS    | /cpan/authors/id/W/WP/WPS/Curses-a8.tar.gz                   | 808903647 |
|     112546 | KJALB  | /cpan/authors/id/K/KJ/KJALB/Term-Info-1.0.tar.gz             | 808903748 |
|      70278 | MICB   | /cpan/authors/id/M/MI/MICB/TclTk-b1.tar.gz                   | 808910379 |
|      70274 | MICB   | /cpan/authors/id/M/MI/MICB/Tcl-b1.tar.gz                     | 808910514 |
|      19408 | GBOSS  | /cpan/authors/id/G/GB/GBOSS/perl_archie.1.5.tar.gz           | 808930091 |
|      81551 | JKAST  | /cpan/authors/id/J/JK/JKAST/StatisticsDescriptive-1.1.tar.gz | 808950837 |
+------------+--------+--------------------------------------------------------------+-----------+
8 rows in set (0.06 sec)

Я решил посмотреть как меняется распределение использование разных символов для отступов по времени. Для этого я написал скрипт .

Вот фрагмент файлы с данными, который создал скрипт:

$ cat app/data/releases_date.csv | head
date,00,01,10,11
1995-08-20,0,1,0,7
1995-08-21,0,0,0,0
1995-08-22,0,0,0,0
1995-08-23,0,0,0,0
1995-08-24,0,0,0,1
1995-08-25,0,0,0,0
1995-08-26,0,0,0,0
1995-08-27,0,0,0,0
1995-08-28,0,0,0,0

Т.е. про каждую дату начиная с 1995-08-20 есть данные о том сколько было релизов с разбивкой по тому какие символы использовались для отступов.

  • 00 — в отступах нет ни пробелов, ни табов
  • 01 — в отступах используются только табы
  • 10 — в отступах используются только пробелы
  • 11 — в отсутпах используются и табы и пробелы

Дальше я написал jupyter notebook  в котором нарисовал график. На графике я отображаю не абсолютное количество релизов с разбивкой по типу отступов, а процент от общего количества релизов в этот день:

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

Удивительно но прослеживается тренд. Количество релизов в которых используются только табы или вообще не используются отступы практически не меняется, но доля релизов в которых используются только пробелы постоянно растет и этот рост происходит за счет доли релизов в которых используются смесь табов и пробелов.

Почему растет "только пробелы". Гипотеза номер 1

Я посмотрел на данные и у меня возникла одна гипотеза почему уменьшается количество релизов в которых используется и проблемы и табы. Моя мысль про Perl библиотеку Module::Install . Если при написании своей библиотеки используется Module::Install, то в релизе на CPAN включаются файлы от этой библиотеки. А в этих файлах используется смесь пробелов и табов. Вот пример файлов от Module::Install в релизе Devel-PeekPoke-0.04:

mysql> select * from files where release_id = 284 and file_name like '%inc/Module/Install%';
+---------+------------+----------------------------------------------------+-------+---------------------+-------------------+
| file_id | release_id | file_name                                          | size  | has_space_beginning | has_tab_beginning |
+---------+------------+----------------------------------------------------+-------+---------------------+-------------------+
|   10328 |        284 | Devel-PeekPoke-0.04/inc/Module/Install.pm          | 12381 |                   1 |                 1 |
|   10329 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Metadata.pm | 18111 |                   1 |                 1 |
|   10330 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Fetch.pm    |  2455 |                   1 |                 1 |
|   10331 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Makefile.pm | 12063 |                   1 |                 1 |
|   10332 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Base.pm     |  1127 |                   0 |                 1 |
|   10333 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/WriteAll.pm |  1278 |                   0 |                 1 |
|   10334 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Win32.pm    |  1795 |                   1 |                 1 |
|   10335 |        284 | Devel-PeekPoke-0.04/inc/Module/Install/Can.pm      |  3183 |                   1 |                 1 |
+---------+------------+----------------------------------------------------+-------+---------------------+-------------------+
8 rows in set (0.03 sec)

Моя гипотеза заключается в том что разработчики используют пробелы для отступов, но из-за того что в релизе находится Module::Install то в статистике учитываются и пробелы и табы. Module::Install стали меньше использовать (так как появились всякие Dist::Zilla, Dist::Milla, Minilla) и поэтому Module::Install перестал давать искажения.

Эту гипотезу нужно проверить. Сначала я решил посмотреть, действительно ли Module::Install используются все меньше и меньше. Я построил график. Каждая точка — это количество релизов за месяц в которых использовался Module::Install. Видно что часть гипотезы верна — действительно, Module::Install стали использовать меньше.

Но действительно ли использование Module::Install так влияет на долю использования пробелов или табов и пробелов для отступов. Для того чтобы это выяснить, я нарисовал еще два графика. Это количество разных типов отступов в релизах по месяцам. На первом графике только релизы в которых используется Module::Install, на втором графике только релизы в которых он не используется.

Тут видно что действительно, если используется библиотека Module::Install, то чаще всего в библиотеке используется именно смесь табов и пробелов.

А вот график на которых отображаются только те релизы в которых не используется Module::Install. Если сравнивать этот график с графиком на котором обращаются все релизы, то разница есть, но ничего кардинально не меняется.

Получается что гипотеза неверна. Если в релизе используется Module::Install, то релиз чаще всего попадает в группу "табы и пробелы", но если не учитывать все релизы в которых используется Module::Install, то все равно есть тренд — доля релизов в которых используются только табы в качестве отступов растет за счет доли релизов в которой используются смесь табов и пробелов.

Почему растет "только пробелы". Гипотеза номер 2

Почему же все таки растет количество релизов в которых используются только табы? Может быть есть какой-то сверх активный автор, которые выпускают очень много релизов и эти автор так влияют на всю статистику?

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

Почему растет "только пробелы". Гипотеза номер 3

По графикам видно что с течением времени становится все больше релизов в которых используются только пробелы для отступов. И эта доля растет за счет релизов в которых используется смесь пробелов и табов.

Мое первое предположение было что так происходит из-за того что в релизы раньше активно включали код библиотеки Module::Install в которых использовалась смесь пробелов и табов, эту библиотеку используют все меньше, и поэтому доля релизов в которых используется смесь табов и пробелов уменьшается. Оказалось что часть правды в этом есть, но даже если убрать из рассмотрения все релизы в которых используется Module::Install, общий тренд это не меняет — все равно доля релизов в которых только пробелы растет за счет доли релизов в которых используется смесь пробелов и табов.

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

Моя третья гипотеза заключается в том что у авторов появляются более удобные текстовые редакторы и IDE, благодаря которым становится проще использовать консистентно только пробелы, а не смесь пробелов и табов. Но, к сожалению, идей как проверить эту гипотезу у меня нет. В данных которые лежат на CPAN нет информации о том какой редактор был использован при создании этого релиза. Я посмотрел на даты релизов популярных редакторов/IDE:

  • Emacs — 1985
  • vim — 1991
  • IntelliJ IDEA — январяь 2001
  • Eclipse — ноябрь 2001
  • Sublime Text — январь 2008
  • Atom — февраль 2014
  • VS Code — апрель 2015

Данные по авторам за 2019 год

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

Данные из результатов работы скрипта :

$ cat app/data/users_2019.log | perl -nalE 'say if /^##/'
## 00 (nothing) - 12 (1.4%)
## 01 (only tabs) - 9 (1.0%)
## 10 (only spaces) - 355 (41.2%)
## 11 (both) - 486 (56.4%)

Если сравнивать данные по авторам за 2019 год и данные за все года, то видно что процент авторов которые использует только табы практически не меняется, но доля авторов которые используют только пробелы сильно увеличилась.

Исходники для этой круговой диаграммы:

Факторы, влияющие на корректность данные

Для формирования чисел и графиков были использованы все .tar.gz релизы, которые были на CPAN на момент начала написания этой статьи, кроме релизов самого языка программирования Perl.

CPAN позволяет удалять релизы, в данных которые показаны в этой статье удаленные релизы не участвовали. Непонятно насколько сильно поменяются данные если учитывать символы отступов в уже удаленных релизах. Вполне возможно что данные поменяются сильно. Существует архив backpan  который хранит все релизы, которые когда либо были на CPAN. Так что в теории есть возможность пересчитать все числа с учетом релизов которых уже не на CPAN.

Второй момент, который влияет на точность данных — это то что учитывались символы отступов только в релизах, которые были запакованы в .tar.gz архив. Другие типы архивов не использовались. Подавляющее большинство релизов — это .tar.gz, поэтому было сделано такое допущение. Если пересчитать данные по всем архивам то данные наверняка изменятся. Предполагаю что изменение будет не больше нескольких процентов.

Исходный код

Весь набор скриптов, которые были использованы для сбора данных, сами данные и jupyter ноутбуки все доступно в репозитории на GitHub.

Код который написан — он прямо очень далек от совершенства. Все что написано было написано с идей как можно быстрее получить результат, а не создать совершенный код.

Резюме

На момент написания этого текста в репозитории Perl библиотек CPAN находилось около 135 тысяч релизов. Первый релиз был сделан 24 года назад (1995-08-20). В этих релизах находится почти 4 миллиона файлов с расширениями .pm, .pl, .t или .pod.

Если учитывать данные за все время, то получится что 76.7%% авторов в отступах используют смесь пробелов и табов, 21.9% используют в отступах только пробелы, а 0.7% — только табы.

Но если учитывать данные только за 2019 года, то становится все больше авторов кто использует только пробелы для отступов, но все равно большинство использует смесь табов и пробелов (56.4% — используют и табы и пробелы,пробелы 41.2% — только пробелы, 1.0% — только табы).

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

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

Другие статьи

Комментарии