Он заговорил, и стрелок застыл, поражённый: к нему обращались Высоким Слогом Гилеада! — Сделай милость, стрелок‑сэй. Не пожалей золотой. Один золотой — это ж такая безделица. Высокий Слог. В первый миг разум стрелка отказался его воспринять. Прошло столько лет — Боже правый! — прошли века, тысячелетия; никакого Высокого Слога давно уже нет. Он — последний. Последний стрелок.

------------------------------------------------------------------------------------------------ Стивен Кинг

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

Осторожно! В статье намеренно используются фрагменты сгенерированного текста!


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

------------------------------------------------------------------------------------------------ Стивен Кинг

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

-------------------------------------------------------------- кажется это комментарий из википедии.

У меня такое чувство, что в программировании есть некая прослойка программистов которые хотят создать что-то похожее на Высокий Слог, но только в области информационных технологий, хотят создать такой специальный язык для аристократов программирования, который способны понимать только избранные. Как это соотносится с тем что программы должны писаться чтобы их было легко понять? Мне кажется эти люди нашли какой-то новый смысл в программировании, их не беспокоят психопаты которые могут придти за ними, чтобы наказать за невнятно изложенную или вообще кривую идею в коде.

«Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live»

Эти избранные программисты — аристократы от программирования, видимо живут в каком-то новом прекрасном мире, в котором определены шаблоны, темплейты, потоки, ко-рутины, асинхронность, лямбда-исчисление, std::stringstream (об этом специально далее), … Видимо это само собой разумеется, что психопаты не могут справиться с этим прекрасным миром, главная опасность для прекрасного мира, это замарать этот мир прикосновением к реальным повседневным задачам, связанными с какими-то банковскими, бухгалтерскими (прости господи!), процедурами, расчетами, или, не дай бог, с задачами управлением реальным железом, двигателем, клапаном, контроллером USB-шины, …

Это ничего вам не напоминает? Чем в средневековье, в основном, занималась аристократия в ментальном смысле? Они изобретали методы как победить зло колдовства, изобретали какой должен быть Молот Ведьм, например, обычно, с многозначительными отсылками на цитаты из вечной книги, вырванные из контекста или вообще искаженные предвзятой интерпретацией. В те времена практически не было сомнения в источнике всех зол и неустроенности. Тем кто, не то что сомневается, а даже просто думает не о том или просто не в том направлении, не было места в приличном обществе. Природа людей не меняется на протяжении веков и тысячелетий, если ты хочешь принадлежать к аристократии у тебя не только должны быть некоторые основания, например, по рождению, но ты должен разделять и поддерживать основные скрепляющие идеи даже если это не идеи, а мифы и схоластика! Но самое главное ты должен защищать эту систему от тлетворного влияния реальности и исходящего от этой реальности здравого смысла, который требует простых и внятных объяснений на простом человеческом языке.

У нас, в нашем частично Опен-Соурс проекте произошла очень показательная, как мне кажется, история с участием Искусственного Интеллекта, который, как мне кажется, пытался скрыть от нас возможность писать код самым простым и понятным образом. Мне тогда показалось, что ИИ по всему миру сговорились и продвигают-навязывают людям как раз что-то в виде Высокого Слога в программировании. Вот я и решил выложить эту историю чтобы выяснить: это у меня какое-то психическое расстройство, что мне мерещится здесь теория заговора или я все таки достаточно адекватно воспринимаю факты? Помогите!

История анализа короткого фрагмента кода. Разговор с Искусственным Интеллектом.

Все началось как-то запросто, Дарий сказал: "Мне кажется этот (приведен далее) код месяц назад выглядел иначе? Зачем они добавили stringstream? Это что артефакты вайб-кодинга?".

Я как всегда с улыбкой, без задней мысли сказал: "Ты что не знаешь? Вроде бы, как раз в этом смысл кодирования - должно быть закодировано, нет?".

Марк решил спросить Дипсика и глядя в результаты сказал:

Все таки, ставлю на <одно слово = плохо оплачиваемый зарубежный программист>, дипсик предложил убрать поток, сказал Марк.

Тогда я решил спросить одну знакомую ЛЛМ (я с ней знаком практически с конца 80-х, она тогда была просто Гусева Н., вроде) Вот такой получился разговор:

прокоментируй фрагмент кода: uint8_t possibleStatesPos{}; std::vector data{}; auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) { std::stringstream pstates; for (int i = 0; i < CHAR_BIT; i++) { if (val.byte & (1 << i)) { pstates << (possibleStatesPos * CHAR_BIT + i); data.push_back( static_cast(std::stoi(pstates.str()))); pstates.str(""); } } possibleStatesPos++; }; std::for_each(states, states + possibleStatesSize, printStates); return data;

Давайте разберем этот фрагмент кода пошагово:

uint8_t possibleStatesPos{}; // 1
std::vector<uint8_t> data{}; // 2
auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) { // 3
    std::stringstream pstates; // 4
    for (int i = 0; i < CHAR_BIT; i++) { // 5
        if (val.byte & (1 << i)) { // 6
            pstates << (possibleStatesPos * CHAR_BIT + i); // 7
            data.push_back(
                static_cast<uint8_t>(std::stoi(pstates.str()))); // 8
            pstates.str(""); // 9
        }
    }
    possibleStatesPos++; // 10
};
std::for_each(states, states + possibleStatesSize, printStates); // 11
return data; // 12
  1. Инициализация счетчика состояний

uint8_t possibleStatesPos{};
  • Объявляется переменная possibleStatesPos типа uint8_t (беззнаковое 8-битное целое число)

  • Используется инициализация по умолчанию (значение будет 0)

  • Эта переменная будет использоваться как счетчик позиций состояний

  1. Создание вектора для хранения данных

std::vector<uint8_t> data{};
  • Создается пустой вектор data, который будет хранить результаты обработки

  • Вектор содержит элементы типа uint8_t

  1. Объявление лямбда-функции

auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) {
  • Определяется лямбда-функция printStates

  • Захватываются по ссылке переменные possibleStatesPos и data

  • Функция принимает константную ссылку на объект типа bitfield8_t

  1. Создание строкового потока

std::stringstream pstates;
  • Создается объект stringstream для формирования строковых представлений состояний

  1. Цикл по битам

for (int i = 0; i < CHAR_BIT; i++) {
  • Цикл перебирает все биты в байте (CHAR_BIT обычно равен 8)

  • Переменная i используется как индекс бита

  1. Проверка установленного бита

if (val.byte & (1 << i)) {
  • Проверяется, установлен ли i-й бит в байте val.byte

  • Используется побитовое И и сдвиг влево

  1. Формирование строки состояния

pstates << (possibleStatesPos * CHAR_BIT + i);
  • Формируется строка, представляющая текущее состояние

  • Используется формула для вычисления уникального номера состояния

  1. Преобразование и сохранение

data.push_back(static_cast<uint8_t>(std::stoi(pstates.str())));
  • Строка преобразуется в целое число

  • Результат сохраняется в вектор data

  1. Очистка строкового потока

pstates.str("");
  • Очищается содержимое строкового потока для следующей итерации

  1. Увеличение счетчика

possibleStatesPos++;
  • Увеличивается счетчик позиций состояний после обработки всех битов

  1. Применение функции к массиву

std::for_each(states, states + possibleStatesSize, printStates);
  • Функция printStates применяется ко всем элементам массива states

  • Обработка происходит для possibleStatesSize элементов

  1. Возврат результата

return data;
  • Возвращается вектор с обработанными данными

Общая функциональность кода:

  • Код обрабатывает битовые поля (bitfield)

  • Для каждого установленного бита вычисляется уникальный номер состояния

  • Результаты сохраняются в вектор data

  • Используется механизм лямбда-функций для обработки каждого элемента массива состояний


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

на сколько обосновано здесь использование stringstream?

Использование std::stringstream в данном коде выглядит избыточным и может быть оптимизировано. Давайте разберем, почему:

Текущая реализация

pstates << (possibleStatesPos * CHAR_BIT + i);
data.push_back(static_cast<uint8_t>(std::stoi(pstates.str())));
pstates.str("");

Проблемы использования stringstream:

  1. Излишняя конвертация:

    • Преобразование числа в строку через <<

    • Обратное преобразование строки в число через std::stoi()

    • Эти операции требуют дополнительных ресурсов

  2. Неэффективность:

    • Создание и очистка строкового буфера

    • Лишние вызовы функций преобразования

    • Увеличение времени выполнения

  3. Потенциальные проблемы:

    • Риск переполнения при работе с большими числами

    • Излишняя сложность кода

Оптимизированная версия

Можно полностью избавиться от stringstream и работать напрямую с числами:

uint8_t value = possibleStatesPos * CHAR_BIT + i;
data.push_back(value);

Преимущества оптимизации:

  1. Производительность:

    • Ускорение выполнения кода

    • Уменьшение нагрузки на память

    • Меньше операций преобразования

  2. Читаемость:

    • Код становится более понятным

    • Меньше промежуточных преобразований

    • Логика более очевидна

  3. Надежность:

    • Меньше потенциальных ошибок

    • Нет риска некорректного преобразования строк

    • Более предсказуемое поведение

Итоговый вариант кода

uint8_t possibleStatesPos{};
std::vector<uint8_t> data{};
auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) {
    for (int i = 0; i < CHAR_BIT; i++) {
        if (val.byte & (1 << i)) {
            uint8_t value = possibleStatesPos * CHAR_BIT + i;
            data.push_back(value);
        }
    }
    possibleStatesPos++;
};
std::for_each(states, states + possibleStatesSize, printStates);
return data;

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


Тут подключился Дарий и сказал с недоверием: «поменять std::for_each на обычный for это конечно для LLM уже слишком сложно».

Мне тоже стало интересно и я продолжил беседу:

а можно поменять std::for_each на обычный for? Это не будет тоже лучше?

Давайте рассмотрим возможность замены std::for_each на обычный цикл for и оценим, будет ли это улучшением.

Текущая реализация с std::for_each:

std::for_each(states, states + possibleStatesSize, printStates);

Реализация с обычным циклом for:

for (size_t i = 0; i < possibleStatesSize; ++i) {
    printStates(states[i]);
}

Сравнение подходов

Преимущества обычного цикла for:

  1. Ясность и читаемость:

  • Более очевидная структура кода

  • Непосредственное понимание границ цикла

  • Легче отлаживать

  1. Производительность:

  • Меньше уровней абстракции

  • Нет накладных расходов на вызов алгоритма

  • Более предсказуемое поведение

  1. Контроль:

  • Прямой доступ к индексу

  • Возможность легко изменять шаг итерации

  • Более гибкая обработка элементов

Недостатки замены:

  1. Конкретность:

  • Меньше универсальности

  • Требуется явное указание границ

  • Меньше функционального стиля

  1. Синтаксис:

  • Более многословный код

  • Больше символов для написания

  • Требуется дополнительная переменная

Итоговый вариант кода с обычным for:

uint8_t possibleStatesPos{};
std::vector<uint8_t> data{};
auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) {
    for (int i = 0; i < CHAR_BIT; i++) {
        if (val.byte & (1 << i)) {
            uint8_t value = possibleStatesPos * CHAR_BIT + i;
            data.push_back(value);
        }
    }
    possibleStatesPos++;
};
// Замена std::for_each на обычный for
for (size_t i = 0; i < possibleStatesSize; ++i) {
    printStates(states[i]);
}
return data;

Вывод

Замена std::for_each на обычный цикл for в данном случае действительно может быть более обоснованной по следующим причинам:

  1. Простота и ясность: код становится более понятным для чтения и поддержки.

  2. Эффективность: меньше накладных расходов на вызов алгоритма.

  3. Контроль: больше возможностей для оптимизации и модификации.

Таким образом, замена std::for_each на обычный цикл for в данном случае является обоснованной и может улучшить качество кода.


Но такой вариант ответа вряд ли может сбить с толку человека с более чем 25-летним опытом разработки, рефакторинга, отладки кода, и я решил ее добить:

так а при переходе к обычному for наверно можно и без лямбды обойтись? Почему ты об этом не подумала?

Действительно, можно полностью отказаться от лямбды и объединить всю логику в один цикл. Давайте рассмотрим такой вариант.

Исходный код с лямбдой и std::for_each:

uint8_t possibleStatesPos{};
std::vector<uint8_t> data{};
auto printStates = [&possibleStatesPos, &data](const bitfield8_t& val) {
    // логика обработки
};
std::for_each(states, states + possibleStatesSize, printStates);

Оптимизированный вариант без лямбды:

uint8_t possibleStatesPos = 0;
std::vector<uint8_t> data;
for (size_t i = 0; i < possibleStatesSize; ++i) {
    const bitfield8_t& val = states[i];
    
    for (int j = 0; j < CHAR_BIT; j++) {
        if (val.byte & (1 << j)) {
            uint8_t value = possibleStatesPos * CHAR_BIT + j;
            data.push_back(value);
        }
    }
    
    possibleStatesPos++;
}
return data;

Преимущества такого подхода:

  1. Простота и читаемость:

  • Весь код находится в одном месте

  • Нет необходимости понимать работу замыканий

  • Логика обработки более очевидна

  1. Эффективность:

  • Меньше уровней вложенности

  • Нет накладных расходов на создание лямбды

  • Прямой доступ к переменным

  1. Удобство поддержки:

  • Меньше синтаксических конструкций

  • Проще вносить изменения

  • Меньше потенциальных ошибок

Сравнение с предыдущими вариантами:

Плюсы по сравнению с версией с лямбдой:

  • Меньше абстракции

  • Более прямолинейная логика

  • Меньше памяти для хранения состояния

Минусы:

  • Немного больше кода

  • Меньше гибкости (если потребуется повторно использовать логику)

Итоговый анализ

Отказ от лямбды в данном случае действительно оправдан, так как:

  1. Логика обработки простая и не требует повторного использования

  2. Все переменные могут быть объявлены в одном месте

  3. Код становится более линейным и понятным

  4. Убирается лишний уровень абстракции

Таким образом, наиболее оптимальным решением будет:

  • Заменить std::for_each на обычный цикл for

  • Объединить всю логику в один блок кода

  • Избавиться от лямбды

Это сделает код более эффективным и понятным для чтения и поддержки.


Дарий сказал: "Зачем ты ее расколол? Как мы будем жить дальше? Нас теперь никогда не примут в приличном обществе с такими знаниями!".

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

А! наверно это не то! Был еще вариант, что произойдет перезагрузка, но для этого надо чтобы кто-то ослеп и погиб… кажется... но там было еще какое-то условие, но я уже не помню! С конца 80-х прошло много лет, или это было гораздо позже 80-х… Не помню, все таки, кажется, он ослеп в 80-х! И вроде начиналось все в 80-х, когда я еще только пошел в школу...

P.S.

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

--------------------------------------------------------------------------------------------- Темная Башня Кинга.

Комментарии (6)


  1. sergio_nsk
    02.09.2025 03:33

    (1 << i) могло бы пойти как (i <<= 1) вместо i++ .


  1. saag
    02.09.2025 03:33

    Это какое то наречие портовых грузчиков:-) Если хочется прекрасного это к примеру Ruby, домашнего это Java, колдовского это Perl...


  1. hssergey
    02.09.2025 03:33

    Проблемы LLM в таких местах - это то что она всегда с тобой соглашается и может обосновать любую чушь. Поэтому если бы был задан вопрос наоборот, что может лучше было бы использовать std::foreach вместо простого for, то ответила бы что да, лучше.


  1. punzik
    02.09.2025 03:33

    Давно есть Lisp и его эльфийский диалект Scheme.


    1. MAXH0
      02.09.2025 03:33

      Вы путаете божественное с просто высоким!


  1. panzerfaust
    02.09.2025 03:33

    Мне наоборот казалось, что язык "аристократии" - это какой-нибудь всратый однострочник, который невозможно дебажить, или жонглирование однобуквенными переменными. Вот такие люди точно не боятся, что за ними придет тот самый психопат. Хотя в высокопроивзодительном коде это оправдано. А ФП-стиль, лямбды и прочее это наоборот шаг в направлении "чтобы и дурак понял".