{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Блоги: заметки с тегом 99",
    "_rss_description": "Автоматически собираемая лента заметок, написанных в блогах на Эгее",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": false,
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/blogengine.ru\/blogs\/tags\/99\/",
    "feed_url": "https:\/\/blogengine.ru\/blogs\/tags\/99\/json\/",
    "icon": false,
    "authors": [
        {
            "name": "Илья Бирман",
            "url": "https:\/\/blogengine.ru\/blogs\/",
            "avatar": false
        }
    ],
    "items": [
        {
            "id": "137041",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-akrscript\/",
            "title": "99 бутылок: AkRScript",
            "content_html": "<p>В полку странных языков программирования, на которых я писал «песню о пиве», прибыло.<\/p>\n<p><b>84. AkRScript<\/b> — самодельный скриптовый язык программирования, написанный в середине десятых годов владельцем телеграм-канала «<a href=\"https:\/\/t.me\/sapporolife\">Notes from the Dark Side of the Moon<\/a>», когда он ещё был школьником.<\/p>\n<p>Он его оживил в эмуляторе и <a href=\"https:\/\/www.genjit.su\/scpt\/\">выложил<\/a> несколько программ, написанных на <i>AkRScript<\/i>, что и позволило мне разобраться в том как этот язык устроен.<\/p>\n<p>Синтаксис, как это часто бывает у самодельных языков, совершенно вырвиглазный. Зато есть все основные примитивные типы данных — строка, число, массив, хеш и даже лямбды. Программирование процедурное, функции возможны только в виде лямбд. У меня в коде тоже есть одна такая, её легко узнать — они всегда начинаются с тильды и заканчиваются обратным апострофом; параметры передаются подстановками <tt>$A1<\/tt>, <tt>$A2<\/tt> и так далее.<\/p>\n<p>Процедуры можно описывать двумя способами — через ключевое слово <tt>subp<\/tt> и при помощи <tt>subp opcode<\/tt>. Отличия существенные — обычную процедуру приходится вызывать через оператор <tt>csp<\/tt>, а опкод вызывается как оператор. Кроме того, в обычную процедуру параметры передаются через глобальные переменные, тогда как опкод может получать их через стек, снимая с него значения оператором <tt>pop<\/tt>.<\/p>\n<p><i>Дополнено<\/i>: автор написал, что процедуры, вызываемые через <tt>csp<\/tt> тоже, вроде бы, могут забирать параметры через <tt>pop<\/tt>. Звучит логично, но проверять это я уже не буду.<\/p>\n<p>Параметры передаются через пробел, включая выражения, которые должны обрамляться обратными апострофами. Кое-где слова я не брал в кавычки, воспользовавшись тем, что вместо несуществующих переменных подставляется их имя.<\/p>\n<p>С пробелами у языка сложные отношения — в параметрах они могут срезаться даже внутри строк, поэтому, чтобы не мучиться с ними, я завёл себе переменную <tt>sp<\/tt>, куда поместил неразрывный пробел и расставлял его везде, где сталкивался с трудностями.<\/p>\n<pre class=\"e2-text-code\"><code class=\"sql\">-- &quot;99 Bottles of Beer,&quot; written in AkrScript on August 9, 2025\n-- by Evgeny Stepanishchev (https:\/\/bolknote.ru)\n\nclrs\n\n-- тут в скобках стоит неразрывный пробел (код 0xA0)\nassign sp &quot; &quot;\n\nsubp opcode plural\nassign _s s\nassign _b `pop`\n\nchk `cmp b 1` ~assign _s &quot;&quot;`\ncno b ~assign _b No`\nassign out `add _b sp bottle _s sp &quot;of beer&quot;`\n\nmkill &quot;_s&quot;\nmkill &quot;_b&quot;\nret\n\nassign b 99\n\nplural b\n\nwhi `gt b 0`\n\nprnt out sp &quot;on the wall, &quot; out &quot;!&quot;\nprnt &quot;Take one down, pass it around,&quot;\n\nassign b `sub b 1`\n\nplural b\n\nprnt out sp &quot;on the wall!&quot;\nprnt\nret\n\nprnt &quot;No more bottles of beer on the wall, no more bottles of beer.&quot;\nprnt &quot;Go to the store and buy some more, 99 bottles of beer on the wall.&quot;<\/code><\/pre><div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2025.09.09@2x.webp\" width=\"1000\" height=\"530\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2025-08-09T23:18:28+05:00",
            "date_modified": "2025-08-10T14:39:40+05:00",
            "tags": [
                "99"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sat, 09 Aug 2025 23:18:28 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "137041",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "136588",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-holyc\/",
            "title": "99 бутылок: HolyC",
            "content_html": "<p>История появления языка, на котором написана сегодняшняя «песня о пиве», довольно занятна.<\/p>\n<p>Его автор, Терри Дэвис — гениальный одиночка и бывший инженер компании <i>Ticketmaster<\/i>, который посвятил более десяти лет созданию <a href=\"https:\/\/habr.com\/ru\/companies\/x-com\/articles\/904018\/\">собственной операционной системы <i>TempleOS<\/i><\/a>. Страдая от шизофрении, он считал, что действует по воле Бога, который лично поручил ему создать «божественную ОС». Несмотря на тяжёлое психическое состояние, Дэвис самостоятельно написал компилятор, ядро, графическую оболочку и язык программирования — в полном одиночестве и с поразительной продуктивностью.<\/p>\n<p><b>83. HolyC<\/b> — тот самый язык, разработанный Терри. Выглядит он довольно интересно. Сильно напоминает Си, но имеет много интересных особенностей, некоторые из которых сразу бросаются в глаза, даже в моей небольшой программе.<\/p>\n<p>Во-первых, конечно, это собственная система типов, простая и понятная — значение либо выводится (<tt>auto<\/tt>), либо сразу видно сколько бит оно занимает, а так же знаковое оно или нет — <tt>U8<\/tt>, <tt>I8<\/tt>, <tt>U16<\/tt> и так далее. Тип <tt>U0<\/tt>, как легко догадаться — аналог <tt>void<\/tt>.<\/p>\n<p>Во-вторых, упрощённый вывод на экран — всё отдельностоящие строки сразу выводятся, всё остальное можно вывести через операцию «запятая», указав формат и значения — одно или несколько.<\/p>\n<p>Чуть менее заметная деталь — оператор <tt>case<\/tt> может не иметь аргумента, в таком случае используется автоикрементное значение, начинающееся с нуля. В <i>HolyC<\/i> вообще довольно много интересного синтаксического сахара.<\/p>\n<p>Поражает, как много может создать в одиночку человек, заражённый какой-то идеей. Работы проделано довольно много, причём, работы проделанной очень хорошо. Я не смотрел внутренности <i>TempleOS<\/i>, но язык <i>HolyC<\/i> выглядит как нечто цельное и продуманное. Некоторые конструктивные особенности я был бы рад видеть и в современном Си, например, значения по-умолчанию для параметров функций.<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">\/\/ &quot;99 Bottles of Beer,&quot; written in HolyC on July 9, 2025\n\/\/ by Evgeny Stepanishchev (https:\/\/bolknote.ru)\n\nauto Bottle(U8 b)\n{\n  U8 *bottles, *ret;\n\n  switch (b) {\n    case:\n      bottles = StrNew(&quot;No bottles&quot;);\n      break;\n    case:\n      bottles = StrNew(&quot;1 bottle&quot;);\n      break;\n    default:\n      bottles = StrPrint(NULL, &quot;%d bottles&quot;, b);\n      break;\n  }\n\n  ret = StrMerge(bottles, &quot; of beer&quot;);\n  Free(bottles);\n  return ret;\n}\n\nU0 Main()\n{\n  U8 *bottles, b = 99;\n\n  while (b) {\n    bottles = Bottle(b);\n    &quot;%s on the wall, %1$s!\\n&quot;, bottles;\n    Free(bottles);\n\n    bottles = Bottle(--b);\n    &quot;Take one down, pass it around,\\n&quot;;\n    &quot;%s on the wall!\\n\\n&quot;, bottles;\n    Free(bottles);\n  }\n\n  &quot;No more bottles of beer on the wall,\\n&quot;;\n  &quot;No more bottles of beer!\\n&quot;;\n  &quot;Go to the store and buy some more,\\n&quot;;\n  &quot;99 bottles of beer on the wall!\\n&quot;;\n}<\/code><\/pre>",
            "date_published": "2025-07-09T21:19:57+05:00",
            "date_modified": "2025-07-09T21:19:29+05:00",
            "tags": [
                "99",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 09 Jul 2025 21:19:57 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "136588",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "135468",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-vhodnoy-yazyk-pevm-iskra-1256\/",
            "title": "99 бутылок: Входной язык ПЭВМ Искра-1256",
            "content_html": "<p>Сегодня у меня рубрике «99 бутылок» язык настолько редкий, что у него даже отдельного названия нет. Иногда его называют «русскоязычным Бейсиком Искры-1256», но это вряд ли верно — на какой-либо Бейсик он едва ли похож. Там где на нём много программировали, конечно, выдумывали какие-то локальные названия — например, «<a href=\"https:\/\/robotlandia.ru\/demo\/js\/0312.htm#:~:text=Дуб\">Дуб<\/a>» за дубовость синтаксиса или «<a href=\"https:\/\/cyber-museum.ru\/iskra1256\/#:~:text=Кузик\">Кузик<\/a>».<\/p>\n<p>В редкой профессиональной литераторе по теме его называют «входной язык…» или «символьный язык ПЭВМ „Искра-1256“».<\/p>\n<p><b>83. Входной язык ПЭВМ «Искра-1256»<\/b> — язык программирования с операторами на русском языке, который использовался на отечественных портативных (по тем временам) ЭВМ «Искра-1256», эта машина выпускалась на заводе «Счётмаш» в Курске с 1981 по 1989 год.<\/p>\n<p>Машина работала с алфавитно-цифровым дисплеем, загружала программы с кассетного магнитофона и выпускалась в нескольких модификациях — с оперативной памятью от 4 до 64 килобайт.<\/p>\n<p>С языком программирования пришлось разбираться по листингу сохранившихся программ, никаких следов его описания к сожалению, не обнаружилось. Впрочем, если прочитать все нашедшиеся программы, основное понять можно, сложного там ничего нет.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2025.04.07@2x.webp\" width=\"1000\" height=\"562\" alt=\"\" \/>\n<\/div>\n<p>Строки нумеруются подряд, видимо в этом кто-то углядел сходство с Бейсиком, но дальше идут сплошные различия.<\/p>\n<p>Номер строки тут, кажется, используется только для удобства набора текста программы, переход же в языке делается не на номер строки, а на метку, причём их минимум два вида — метка для передачи управления, начинающаяся с буквы «М» (например — <tt>M01<\/tt>), и метка подпрограммы, которая начинается с «П» (<tt>П01<\/tt>).<\/p>\n<p>Операторы записываются русскими буквами. Например <tt>ПЕРЕХ П01<\/tt> —  вызов подпрограммы, перед которой стоит метка <tt>П01<\/tt>.  Оператор перехода часто используется с условным оператором, у которого нет какого-то явно обозначенного начала. Это просто сравнение с переходом, причём оператор перехода записывается впритык, без пробела: <tt>И12=0ПЕРЕХ П01<\/tt>.<\/p>\n<p>Переменные так же записываются русскими буквами, их буквенные префиксы значимы. Со всеми префиксами разобраться не удалось, но некоторые я понял: например, буквами «И» и «А» обозначаются переменные, содержащие числа. Разница, кажется, в том, что на вторые каким-то образом отображается массивы, которые имею префикс «АИ». Помимо того есть префиксы «СА» и «СИ» — так обозначаются символьные переменные, второе, думаю, тоже массив.<\/p>\n<p>Необычно выглядит присваивание — оно тут записывается как <tt>=&gt;<\/tt> и присваивает левое значение правому операнду.<\/p>\n<p>Но самое странное — это функции.<\/p>\n<p>Во-первых, тут они почему-то записываются английскими буквами. Во-вторых, могут стоять после аргумента. В-третьих, тут есть функции, которым, прямо как в Перле, можно присваивать значение: <tt>СИ01=>STR(СА00, И02, 1)<\/tt>. В-четвёртых, и это самое, наверное, странное, их можно записывать подряд без пробела: <tt>(1.234567↑2+8.76543-0.551122)ABSINTLNEXP<\/tt>.<\/p>\n<p>Вот какая программа получилась у меня после попытки освоить этот язык. Не факт, что она написана без ошибок, но попробовать, как вы догадываетесь, негде:<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">1 ПЕЧАТЬ(HEX(03),,)\n2 ЦИКЛ(И01,0,98)\n3 И01-99=&gt;A01\n4 ПЕРЕХ П02\n5 ПЕЧАТЬ(&quot; ON THE WALL, &quot;,,)\n6 ПЕРЕХ П02\n7 ПЕЧАТЬ(&quot;!&quot;,HEX(0A0D),&quot;TAKE ONE DOWN, PASS IT AROUND,&quot;,)\n8 А01-1=&gt;А01\n9 ПЕРЕХ П02\n10 ПЕЧАТЬ(&quot; ON THE WALL!&quot;,)\n11 ЦИКЛ*\n12 ПЕЧАТЬ(&quot;NO MORE BOTTLES OF BEER ON THE WALL,&quot;,)\n13 ПЕЧАТЬ(&quot;NO MORE BOTTLES OF BEER!&quot;,)\n14 ПЕЧАТЬ(&quot;GO TO THE STORE AND BUY SOME MORE,&quot;,)\n15 ПЕЧАТЬ(&quot;99 ON THE WALL!&quot;,)\n16 П01\n17 &quot;S&quot;=&gt;СА01\n18 А01=1ПЕРЕХ М01\n19 &quot;&quot;=&gt;СА01\n20 М01\n21 П02\n22 A01=0ПЕРЕХ М02\n23 ПЕЧАТЬ(А01(2.0),,)\n24 ПЕРЕХ М03\n25 М02\n26 ПЕЧАТЬ(&quot;NO&quot;,,)\n27 М03\n28 ПЕРЕХ П01\n29 ПЕЧАТЬ(&quot; BOTTLE&quot;,СА01,&quot; OF BEER&quot;,,)<\/code><\/pre><p>Разобраться в языке мне помогли следующие книги, <a href=\"https:\/\/retro-computer.ru\/Lists\/ItemList\/DispForm.aspx?ID=359&RootFolder=%2A\">найденные<\/a> <a href=\"http:\/\/oldpc.su\/lib\/magaz\/vtiep\/\">в<\/a> <a href=\"http:\/\/www.leningrad.su\/jj\/2012\/iskra1256books.rar\">интернете<\/a>. В них нашлось какое-то количество программ, по которому удалось понять что из себя представлял этот язык:<\/p>\n<ul>\n<li>«Комплекс вычислительный „Искра 1256“. Комплект контрольных тест-программ. 1.320.118 Д15»;<\/li>\n<li>«Процессор интерпретирующий „Искра-1256“. Библиотека программ. 3.050.187 Д14»;<\/li>\n<li>«Задачник по аналитической химии», Н. Ф. Клещев, Е. А. Алферов и другие, М., «Химия», 1993;<\/li>\n<li>Журнал «Вычислительная техника и её применение», №3, 1988.<\/li>\n<\/ul>\n<p>Самым большим тёмным пятном остался оператор <tt>СЕЛЕКТ()<\/tt>. Без чёткого описания понять его функцию мне кажется невозможным. Всё, что я смог о нём узнать, заключено в цитате, найденной <a href=\"https:\/\/vk.com\/wall-38178546_31679?w=wall-38178546_31679_r31685\">в одном из обсуждений<\/a>: «там половину языка составлял оператор СЕЛЕКТ(<i>n<\/i>, параметры). В зависимости от этого <i>n<\/i> он чего только не делал».<\/p>\n<p><i>Добавлено позднее<\/i>: действительно, оператор <tt>СЕЛЕКТ()<\/tt> чего только не делал, например через него можно было производить смену режимов дисплея или <a href=\"https:\/\/www.youtube.com\/watch?v=HrmktjO4nE4&lc=UggwNVIqqWiLG3gCoAEC\">указывать задержку<\/a>.<\/p>\n",
            "date_published": "2025-04-07T07:42:11+05:00",
            "date_modified": "2025-06-05T11:29:21+05:00",
            "tags": [
                "99",
                "айрхеология",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Mon, 07 Apr 2025 07:42:11 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "135468",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "133908",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-kalkulyator-bc\/",
            "title": "99 бутылок: калькулятор bc",
            "content_html": "<p>Разбирал одну старенькую научную статью с некоторым количеством математики, а внутри увидел вычисления на <tt>bc<\/tt>.<\/p>\n<p>Не особо с ним знаком, но несколько раз пользовался, даже не вникая что он умеет. Честно говоря, не помню зачем он мне тогда понадобился, наверное надо было что-то вычислить в консоли. Из статей в интернете, где он иногда вскользь упоминался, я знал, что это навороченный калькулятор, но не знал насколько.<\/p>\n<p>В общем, оказалось, там внутри целый язык программирования, с циклами, ветвлениями, локальными переменными, строками, массивами и числам. Поддерживаются даже пользовательские функции. Решил попробовать написать на нём очередную «песню о пиве».<\/p>\n<p><b>82. bc<\/b> — интерактивный интерпретатор Си-подобного языка, появившийся ещё в 1975 году как интерфейс для утилиты <tt>dc<\/tt>, на котором «песню о пиве» я <a href=\"https:\/\/bolknote.ru\/all\/99-bottles-dc\/\">уже писал<\/a>. Утилита <tt>dc<\/tt> выполняла вычисления с произвольной точностью, используя обратную польскую запись, а <tt>bc<\/tt> предоставила более привычный Си-подобный синтаксис, транслируя его в команды <tt>dc<\/tt>. В 1991 году утилита <tt>bc<\/tt> была стандартизирована <i>POSIX<\/i>, и сегодня существует несколько её реализаций.<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">\/* Beer song. Evgeny Stepanischev, Jan 2025 *\/\n\ndefine void bottles(b) {\n    if (b) print b else print &quot;No&quot;\n    &quot; bottle&quot;\n    if (b-1) print &quot;s&quot;\n    &quot; of beer&quot;\n}\n\nfor(beer = 99; beer;) {\n    bottles(beer); &quot; on the wall, &quot;\n    bottles(beer); print &quot;!\\n&quot;\n\n    print &quot;Take one down, pass it around,\\n&quot;\n    bottles(--beer); print &quot; on the wall!\\n\\n&quot;\n}\n\nprint &quot;No more bottles of beer on the wall,\\n&quot;\nprint &quot;No more bottles of beer!\\n&quot;\nprint &quot;Go to the store and buy some more,\\n&quot;\nbottles(99); &quot; on the wall!&quot;<\/code><\/pre><p>Единственное, что вызвало у меня затруднение — это как сделать так, чтобы функция ничего не возвращала. Дело в том, что функции в <tt>bc<\/tt> по-умолчанию возвращают число. Если не указано какое, вернётся ноль. А в глобальном контексте то, что возвращается из функции будет напечатано на экране. Поэтому у меня выводился лишний ноль. Чтобы это исправить, нужно в определение функции добавить ключевое слово <tt>void<\/tt>.<\/p>\n<p>Никаких других сложностей с этой программой у меня не возникло, язык простой, хорошо продуманный, лёгок в освоении.<\/p>\n<p><i>Добавлено позднее<\/i>: как оказалось, у языка есть свои особенности о которых нелишне знать. Например, подключение математической библиотеки меняет точность, определяемую переменной <tt>scale<\/tt>, что влияет, например, на операцию вычисления модуля, она начинает работать иначе.<\/p>\n<p>Другая странность — операции сдвига работают в десятичной системе. Не понимаю какой от этого толк. Например, <tt>1&lt;&lt;2<\/tt> даст <tt>100<\/tt>.<\/p>\n",
            "date_published": "2025-02-02T19:42:38+05:00",
            "date_modified": "2025-03-22T00:34:00+05:00",
            "tags": [
                "99",
                "bc",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Sun, 02 Feb 2025 19:42:38 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "133908",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "133463",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-yazyk-b-bi\/",
            "title": "99 бутылок: язык B («Би»)",
            "content_html": "<p>В последнее время я довольно много пишу на Си, как-то так получилось. А поскольку мне хочется углубить свои знания этого языка, иногда что-нибудь ищу и читаю по теме. Так как авторы статей, по всей видимости, любят исторические экскурсы, примерно в каждой третьей написано, что язык Си произошёл от Би.<\/p>\n<p>При этом ни в одной проштудированной мной статье нет ни одной даже самой простенькой программы на Би, а я человек любопытный. В общем, я решил сам посмотреть и вам показать.<\/p>\n<p><b>81. B (язык «Би»)<\/b>, после появления Си, был полностью вытеснен последним и сейчас совершенное не используется. В отличие от, например, Кобола, который старше на десять лет, но при этом вполне ещё встречается.<\/p>\n<p>Думаю, причин тут несколько.<\/p>\n<p>Во-первых, это нацеленность на одну платформу — указатели в Би работают с машинными словами, а не байтами. Во-вторых, неудачные решения в синтаксисе, например, <tt>a =* 10<\/tt> и <tt>a = *10<\/tt> — это разные операции. Первая — умножение переменной <tt>a<\/tt> на десять, вторая — чтение в переменную <tt>a<\/tt> значения по адресу <tt>10<\/tt>. В-третьих, у Си явно больше возможностей, а учитывая, что программы переписываются довольно легко (кроме части с адресацией, где легко наделать ошибок), переход выглядит очень оправданным.<\/p>\n<p>Язык Би и правда очень похож на Си, в этом можно убедиться, взглянув на написанную мной программу. Что тут режет глаз сишному программисту? Прежде всего — отсутствие типов у заголовка функции, импорта функции <tt>printf<\/tt> и странное ключевое слово <tt>extrn<\/tt> внутри функций. <tt>extrn<\/tt> означает, что этот символ (в терминах компилятора) пришёл из внешней зоны видимости. Современное <tt>extern<\/tt> явно произошло от него, только значение немного поменялось.<\/p>\n<p>Немного непривычно, что все переменные объявлены как <tt>auto<\/tt>, но в современном Си так тоже можно. <tt>auto<\/tt>, в данном случае, — класс хранения. В современном Си это класс хранения по-умолчанию, поэтому его не указывают. При этом, если отсутствует указание типа, Си по-умолчанию подставит <tt>int<\/tt>. Как раз для совместимости с Би. То есть <tt>auto beer<\/tt> Си даже правильно поймёт, но выдаст предупреждение.<\/p>\n<p>Впрочем, в стандарте Си23 <tt>auto<\/tt> теперь означает автоматический вывод типа, что для совместимости с Би подходит ещё больше, так как, если приглядеться, строковые массивы («строки») в Би не выделены в отдельный тип.<\/p>\n<p>Из того, что заметить чуть сложнее — экранирующий символ в строке не обратный слэш, а «звёздочка» (<tt>*<\/tt>), поэтому символ перевода строки не <tt>\\n<\/tt>, а <tt>*n<\/tt>.<\/p>\n<p>Ещё немного странно, что тут цикл <tt>while<\/tt>, а не <tt>for<\/tt>, но сильно это в глаза не бросается. В «Би» цикла <tt>for<\/tt> просто нет, поэтому вот так.<\/p>\n<p>Вообще, ощущение такое, что Си взял от Би буквально всё, но, как ни странно, это не так. В Би есть конструкции <tt>===<\/tt>, <tt>=!=<\/tt>, <tt>=&lt;=<\/tt> и <tt>=&gt;=<\/tt>. Наверное они не очень-то нужны, но выглядят интересно. Например, <tt>a === 5<\/tt> означает «если переменная <tt>a<\/tt> равна <tt>5<\/tt>, записать в <tt>a<\/tt> число <tt>1<\/tt>, иначе — <tt>0<\/tt>».<\/p>\n<p>Кроме того, другая почти вся стандартная библиотека. Совпадают всего несколько функций, по больше части это ввод-вывод. Например, в Би можно объединить несколько строк функцией <tt>concat<\/tt>, а <tt>char<\/tt> не тип, а функция получения символа строки по индексу.<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">\/* Beer song. Evgeny Stepanischev, Dec 2024 *\/\nprint_bottles(b, tail) {\n  extrn printf;\n  auto s;\n\n  s = &quot;s&quot;;\n\n  switch (b) {\n    case 0: printf(&quot;No&quot;); break;\n    case 1: printf(&quot;%d&quot;, b); s = &quot;&quot;; break;\n    default: printf(&quot;%d&quot;, b);\n  }\n\n  printf(&quot; bottle%s of beer%s&quot;, s, tail);\n}\n\nmain() {\n  extrn printf, print_bottles;\n  auto beer;\n\n  beer = 99;\n  while (beer) {\n    print_bottles(beer, &quot; on the wall, &quot;);\n    print_bottles(beer, &quot;!*n&quot;);\n\n    beer--;\n\n    printf(&quot;Take one down, pass it around.*n&quot;);\n    print_bottles(beer, &quot; on the wall!*n*n&quot;);\n  }\n\n  printf(&quot;No more bottles of beer on the wall, &quot;);\n  printf(&quot;no more bottles of beer.*n&quot;);\n  printf(&quot;Go to the store and buy some more,*n&quot;);\n  printf(&quot;99 bottles of beer on the wall...*n&quot;);\n}<\/code><\/pre><p>Я долго пытался найти хоть какой-нибудь работающий под современными платформами компилятор, перебрал около десятка, но везде были свои сложности — некоторые не собирались, другие падали на совершенно валидном коде (например, <tt>printf(&quot;%c&quot;, &apos;t&apos;)<\/tt>).<\/p>\n<p>В итоге, остановился на <a href=\"https:\/\/tio.run\/#ybc\">онлайн-версии <i>ybc<\/i><\/a> (<i>Yasha’s B Compiler<\/i>).<\/p>\n",
            "date_published": "2024-12-27T11:53:15+05:00",
            "date_modified": "2024-12-30T18:12:36+05:00",
            "tags": [
                "99",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Fri, 27 Dec 2024 11:53:15 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "133463",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "132569",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-preprocessor-si\/",
            "title": "99 бутылок: препроцессор Си",
            "content_html": "<p>Те программисты, кто каким-либо образом сталкивался с языком Си, знают, что у него есть так называемый препроцессор. Его директивы начинаются с символа решётки и выполняются до компиляции программы. Обычно его используют, чтобы включить одни файлы в другие, определить макрос или проверить значение каких-либо внутренних флагов компилятора.<\/p>\n<p>С тех пор как мне много лет назад кто-то показал как можно на нём <a href=\"https:\/\/github.com\/pfultz2\/Cloak\/wiki\/C-Preprocessor-tricks,-tips,-and-idioms\">писать программы<\/a>, меня не оставляла идея написать на нём «песню о пиве».<\/p>\n<p><b>80. Препроцессор Си<\/b>, разумеется, не задумывался как что-то на чём будут писать программы, поэтому, чтобы добиться цели, приходится использовать россыпь хитростей и разнообразных хаков. Не думаю, что имеет смысл их описывать, тем более не я их придумал. В интернете есть достаточно подробных статей по этой теме, как на <a href=\"https:\/\/www.scs.stanford.edu\/~dm\/blog\/va-opt.html\">английском<\/a>, так и на <a href=\"https:\/\/habr.com\/ru\/articles\/787442\/\">русском<\/a>.<\/p>\n<p>Изучение этих материалов было не только для меня интересным делом, но и полезным — я лучше понял как работает препроцессор в Си, а так же узнал кое-что новое, являющееся, правда, расширением языка.<\/p>\n<p>Вопреки обыкновению, вывод программы я обрабатываю другой утилитой (см. строку запуска). Это приходится делать, так как в этом языке нет простого способа вывести перевод строки. Можно было бы сделать это <i>ANSI<\/i>-кодами, но их не будет видно в листинге кода, что меня не устраивает.<\/p>\n<pre class=\"e2-text-code\"><code class=\"less\">#if 0\nBolknote.ru, 2024.11.05\nTo run:\ngcc -P -E - &lt; 99 | sed &#039;s\/\\\\n *\/\\n\/g&#039;\n#endif\n\n#define CAT(a, b) a##b\n#define SECOND(a, b, ...) b\n#define TEST(...) SECOND(__VA_ARGS__, 0)\n#define LF \\n\n\n#define DEC(n) CAT(DEC_,n)\n#define DEC_0 9\n#define DEC_1 0\n#define DEC_2 1\n#define DEC_3 2\n#define DEC_4 3\n#define DEC_5 4\n#define DEC_6 5\n#define DEC_7 6\n#define DEC_8 7\n#define DEC_9 8\n\n#define DEC_X0(a, b) IF_ELSE(ISZERO(b)) (DEC(a)) (a)\n\n#define ISEND(a, b) TEST(ISEND_ ## a ## b)\n#define ISEND_01 ,1\n\n#define ISZERO(n) TEST(ISZERO_ ## n)\n#define ISZERO_0 ,1\n\n#define ISONE(n) TEST(ISONE_ ## n)\n#define ISONE_1 ,1\n\n#define IF_ELSE(b) CAT(IF_, b)\n#define IF_0(i) ELSE_0\n#define IF_1(i) i ELSE_1\n#define ELSE_0(e) e\n#define ELSE_1(e)\n\n#define PLUR(n) IF_ELSE(ISONE(n)) (bottle) (bottles)\n\n#define PRINT_PLUR(a, b) IF_ELSE(ISZERO(a)) (b PLUR(b)) (CAT(a, b) bottles)\n\n#define PRINT_B(a, b) IF_ELSE(ISZERO(b)) \\\n        (IF_ELSE(ISZERO(a)) \\\n            (No bottles) \\\n            (PRINT_PLUR(a, b)) \\\n        ) \\\n        (PRINT_PLUR(a, b)) of beer\n\n#define PRINT_ROW(a, b) \\\n    PRINT_B(a, b) on the wall, PRINT_B(a, b)! LF \\\n    Take on down, pass it around, LF \\\n    PRINT_B(DEC_X0(a, b), DEC(b))! LF LF\n\n\n#define STOP(...) No more bottles of beer on the wall, LF \\\nNo more bottles of beer! LF \\\nGo to the store and buy some more, LF \\\nPRINT_B(9, 9) on the wall!\n\n#define EMPTY()\n#define DEFER(...) __VA_ARGS__ EMPTY()\n\n#define CROSS1(a, b) PRINT_ROW(a, b) \\\n    DEFER(IF_ELSE(ISEND(a, b)) (STOP) (CROSS2)) (DEC_X0(a, b), DEC(b))\n\n#define CROSS2(a, b) PRINT_ROW(a, b) DEFER(CROSS1) (DEC_X0(a, b), DEC(b))\n\n#define MUL0(...) MUL1(MUL1(MUL1(MUL1(__VA_ARGS__))))\n#define MUL1(...) MUL2(MUL2(MUL2(MUL2(__VA_ARGS__))))\n#define MUL2(...) MUL3(MUL3(MUL3(MUL3(MUL3(__VA_ARGS__)))))\n#define MUL3(...) __VA_ARGS__\n\n#define RUN(a, b) MUL0(CROSS1(a, b))\n\nRUN(9, 9)<\/code><\/pre>",
            "date_published": "2024-11-05T22:34:51+05:00",
            "date_modified": "2024-11-06T11:41:14+05:00",
            "tags": [
                "99",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Tue, 05 Nov 2024 22:34:51 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "132569",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "130901",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-piva-na-perq-pascal\/",
            "title": "«99 бутылок пива» на PERQ Pascal",
            "content_html": "<p>Мои читатели знают, что последние несколько дней я возился с эмулятором <i>PERQ<\/i>. <i>PERQ<\/i> — это персональный компьютер, выпускавшийся в начале 1980-х годов, первый коммерчески успешный ПК с графическим интерфейсом. Такой олдскул мил моему сердцу, поэтому я потратил несколько вечеров, чтобы попробовать хоть на базовом уровне разобраться что из себя представляла операционная система тех компьютеров.<\/p>\n<p>Последние пару дней я пытался написать на тамошнем «Паскале» «песню о пиве», но поскольку на моём ноутбуке потребовалась двойная эмуляция, скорость работы встроенного в систему редактора была ниже плинтуса. Писать было очень сложно, а править ошибки — ещё сложнее. Тем не менее, я справился.<\/p>\n<p><b>79. PERQ Pascal<\/b> — диалект некогда широко распространённого в учебных учреждениях языка «Паскаль». Компилируемый язык, в какой-то степени можно назвать его конкурентом Си, которому он проиграл вчистую.<\/p>\n<p>Не думаю, что кто-то, кроме редких энтузиастов языка, сейчас выберет «Паскаль» для чего-то нового, но со старыми проектами на нём я иногда сталкиваюсь. Например, в начале этого года видел электронный документооборот, написанный на «Делфи» (это визуальная среда программирования и название диалекта «Турбо Паскаля»).<\/p>\n<pre class=\"e2-text-code\"><code class=\"pascal\">{$MESSAGE Written by Evgeny Stepanischev, 2024}\nPROGRAM BOTTLES_OF_BEER;\n\nconst\n    MAX_BOTTLES = 99;\n\nFunction BotStr(count: integer): string;\nbegin\n    if count = 1 then\n        BotStr := &#039; bottle of beer&#039;\n    else\n        BotStr := &#039; bottles of beer&#039;;\nend;\n\nProcedure SingSong;\nvar\n    i: integer;\n    b: string;\nbegin\n    for i := MAX_BOTTLES downto 1 do\n    begin\n        b := BotStr(i);\n        writeln(i:0, b, &#039; on the wall,&#039;);\n        writeln(i:0, b, &#039;!&#039;);\n        writeln(&#039;Take one down, pass it around,&#039;);\n        if i &gt; 1 then\n        begin\n            b := BotStr(i-1);\n            writeln((i - 1):0, b, &#039; on the wall!&#039;)\n        end\n        else\n            writeln(&#039;No more bottles of beer on the wall!&#039;);\n        writeln;\n    end;\n    \n    writeln(&#039;No more bottles of beer on the wall,&#039;);\n    writeln(&#039;No more bottles of beer!&#039;);\n    writeln(&#039;Go to the store and buy some more,&#039;);\n    b := BotStr(MAX_BOTTLES);\n    writeln(MAX_BOTTLES:0, b, &#039; on the wall!&#039;);\nend;\n\nbegin\n    SingSong;\nend.<\/code><\/pre><p>Из того, с чем мне пришлось столкнуться, когда я писал программу.<\/p>\n<p>Больше всего ушло времени на ошибку <i>Error 116: Error in type of standard procedure parameter<\/i>. Речь тут, вроде, идёт о том, что я какой-то процедуре передаю параметры не того типа. Несмотря на то, что в ошибке был указан номер строки, мне всё равно не удавалось понять, что я делаю не так.<\/p>\n<p>Оказалось, что вызов функции <tt>BotStr<\/tt> нельзя напрямую поместить во <tt>writeln<\/tt>, надо обязательно использовать промежуточную переменную (у меня это <tt>b<\/tt>). Учитывая, что скорость работы с эмулятором не позволяет быстро попробовать разные варианты, пришлось изрядно поскрипеть мозгом, чтобы дойти до сути.<\/p>\n<p>Вторая вещь оказалась проще для исправления. В этом диалекте «Паскаля» вывод чисел происходит с отступом. В моём случае выглядит это неаккуратно. К счастью убрать его просто — нужно добавить параметр <tt>:0<\/tt> для каждого числа, выводимого на экран.<\/p>\n<p>В какой-то момент я так устал редактировать код со скоростью улитки, стал использовать обычный редактор своего ноутбука. Потом я просто запускал небольшую программу, написанную на «Пайтоне», которая печатала код за меня прямо внутрь окна с эмулятором <i>PERQ<\/i>:<\/p>\n<pre class=\"e2-text-code\"><code class=\"python\">import pyautogui, time\n\nFILE_PATH = &#039;99.pas&#039;\n\ntime.sleep(5)\n\nwith open(FILE_PATH, &#039;r&#039;) as file:\n    for line in file:\n        for x in line.lstrip():\n            pyautogui.typewrite(x)\n            time.sleep(.6)\n\n        time.sleep(1)<\/code><\/pre><p>Вчера ночью всё наконец запустилось и я смог посмотреть как работает моя первая (и наверняка единственная) программа для <i>PERQ<\/i>.<\/p>\n<p>Кстати, вот ещё одна интересная деталь.<\/p>\n<p>Чтобы получить исполняемый файл, надо исходный код программы сначала скомпилировать командой <tt>COMPILE<\/tt> в объектный файл (<tt>.SEG<\/tt>), потом скомпоновать командой <tt>LINK<\/tt> в исполняемый (<tt>.RUN<\/tt>). У обеих команд есть параметр, где указывается имя файла, с которым мы работаем.<\/p>\n<p>Но если мы открывали на редактирование какой-то файл, его имя указывать не надо — операционная система его где-то запоминает и команды <tt>COMPILE<\/tt> и <tt>LINK<\/tt> знают с каким файлом мы работаем. Очень удобно.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2024.09.25@2x.png\" width=\"768\" height=\"1024\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Результат запуска «Песни о пиве» под эмулятором компьютера <i>PERQ<\/i><\/div>\n<\/div>\n",
            "date_published": "2024-09-25T10:57:33+05:00",
            "date_modified": "2024-09-25T17:02:38+05:00",
            "tags": [
                "99",
                "pascal",
                "PERQ"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Wed, 25 Sep 2024 10:57:33 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "130901",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "129998",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-bootbasic\/",
            "title": "99 бутылок: bootBASIC",
            "content_html": "<p>Очередная «песня о пиве», в последнее время я что-то прям зачастил.<\/p>\n<p><b>78. bootBASIC<\/b> — интерпретатор простенького диалекта Бейсика, занимающий 512 байт. В наше время, когда некоторые программы занимают гигабайты, удивительно видеть что-то настолько крошечное.<\/p>\n<p>А тут целый язык, со строками, поддержкой математики, ветвлением и даже небольшим редактором кода. Правда я уже писал «песню о пиве» на <a href=\"https:\/\/bolknote.ru\/all\/99-butylok-sectorc\/\">миниатюрном Си<\/a>. Он занимал чуть меньше, но и возможности там были в чём-то скромнее.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2024.08.08@2x.png\" width=\"720\" height=\"428\" alt=\"\" \/>\n<\/div>\n<p>Конечно, при таком размере есть свои ограничения — арифметика только целочисленная, до 65535 и нет операций сравнения, — можно проверить только, что число не равно нулю.<\/p>\n<p>Кроме того, строки кода ограничены по длине, но из-за несовершенства интерпрератора можно хитрить — пропускать пробелы и опускать закрывающую кавычку. За счёт этого в строке можно уместить чуть больше, но редактору от такого плохеет — команда <tt>list<\/tt> начинает выдавать мусор и отредактировать некоторые строки не удаётся. Правда работе программы это не мешает.<\/p>\n<p>Обидно, что нет <tt>gosub<\/tt> для перехода к подпрограмме, вместо этого приходится использовать <tt>goto<\/tt> на переменную. Ладно хоть такая возможность есть. Комментариев нет, но можно использовать для этого команду <tt>print&quot;&quot;;<\/tt> — дальше неё интерпретатор в строке ничего не видит.<\/p>\n<pre class=\"e2-text-code\"><code class=\"basic\">1 print&quot;&quot;; Written by Evgeny Stepanischev https:\/\/bolknote.ru\n10 b=99\n20 r=30\n21 goto 200\n\n30 print&quot;of beer on &quot;;\n31 print&quot;the wall, &quot;;\n\n40 r=50\n41 goto 200\n\n50 print&quot;of beer.&quot;\n60 b=b-1\n\n70 print&quot;Take one do&quot;;\n71 print&quot;wn and pass&quot;;\n72print&quot; it around, &quot;;\n\n80 r=90\n81 goto 200\n\n90 print&quot;of beer on &quot;;\n91 print&quot;the wall.&quot;\n\n100 if b goto 20\n110 goto 900\n\n200 if b goto 203\n201 print&quot;no&quot;;\n202 goto 210\n203 print b;\n210 print&quot; bottle&quot;;\n220 if b-1 goto 250\n230 print&quot; &quot;;\n240 goto r\n250 print&quot;s &quot;;\n299 goto r\n\n900print&quot;No more bott&quot;;\n901print&quot;les of beer &quot;;\n902print&quot;on the wall,&quot;;\n903print&quot; no more bot&quot;;\n904print&quot;tles of beer.&quot;\n905print&quot;Go to the st&quot;;\n906print&quot;ore and buy &quot;;\n907print&quot;some more, 9&quot;;\n908print&quot;9 bottles of&quot;;\n909print&quot; beer on the&quot;;\n910print&quot; wall.<\/code><\/pre>",
            "date_published": "2024-08-09T01:02:38+05:00",
            "date_modified": "2024-08-09T09:16:23+05:00",
            "tags": [
                "99",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Fri, 09 Aug 2024 01:02:38 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "129998",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "129806",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-mkdir-find\/",
            "title": "99 бутылок: mkdir + find",
            "content_html": "<p><b>77. mkdir + find<\/b> — тут некий японец <a href=\"https:\/\/web.archive.org\/web\/20240731103120\/https:\/\/ogiekako.vercel.app\/blog\/find_mkdir_tc\">придумал<\/a> как писать программы, используя только утилиты <tt>mkdir<\/tt> и <tt>find<\/tt>. Мне такое очень нравится, поэтому и я решил что-нибудь написать на связке этих утилит. Сразу вспомнил, что давно у меня не было «песни о пиве». Есть у меня такое развлечение — писать её на всём подряд.<\/p>\n<p>Программу вы можете видеть на экране, а как она работает расскажу чуть ниже.<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\"># Written by Evgeny Stepanischev https:\/\/bolknote.ru\n\nrm -rf bottles; mkdir bottles\nfind bottles -maxdepth 98 -execdir mkdir bottles\/bottles \\;\nfind bottles -empty -name bottles -execdir mkdir bottles\/bottle \\;\n\nfind bottles \\\n        -empty -printf &quot;1 bottle of beer on the wall, 1 bottle of beer.\nTake one down and pass it around, no bottles of beer on the wall.\n\nNo more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\\n&quot; -o \\\n        -regex &#039;.*\/bottles\/bottles&#039; \\\n        -execdir find . \\\n                -empty -printf &quot;%d bottles of beer on the wall, %d bottles of beer.\\n&quot; \\; \\\n        -execdir find bottles \\\n                -empty -printf &quot;Take one down and pass it around, %d %f of beer on the wall.\\n\\n&quot; \\;\n\n# Clean up\nrm -rf bottles<\/code><\/pre><p>Прежде всего — что за две команды тут используются? <tt>rm<\/tt> не в счёт, она для очистки мусора, можно без неё.<\/p>\n<p>Команда <tt>mkdir<\/tt>, наверное, многим знакома — это создание директория, а <tt>find<\/tt> — утилита для поиска файлов и директориев по различным критериям. Причём с найденным она может делать некоторые простые действия.<\/p>\n<p>Чтобы писать программы, автор оригинальной статьи использует несколько находок.<\/p>\n<p>Во-первых, создание вложенных директориев командой <tt>find<\/tt> с параметром <tt>-execdir mkdir имя<\/tt>.<\/p>\n<p>Параметр запускает указанную команду в директории, который сейчас просматривает <tt>find<\/tt>. Эти вложенные директории в дальнейшем используются как конечный цикл для следующей команды <tt>find<\/tt>. Вложенность при этом можно регулировать параметром <tt>-maxdepth<\/tt>.<\/p>\n<p>Во-вторых, параметр <tt>-printf<\/tt> поддерживает аргумент <tt>%d<\/tt>, который позволяет вывести текущий уровень вложенности и полезен в тех же циклах.<\/p>\n<p>В-третьих, фильтры команды <tt>find<\/tt>, а так же директивы <tt>-o<\/tt> («ИЛИ») и <tt>-and<\/tt> («И») можно использовать для организации условий.<\/p>\n<p>Мне пришлось дополнительно решить ещё две проблемы.<\/p>\n<p>Во-первых, цикл у меня обратный. Чтобы его получить, я запускаю две команды <tt>find<\/tt> — одну в другой. Первая считает директории в одну сторону по возрастанию, вторая запускается на каждом шаге и итерируется по остатку директориев до конца. Поскольку это тоже цикл, а мне нужно только последнее значение, я использую параметр <tt>-empty<\/tt>, чтобы получить только последний директорий — тот, в котором пусто.<\/p>\n<p>На самом деле на каждой внешней итерации я пускаю не один, а два внутренних цикла, поскольку мне нужны два числа — текущее значение считаемого «пива» и это же значение, уменьшенное на единицу. Сделать это было просто — ещё один <tt>find<\/tt> надо запускать во вложенном директории.<\/p>\n<p>Во-вторых, мне нужно поддержать окончание множественного числа. Тут я немного смухлевал и слегка замёл эту проблему в конец — там я вывожу дополнительные строки «<i>1 bottle of beer on the wall, 1 bottle of beer. \/ Take one down and pass it around, no bottles of beer on the wall<\/i>».<\/p>\n<p>Но в строке перед этим текстом у меня сделано более честно. Как видно, структура директориев у меня такая: 98 раз созданы вложенные <tt>bottles<\/tt>, а конечная директория называется <tt>bottle<\/tt>.<\/p>\n<p>Это мне позволяет в одном из «циклов», огранизованных через <tt>find<\/tt>, использовать аргумент <tt>%f<\/tt> параметра <tt>-printf<\/tt>, который выводит имя текущего директория. Таким образом в самом конце я получаю <tt>bottle<\/tt> вместо <tt>bottles<\/tt>.<\/p>\n<p><i>Добавлено<\/i>: мне подсказали, что обратный цикл можно огранизовать проще, через ключ <tt>-depth<\/tt>, переделал программу под него:<\/p>\n<pre class=\"e2-text-code\"><code class=\"bash\"># Written by Evgeny Stepanischev https:\/\/bolknote.ru\n\nfind start -delete 2&gt;&amp;-\nmkdir -p start\/bottle\/bottles\nfind start\/bottle -maxdepth 97 -name bottles -execdir mkdir bottles\/bottles \\;\n\nfind start -mindepth 1 -depth \\\n        -empty -printf &quot;%d %f of beer on the wall, %d %f of beer.\\n&quot; -o \\\n        ! -empty -printf &quot;Take one down and pass it around, %d %f of beer on the wall.\\n\\n&quot; \\\n        -printf &quot;%d %f of beer on the wall, %d %f of beer.\\n&quot; \\\n        -name bottle -printf &quot;Take one down and pass it around, no bottles of beer on the wall.\n\nNo more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\\n&quot;\n\nrm -rf start<\/code><\/pre>",
            "date_published": "2024-08-01T00:43:34+05:00",
            "date_modified": "2025-01-30T09:47:38+05:00",
            "tags": [
                "99",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Thu, 01 Aug 2024 00:43:34 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "129806",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "123634",
            "url": "https:\/\/bolknote.ru\/all\/programmiruyu-pod-flipper-zero\/",
            "title": "Программирую под Flipper Zero",
            "content_html": "<p>Мне месяц назад на день рождения подарили «Флиппер Зеро» — «перочинный ножик для хакера». Довольно известное в узких кругах устройство, очень занятное. Но об этом как-нибудь в другой раз.<\/p>\n<p>Давно подмывало что-нибудь под него написать, но всё как-то занят по уши был всякими неотложными делами — то сериалы надо посмотреть, то ленту в соцсетях полистать. Но наконец освободился, сегодня утром <a href=\"https:\/\/github.com\/bolknote\/99-Flipper\">начал<\/a>.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/bolknote.ru\/pictures\/2023.10.19@2x.jpg\" width=\"1000\" height=\"562\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Попробую написать «Песню о пиве»<\/div>\n<\/div>\n<p>В интернете есть несколько статей о том как начать, которые мне очень помогли. Довольно быстро я научился собирать первое приложение, которые выводит строчку текста и опрашивает клавиатуру. По пути поразился сколько бойлерплейта (шаблонного почти одинакового для каждого приложения кода) пришлось написать. Очень не хватает какого-нибудь фреймворка.<\/p>\n<p>Пока застопорился — не пойму как прокручивать экран и есть ли что-то вообще для этого. Исчерпывающей документации по АПИ я что-то не нашёл, а в статьях сторонних разработчиков рекомендуют читать исходники других программ. Что ж, уже начал, но пока решение мне не попалось.<\/p>\n",
            "date_published": "2023-10-19T21:59:06+05:00",
            "date_modified": "2023-10-19T21:59:02+05:00",
            "tags": [
                "99",
                "flipper zero",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Thu, 19 Oct 2023 21:59:06 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "123634",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "120360",
            "url": "https:\/\/bolknote.ru\/all\/99-butylok-sectorc\/",
            "title": "99 бутылок: SectorC",
            "content_html": "<p>Мне тут попеняли в комментариях, что я совсем забросил раздел, в котором я пишу на разных языках программирования американскую считалочку про пиво на стене. Я почему-то думал, что недавно что-то в него добавлял, а оказалось, что с того раза прошло полтора года.<\/p>\n<p>Пора возобновить.<\/p>\n<p><b>76. SectorC<\/b> — очень <a href=\"https:\/\/web.archive.org\/web\/20230609141452\/https:\/\/habr.com\/ru\/companies\/ruvds\/articles\/740310\/\">маленький компилятор<\/a> подмножества языка Си. Помещается в один сектор — 512 байт, отсюда и название. Язык очень сильно урезанный, но всё равно меня поражает как автор запихнул имеющееся в такой малый объём.<\/p>\n<p>В языке нет строк и операторов ввода-вывода, но в стандартной библиотечке языка (часть которая написана в машинных кодах), есть функции, позволяющие выводить символы, используя их коды.<\/p>\n<p>Из-за этого моя программа было сильно распухла, но я немного заморочился и закодировал строки в целых шестнадцатибитных числах — это единственный тип, помимо указателя из доступных. В итоге объём сильно сократился по сравнению с первой версией.<\/p>\n<p>Самые распространённые в выходном тексте буквы я кодирую полубайтом от <tt>0x1<\/tt> до <tt>0xE<\/tt>, а более редкие — байтом от <tt>0xF1<\/tt> до <tt>0xFC<\/tt>. Завтра попробую описать это чуть подробнее.<\/p>\n<p>Немного так же печалит, что нет локальных переменных, параметров и возвращаемых значений — всё передаётся через глобальные переменные, как в ранних Бейсиках. Кроме того, немного по-особенному расставляются пробелы. Так надо для упрощения разбиения компилятором программы на токены.<\/p>\n<pre class=\"e2-text-code\"><code class=\"cpp\">\/\/ Written by Evgeny Stepanischev https:\/\/bolknote.ru\r\n\/\/ SectorC\r\n\r\nint c; \/\/ input\r\nint shift; int x;\r\n\r\nvoid x() { if( c == ( x &gt;&gt; 8 ) ){ print_ch = x &amp; 255; } }\r\nvoid p() { print_ch = c; print_char(); }\r\n\r\nvoid c2c() {\r\n    c = c + shift;\r\n    x =  288; x(); x =  613; x(); x =  879; x(); x = 1140; x(); x = 1388; x();\r\n    x = 1634; x(); x = 1902; x(); x = 2145; x(); x = 2419; x(); x = 2674; x();\r\n    x = 2918; x(); x = 3191; x(); x = 3338; x(); x = 3684; x(); x = 4200; x();\r\n    x = 4396; x(); x = 4654; x(); x = 4981; x(); x = 5204; x(); x = 5481; x();\r\n    x = 5739; x(); x = 6000; x(); x = 6253; x(); x = 6471; x(); x = 6734; x();\r\n    x = 7033; x();\r\n\r\n    if( c != 15 ){ print_char(); }\r\n    shift = 0; if( c == 15 ){ shift = 15; }\r\n}\r\n\r\nint s; \/\/ input\r\nvoid ps() {\r\n    while( s ){\r\n        c = s &amp; 15;\r\n        c2c();\r\n        s = s &gt;&gt; 4;\r\n        if( s == ( 0 - 1 ) ){ s = 0; } \/\/ signed workaround\r\n    }\r\n}\r\n\r\nvoid bottle()  { s = 17249; ps(); s = 596; ps(); }\r\nvoid of_beer() { s = 6961; ps(); s = 41510; ps(); }\r\nvoid on_wall() { of_beer(); s = 5937; ps(); s = 8692; ps(); s = 22721; ps(); print_char(); }\r\nvoid o_more()  { s = 40723; ps(); s = 675; ps(); }\r\n\r\nint b; \/\/ input\r\nvoid bottle_b() {\r\n    if( b == 0 ){ s = 55; ps(); }\r\n    if( b &gt; 0 ){ print_num = b; print_u16(); }\r\n    bottle();\r\n    if( b != 1 ){ c = 115; p(); } \/\/ s\r\n}\r\n\r\nvoid main() {\r\n    shift = 0; b = 99;\r\n\r\n    while( b &gt; 0 ){\r\n        bottle_b(); on_wall();\r\n\r\n        s = 303; ps(); \/* &#039;, &#039; *\/ bottle_b(); of_beer();\r\n        c = 46; p(); \/\/ &#039;.&#039;\r\n        print_newline();\r\n        b = b - 1;\r\n\r\n        s = 2143; ps(); s = 4735; ps(); s = 4723; ps(); s = 31806; ps();\r\n        s = 59265; ps(); s = 35057; ps(); s = 409; ps(); s = 5231; ps();\r\n        s = 936; ps(); s = 59215; ps(); s = 303; ps();\r\n\r\n        bottle_b(); on_wall(); c = 46; p(); \/\/ &#039;.&#039;\r\n        print_newline(); print_newline();\r\n    }\r\n\r\n    c = 78;  p(); \/* &#039;N&#039; *\/ o_more(); bottle(); c = 115; p(); \/\/ &#039;s&#039;\r\n    on_wall();\r\n    s = 28975; ps(); \/* &#039;, n&#039; *\/ o_more(); bottle(); c = 115; p(); \/\/ &#039;s&#039;\r\n    on_wall();\r\n    c = 46;  p(); \/\/ &#039;.&#039;\r\n    print_newline(); \r\n\r\n    s = 5039; ps(); s = 16692; ps(); s = 4639; ps(); s = 41801; ps();\r\n    s = 30738; ps(); s = 1566; ps(); s = 53071; ps(); s = 913; ps();\r\n    s = 4767; ps(); s = 41887; ps(); s = 4850; ps();\r\n    b = 99; bottle_b(); on_wall(); c = 46; p(); \/\/ &#039;.&#039;\r\n}<\/code><\/pre>",
            "date_published": "2023-06-12T01:05:16+05:00",
            "date_modified": "2023-06-14T18:15:51+05:00",
            "tags": [
                "99",
                "sectorc",
                "программирование",
                "си"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Mon, 12 Jun 2023 01:05:16 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "120360",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        },
        {
            "id": "125426",
            "url": "https:\/\/bolknote.ru\/all\/4244\/",
            "title": "99 бутылок: Scratch и HQ9+",
            "content_html": "<p><b>54. HQ9+<\/b> — язык, у которого только четыре оператора. «H» выводит «Hello world», «Q» печатает исходный код программы, 9 выводит «песню о пиве», а «плюс» «увеличивает аккумулятор». Язык, понятное дело, придуман в шутку и у неё есть продолжения — «объектный» «HQ9++», Тюринг-полный «HQ9+B» и так далее.<\/p>\n<p>Данная программа на этом языке выглядит вот так:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">9<\/code><\/pre><p><br><\/p>\n<p><b>53. Scratch<\/b>. «<a href=\"https:\/\/ru.wikipedia.org\/wiki\/Скретч_(язык_программирования)\">Скретч<\/a>» — язык программирования для школьников младших и средних классов. Интересный класс языков, я никогда им особо не интересовался, а тут возник совершенно профессиональный интерес — как можно дать возможность программировать что-то в нашей системе людям, которые в программировании ничего не понимают.<\/p>\n<p>Я, для интереса, написал на «Скретче» свои «99  бутылок». Программа на скриншоте. В среде редактирования мы управляем  лисёнком и всё программирование крутится вокруг него, куча блоков  позволяют что-то с ним делать — двигать, вращать, заставляют его что-то  говорить и так далее. «Думать» выводит свой аргумент в  «облачке» его мыслей.<\/p>\n<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"e2-text-table\">\n<tr>\n<td><img width=\"493\" height=\"576\" src=\"\/\/bolknote.ru\/imgs\/2014.12.02.png\" alt=\"Скретч (84.30КиБ)\" srcset=\"\/\/bolknote.ru\/imgs\/\/2014.12.02@2x.png 2x\"><\/td>\n<\/tr>\n<\/table>\n<p>Программа строится из цветных блоков, цвета кодируют тип блока — «движение», «внешность», «звук», «данные», «управление», «операторы» и так далее. Основные конструкции обычных языков в наличии. Процедур, как таковых, нет, но можно отсылать события другим ээ… подпрограммам и через глобальные переменные получать результат. Есть даже что-то похожее на потоки — передавая события, можно запускать подпрограммы асинхронно.<\/p>\n<p>Из структур данных есть целые числа и числа с плавающей точкой, строки и даже массивы. Для последних доступны все операторы для работы с ними — добавление, удаление, замена, получение значения по индексу, замена и так далее.<\/p>\n<p>Есть ещё какие-то «Другие блоки», но с ними я не разбирался. Кажется, через них можно как-то расширять язык.<\/p>\n",
            "date_published": "2014-12-02T23:56:00+05:00",
            "date_modified": "2025-04-25T19:44:58+05:00",
            "tags": [
                "99",
                "scratch",
                "программирование"
            ],
            "author": {
                "name": "Евгений Степанищев",
                "url": "https:\/\/bolknote.ru\/",
                "avatar": "https:\/\/bolknote.ru\/pictures\/userpic\/userpic@2x.jpg?1760600028"
            },
            "_date_published_rfc2822": "Tue, 02 Dec 2014 23:56:00 +0500",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "125426",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": null,
                "og_images": []
            }
        }
    ],
    "_e2_version": 4079,
    "_e2_ua_string": "Aegea 11.0 (v4079e)"
}