Ненормальное программирование ★
Введение
Недавно разговорился с одним приятелем, тема разговора
Я его удивил, рассказав, что тема «непрактичного» или, как я его ещё называю, «ненормального программирования» привлекает меня давно, по меньшей мере с конца
Более нормальным программированием я тоже занимаюсь, но чаще всего это работа
«Junix» (1999—2003)
Это самый старый из сохранившихся подобных проектов. И, наверное, самый монументальный. Дело было в конце
Мне же было очевидно, что ДжаваСкрипт уже можно использовать для большего. Я так был уверен в своей идее, что был готов продемонстрировать её всему миру. Проект занял несколько лет — в начальной фазе я занимался им каждый вечер на протяжении нескольких месяцев, а потом изредка реализовывал
Так у меня получилось
«JS-FTPd » (2001)
В то время, когда я делал этот проект, ДжаваСкрипт ещё не был монополистом в мире браузерных языков. Существовало несколько альтернативных технологий, так, например, в браузерах была технология «апплетов», позволявших писать небольшие встраиваемые в браузер приложения на языке Джава.
В то же время в браузере «Нетскейп» была нестандартная технология «LiveConnect», которая позволяла вызывать Джаву из ДжаваСкрипта, а так же запрашивать из браузера различные разрешения, например, на сетевое взаимодействие. Впервые я познакомился с этой технологией, когда писал «Junix», там я через неё работал с диском компьютера, а в этом проекте мне захотелось изучить работу с сокетами.
Используя «LiveConnect» я написал FTP-сервер — на ДжаваСкрипте, вызывая Джаву там, где ДжаваСкрипт
«Крестики-нолики » на HTML и CSS (2011)
Тема игр для компьютера без программирования меня давно интересовала.Я написал их несколько. В основном, с использованием HTML и CSS. Ни тот ни другой языками программирования не считаются, тем не менее, развились настолько, что на них можно писать очень сложные интерактивные вещи.
«Арканоид» на bash (2011)
В рамках этой игры я хотел испытать две концепции.
«Шахматы» на языке утилиты sed (2013)
Когда я решил написать шахматы на sed, знакомые, с которыми я поделился своей идеей, подумали, что я сошёл с ума. Ну а как иначе? sed — в их глазах не язык программирования, а текстовый редактор, который умеет менять «кота» на «собаку» в конфигах.
Но мне хотелось доказать, что даже в таком примитиве можно выжать больше, чем кажется. В итоге я провёл две недели в диалоге с регулярными выражениями: заставлял их считать, сравнивать числа в придуманном мной формате 111::11 (так в нём записывается число 302) и имитировать логику шахматного ИИ. Каждый день я порывался бросить: когда код превращался в лабиринт из меток и переходов, а функция вычитания занимала 59 строк, но упрямство перевешивало.
Как я уже сказал, большая часть логики сделана на регулярных выражениях, поверх которых я написал
«Тетрис» на языке AppleScript (2017)
Встроенный в «МакОс» язык автоматизации «ЭплСкрипт», кажется, никто никогда для написания игр использовать и не пытался. По крайней мере, я таких попыток не обнаружил. И немудрено — сходу неясно чем бы он мог таким оперировать, чтобы взаимодействовать с пользователем.
Я написал «Тетрис» на нём, когда понял, что графику можно выводить, манипулируя свёрнутыми в точку окнами приложений и опрашивая клавиши регулирования громкости.
«Морской бой» на клавише «Капслок» (2020)
Игру «Морской бой» я написал по двум причинам.
Идея с клавишей пришла в голову, когда я в очередной раз размышлял о бессмысленной для меня клавише «Капслок» — я её совершенно не использую и всегда отключаю на уровне операционной системы.
В итоге я написал игру, где всё взаимодействие с компьютером делается через азбуку Морзе — пользуясь одной клавишей, как ключом, мы вводим координаты для игры в «Морской бой», а ответы компьютера считываем через мигание светодиода на «Капслоке». Картинка ниже — это отладочный режим, его можно включить,
«Вампус» для наручных часов в корпусе «Касио» (2024)
Я вообще очень люблю различные
До сих пор ношу эти часы почти каждый день, они у меня сейчас самые любимые.
Но если уж я добыл себе эти часы, было бы логично
«Вампус» на командах командной строки (2024)
Была у меня ещё одна идея — сделать
Путём разных манипуляций я ввёл целый набор всяких ограничений, чтобы шелл напоминал игру, а не просто командную строку, но если знать как всё устроено, это всё, конечно, можно обойти. Тем не менее, мне кажется, что с целью я справился.
«PowerShoot» игра, управляемая подключением зарядки (2024)
Игра, написанная под влиянием болезни. Я довольно тяжело болел и хотелось развлечь себя
На искомую идею меня натолкнул звук, который раздаётся, когда «Макбук» подключается к зарядке. Я задумался, что можно же программно узнать — заряжается ноутбук или нет, а потом уже, от скуки, развил эту мысль до небольшой игрушки. В ней игрок выпускает в сторону врагов бомбу, когда ноутбук подключается к зарядке.
«Дум» на языке калькулятора bc (2025)
Концепция вывода графики в текстовую консоль существует давно — в различных терминалах было последовательно придумано несколько способов как это сделать, что меня весьма интриговало. Хотя и понятно, что терминалы нынче никакие не текстовые, всё равно графика там поначалу
Во мне идея сделать
Хотя первые несколько часов хакатона я был в отчаянии от своей идеи и мне казалось, что я либо ничего не сделаю, либо не уложусь в срок, у меня всё получилось — за сутки я сделал порт игры «Wolf5k» на bc и придумал как на этом языке генерировать графику. А заодно доказал, что кодить сутки с раннего утра и до трёх ночи можно и в 47 лет.
«Throttling» игра, использующая для отображения мониторинг загрузки процессора (2025)
Как я недавно рассказывал, эта игра основывается на моей старой идее — выводить пульсацию проигрываемой мелодии через «Мониторинг ресурсов» процессора. В старых «Макбуках» каждому ядру соответствовал свой столбик и я даже написал код,
Недавно я этот код случайно нашёл и придумал небольшую игрушку. Её несложный сюжет, остающийся за рамками игрового процесса, таков: вы оператор блока реактора на АЭС, у вас отключается автоматика и вам нужно вручную удерживать температуру внутри блока, которая отображается как загрузка процессора.
Заключение
Я отобрал всего несколько проектов из того «ненормального», что я делал. Надеюсь, вполне очевидно, что «ненормальное» программирование я ценю и люблю очень давно. Для меня это не просто странное баловство, а способ изучить
Программирование, как например, и рисование, может быть процессом получения как можно более точной копией задачи, просто в другой форме, как, например, в реализме. В целом, нужный и почитаемый навык, но оно может быть и очень абстрактным, отражающим иную реальность, такой, какой она видится внутри, как идея.
Мучительная потребность поделиться своими внутренними идеями, думаю, знакома любому художнику. Я делаю свои странные программы по той же причине — потому что не могу, да и не хочу держать эти идеи только внутри себя.
Тыканье
Мне казалось я об этом уже писал, но поискал и не смог найти, возможно ложная память.
В общем постоянно сталкиваюсь с тем, что у многих людей есть
Вместо «нажимаем», «читаем тут», «выбираем вот это» они людям, с которыми это неприемлемо, говорят «нажимаешь», «читаешь тут», «выбираешь вот это».
Думаю, авторам такого «тыкания» кажется, что это деперсонализированная речь и они так говорят, держа в голове некого пользователя, которым они как будто бы руководят. Но слушатели, уверяю, воспринимают это совершенно иначе — на свой счёт.
Если вы так делаете, подумайте — уместно ли это.
Ускорение операций в bc
Как вы помните, я недавно устроил себе суточный хакатон, писал «Дум» на bc. Получился он тогда довольно медленным, особенно когда на экране было много объектов. Сейчас я его потихоньку оптимизирую, но так как профилирования в bc не существует, двигаюсь наугад.
По сути я до сих пор не знаю, на что там уходит больше всего времени, но иногда выбираю
В стандартной библиотеке bc есть много функций, написанных нативно. Их, как правило, удаётся так или иначе ускорить, поэтому я решил посмотреть что у меня из такого ещё используется. Взор пал на функцию band(), особенно в форме (band(a, b) && 1), то есть там, где всё, что меня интересует — установлен бит или нет. У меня в коде было несколько таких мест.
Написал свой вариант по мотивам того кода, который используется в стандартной библиотеке:
define check_bit(x, y) {
auto result, m[]
result = 0
scale = 0
while (x && y) {
x = divmod(x, 2, m[])
if (m[0]) {
y = divmod(y, 2, m[])
result += m[0]
} else {
y /= 2
}
}
return result
}Получилось быстрее почти в два с половиной раза. Саму операцию band() тоже удалось ускорить в 1,7 раза, в «чистом» виде она у меня в коде тоже есть. Думаю, может сделать коммит в код bc?
Добавлено: коммит сделал. Но беда — автор bc в примечании к репозиторию на «Гитхабе» написал, что с «Гитхаба» он ушёл. К сожалению, то место, куда он ушёл не работает. Так что коммит пришлось залить на «Гитхаб»,
DeepSeek R1
Хочу поделиться. Погоняли китайскую нейросеть «ДипСик Эр1» на оборудовании «Селектела», за что ребятам, как всегда, большое спасибо. Это, конечно, чудо, что на относительно скромном оборудовании можно себе развернуть полноценную большую языковую модель.
На двух nVidia A100 40 GB модель
Завтра попробуем замерить сколько человек может работать с такой моделью параллельно. Но даже уже в этой конфигурации можно проверять
Throttling game
В полку́ странных игр прибыло! Помните я писал игру, где надо управлять человечком, подключая и отключая зарядку «Макбука»? Вчера вечером написал небольшую игру из той же оперы.
Фабула игры такова: у одного из блоков атомной электростанции сработала система предупреждения — все три уровня защиты вырубились, теперь управлять реактором нужно вручную. График загрузки процессора символизирует температуру внутри блока — много плохо, мало — тоже плохо.
Чтобы поиграть в эту игру, надо открыть рядом «Монитор ресурсов» и вывести график загрузки процессоров. Частоту обновления («Вид» → «Частота обновления») надо поставить «Очень часто».
После этого можно запустить игру и, управляя загрузкой процессора через клавишу «Касплок» («капс» включен — процессор загружен), держать загрузку между 10 и 90%. После старта у вас будет четыре секунды, чтобы подготовиться, вывести все нужные окна на передний план и нащупать кнопку.
На идею меня навела бессмертная игра «Посадка на Луну» для калькулятора «Электроника
Код игрушки лежит в моём репозитории.
Ведение сайта
Читатели
Времени трачу обычно немного, но раз на раз не приходится. Как правило это 10—15 минут. Причём самые большие заметки пишутся, чаще всего, «на одном дыхании», а маленькие иногда долго вымучиваются. Больше всего уходит времени на атрибутирование
Пишу где угодно. Чаще всего это утро до работы, когда еду в такси, или то время, когда укладываю дочку. Время публикации при этом может не совпадать со временем написания — бывает, что я откладываю заметку в черновик и возвращаюсь к ней ещё
Пишу, на самом деле, мало. Ведь чаще всего это одна заметка в день, никто же не думает одну мысль в день. Самое главное и важное чаще всего сюда не попадает — просто нет времени их сформулировать. Бывает так же, что некоторые мысли я берегу. От критики прежде всего. Да и так бывает — некоторым мыслям критика только вредит, так как она ещё не оформилась и защитить её нечем. Надо ждать.
В общем, сайт — одна из самых малых моих активностей, но на большее нет времени и мотивации.
Ближе носа
Недавно читал всякие статьи про зрение и опять встретил утверждение, что люди не видят свой нос. Всю жизнь я воспринимал эту фразу как «не замечают». Но недавно подумал — а вдруг есть люди, которые действительно не видят свой нос? Просто
Если вам не лениво, напишите в комментариях здесь или в канале «Телеграма» — можете ли вы заметить свой нос и, если да, то что вам надо для этого сделать — глаза скосить или просто сосредоточиться на нём? Или не видите вовсе, что ни делай?
«Охота на роботов», 1984
Многие годы я мучился от того, что не мог вспомнить в каком же фильме видел сцену
Даже у Артемия Лебедева, когда у него была рубрика «Что за фильм?» в блоге, где люди помогали друг другу вспомнить название по описанию, спрашивал. Но и там не помогли.
Та сцена, как заноза, сидела у меня в мозгу и беспокоила. Не знаю как это всё устроено, но я не мог избавиться от идеи разыскать этот фильм. У меня похожие навязчивые идеи часто бывают.
Попробовал описать каждую деталь, которую я помню, китайской нейросети «ДипСик». Она великолепно справилась даже без поиска по интернету — просто сразу выдала кандидата и на сто процентов попала. Я, по мере ответа, даже начал улыбаться — быстро понял, что попадание верное.
У меня в списке «хочу разыскать» есть ещё два фильма и книга. С книгой вряд ли
Добавлено позднее: рано обрадовался. В остальном не повезло — не нашлись ни фильмы, ни книга.
Субтитры в «шортсах»
У многих авторов «шортсов» на «Ютубе»
Я смотрю со звуком, но не читать не могу — у меня
В общем, иногда там бывает
Улучшение в словаре на bc
За последние недели мой уровень в bc очень сильно вырос. Я стал видеть вещи в своих прежних проектах, которые можно сделать проще и изящнее. Но, кажется, мой рост остановился. Интересно, достиг ли я потолка? Или, может, стал ли я сеньором в этом? Не пора ли мне учить других? Открыть курсы, поднимать сообщество? Вопросы без ответов!
В общем, открыл сегодня, пока ехал в такси, исходник своего
Почему же я
Добавлено: грустно — на скриншоте обнаружил ошибки в словаре, который я использую.
Быстрый корень из суммы квадратов
Я недавно писал как ускорял свой «Дум» для bc. В частности я заменял тригонометрические функции их менее точными, но более быстрыми, аналогами.
Роман Парпалак посоветовал способ ускорения вычисления квадратного корня из суммы квадратов. Аппроксимация там через формулу 0,96 × x + 0,4 × y при x ≥ y. Погрешность должна быть не более 4%.
Выглядит здорово, я замерил — производительность вырастает в восемь раз, заманчиво, но если посмотреть абсолютную погрешность там она почти 5,5 единиц. Это как будто бы очень много. Я даже нарисовал график разницы между корнем и аппроксимацией в «Днунипере».
Заменять я не буду, но знать о такой возможности полезно, спасибо, Роман!
Что там нечётные числа?
Возможно вы уже забыли, но в конце прошлого года я написал небольшую программу, которая искала числа, генерируемые по определённому закону. Рассчитывая быстро упереться в ограничения целого типа в Си, я запускал её на своём ноутбуке, но конца вычислений не дождался.
Тогда я сократил размерность типа вдвое — стал вычислять
Сегодня мне надоело, я этот процесс остановил. Вывод — на одном ядре считать совершенно не вариант, надо дописывать многопоточную версию.
Я, кстати, её даже начинал писать — накидал
Единственное, в чём я признателен нейросеткам, — они решили мою проблему с производительность. Моя многопоточная версия упиралась в блокировку некого разделяемого ресурса и я решил посмотреть справятся ли нейросети с этой проблемой. Справились! В остальном время было потрачено зря.
Надо бы уже показать кованным мешкам кто тут папочка и дописать код самостоятельно.
Увидел икону
У массажистки, к которой иногда хожу,
В иконах я не разбираюсь, да и не особо интересуюсь, так что не сфотографировал, меня заинтересовало
В основании
Вчера ездил ещё раз, уже с лупой. Икону мы снова сняли, попытались разглядеть написанное получше. Позже я посмотрел в интернете что означает каждая буква и цифра.
Год действительно 1859, над ним буквы — И. А, клеймо Артамонова Ивана Семеновича, мастера, который ставил пробу. Работал в Туле, позже был переведён в Ульяновск. Честно сказать, не знал, что у пробиреров были свои собственные клейма.
84 — это легко, золотниковая проба, в те времена мерили ещё имперскими единицами. В переводе на нашу —
Дальше, вроде бы, Георгий Победоносец, пронзающий копьём змея, но не уверен, детализация низкая. Если это так, то икону, вероятно, делали в Москве и инициалы рядом, «АО», расшифровываются как «Алексей Осипов» — был там такой серебряных дел мастер.
В музее Куралова, куда мы ездили с другом в мае, тоже лежат несколько старинных икон. Нам тогда даже, как и многое другое, дали их подержать в руках. Но клейма у них я
Мы в этом году тоже собираемся нагрянуть туда в гости, надо будет поискать клейма на иконах, да и остальные артефакты повертеть повнимательнее.
Обзывалки
Вчера мы с дочкой придумывали рифмованные обзывательства к именам детей в её классе. Учитывая современные имена, это
Началось всё с того, что дочка спросила дразнили ли меня в школе в детстве. Ну кого не дразнили? Меня тоже, о чём я честно и сказал. Дочка расстроено сказала, что её тоже дразнят, даже в школу идти не хочется. Я спросил — что она на это отвечает? Оказалось, что ничего, она не умеет придумывать дразнилки.
В общем, это одно из немногих домашних заданий, которые мы сделали вместе.
А что там с кафе?
Вы, поди, и забыли, что я собирался кафе открывать? Всё ещё собираюсь. С помещением, на которое мы рассчитывали, к сожалению, ничего не получилось, так что быстро не вышло. Мы посмотрели ещё несколько, но пока всё не то. Поэтому, пока суд да дело, открыли небольшой цех, который в будущем будет делать десерты для кафе, а пока что делает их на продажу.
На фото наши десерты (с розовыми ценниками) в кафе «Скуратов», уже второй день можно купить по всему городу.
Из старых закромов
Копался вчера в старых картинках, нашлось.
Позже я сделал скриншот, хотел им поделиться, но он
Doom на bc
Давно хотел повысить градус безумия своих приложений и изобразить
Писать начал, разумеется, на одном из самых далёких от вывода графики на экран языков — bc, я ведь теперь всё на нём пишу
Как вообще устроен вывод графики в текстовый терминал? Надо сказать, что как правило, терминал у нас текстовый число номинально. Да, там выводятся
Обычно мы отсылаем в терминал
ESC ] 1337 ; File = [необязательные параметры] : изображение в формате base64 ^GХорошо, с этим разобрались. Но как нарисовать картинку именно из bc? Тут надо решить несколько очевидных проблем.
BMP — это, как водится во многих старых форматах, внутренне не один формат хранения графики, а несколько. В данном случае я взял самый простой и примитивный — BMP Core с индексированной палитрой из двух цветов.
Его плюс — у него очень простой заголовок и его относительно дёшево формировать. Тем более, что мне не хотелось возиться со сжатием, количество вычислений для этого могло превысить все разумные пределы.
Теперь надо было решить что же я буду писать. На самом деле к этому моменту я уже внутренне определился. В начале двухтысячных локальный фурор произвела игрушка «Wolf5k», написанная на ДжаваСкрипте. Чтобы понять, чем она крута, надо знать, что ДжаваСкрипт тогда графику выводить не умел — не существовало ни тега CANVAS, ни, тем более, WebGL или WebGPU.
Изображение формировалось в текстовом формате XBM, которое, через весьма остроумный хак, показывалось в браузере. На настоящий момент поддержки этого формата давно уже нет ни в одном современном браузере.
Это как будто могло быть очень символично — сделать порт игрушки, которая выводила свою графику на языке, для этого в те годы непригодном, на язык, который так же непригоден для этого сейчас.
К несчастью, исходный код игры обфусцирован и, когда я в самом начале сел за его чтение, был в отчаянии — мне казалось, что за отведённый самому себе суточный срок разобраться не получится. К счастью, раскопки
Не буду описывать все сложности, с которыми пришлось столкнуться, но примерно к полуночи у меня получился код, который умел рендерить картинку покадрово — к тому времени я ещё не решил проблему опроса клавиатуры и мой «игрок» просто крутился на одном месте. В рендере я увидел несколько проблем, но не критичных.
Самой большой проблемой была производительность. Но это я отложил на следующий день — решил, что работающая, но немного глючащая и сильно подтормаживающая игра, написанная за сутки — удовлетворительный результат. До окончания хакатона мне предстояло решить ещё одну проблему — проблему опроса клавиатуры.
«Wolf5k» — динамичная игра, а во всём языке bc единственная функция ввода — read(), которая требует нажатия на «Энтер» после каждого ввода. Такой себе интерактив.
В самом языке это никак не решить, так что, признаюсь, тут я немного смухлевал — написал на zsh запускающую часть, которая сама читает клавиатуру и пересылает нажатия в bc, «нажимая» на «Энтер»:
#!/bin/zsh
x=/*
while {}; do
v=
read -rs -k1 -t0.4 v
case "${v:l}" in
[wasdljkm\ ]) printf "%d\n" "'${v:l}";;
*) echo 0;;
esac
done | bc -g -Ll -f "$0"
exit; */1
/* тут программа на bc */Да, не очень честно, но какой выбор?
К трём ночи у меня была игрушка, в которой всё работало на три с плюсом, особенно, если на экране было не так много объектов. Правда, при поворотах враги иногда пропадали, а иногда взлетали и улетали за пределы экрана. Но в это уже можно было играть.
У меня был соблазн опубликовать её на следующий же день, но хорошо, что я дал коду «отлежаться».
Результат выложил в свой репозиторий. Несмотря на то, что это порт «Wolf5k», игра называется
Ещё о кирпиче с клеймом «.А.»
Одно из моих увлечений — ищу, фотографирую и пытаюсь атрибутировать кирпичные клейма на казанских кирпичах. В последние годы случаев, когда я нахожу новые клейма, всё меньше и меньше, тем не менее, тему я не забрасываю.
В основном, казанские кирпичные клейма — это две буквы инициалов владельца завода с минимумом украшательств. Очень редко встречаются три буквы и только один раз встретилась одна — «А». Не в том смысле, что я встретил её только на одном кирпиче, а просто других одиночных букв на кирпичах Казани не попадалось.
Была попытка атрибутировать это клеймо как завод А. Ф. Амлонга, но версия слабая — непонятно почему почти на всех казанских кирпичах по две буквы, а тут — одна. Ну, может быть, это
Недавно у меня родилась новая версия, опять же, бездоказательная, но в этом направлении можно бы покопать. Я подумал, что «.А.» может быть не инициалами владельца завода, а сокращением названия заведения при котором был завод или для которого делались кирпичи.
Недавно я видел кирпичи с клеймом «.А.» в стенах торгового центра, построенного на месте и из кирпичей снесённых казарм, которые были предназначены для размещения военных полков, передислоцированных в Казань перед Первой мировой войной. Возможно букву «А» стоит искать в названии
Ускорение тригонометрических и прочих функций в bc
Интересно, что вся стандартная библиотека (подключаемая через ключ -l) для bc написана нативно. С одной стороны, это
Я тут потихоньку ускоряю одну из своих программ на bc, но все «низко висящие фрукты» я уже подобрал. Добрался до тригонометрических функций. Во внутренней библиотеке косинус выражается через синус, так что, если ускорить синус, то ускорится сразу и косинус.
Посмотрел разные реализации, в итоге портировал функцию, которую взял из одной из статей на «Хабре», получилось так (используется многочлен Чебышёва):
define mod(a, b) {
auto s, m
s = scale; scale = 0; m = a$ % b$; scale = s; return m
}
define s(x) {
auto sign, xf, per, xx, y
x *= 0.63661977236758134308
if (sign = (x < 0)) x = -x
xf = x$
x -= xf
if (mod(xf, 2)) x = 1 - x
per = mod(xf / 2, 2)
xx = x * x
y = x * (1.5707903005870776 + xx * (-0.6458858977085938 + \
xx*(0.07941798513358536 - 0.0043223880120647346 * xx)))
if (sign != per) return -y
return y
}Расхождение со встроенной в bc реализацией начинается в седьмом знаке, это более чем достаточно для меня, зато вычисление синуса ускорилось больше, чем в четыре раза.
Добавлено позднее: ещё я заменил функцию нахождения логарифма по основанию десять. Логарифмом я находил количество разрядов числа, чтобы потом вывести его на экран, так что его легко было заменить на цикл с подсчётом порядка делением. Разница в производительности — больше, чем в 60 раз. Код теперь выглядит так:
define cnt_digits(x) {
auto i, s
s = scale; scale = 0
for (i = 0; x /= 10; i++){}
scale = s; return i
}Артём Поликарпов
Не знаю как о таком писать. Сегодня я узнал, что несколько лет назад погиб один из моих старых друзей — Артём Поликарпов. Мы знали друг друга больше десяти лет.
Я тогда
Он был интересным, добрым человеком. На дистанции мы мало общались, но если удавалось пересечься
Насколько я помню, в основном мы говорили на профессиональные темы — он был талантливым фронтендером, я даже пытался пригласить его на работу. Иногда говорили про музыку — спустя многие годы я
Очень странно, что его больше нет.
Я долго колебался писать ли