Выше дан код C++. Какой вывод будет у программы?
Anonymous Quiz
18%
(1, 1) (2, 2)
71%
(1, 2) (2, 4)
12%
(1, 2) (3, 4)
0%
(1, 2) (3, 6)
Синтаксис или семантика
Синтаксис — это правила языка, его орфография.
Семантика — это смысл кода, его логика.
Если написать одну и ту же программу на разных языках программирования, то синтаксически код будет разным, но семантически - одинаковым.
Оба термина можно встретить в контексте статического анализа кода. Анализаторы, благодаря строгим правилам языка, легко находят синтаксические ошибки. Сематические ошибки найти сложнее. Например, здесь в C++:
🔼 Синтаксически вопросов нет. Но по смыслу это операция присваивания, а не сравнения. Но может быть программист как раз и хотел присваивать? Не понятно. И в этом сложность.
—
Ранее в сериале #Словарь 👉 Эмулятор или симулятор
Синтаксис — это правила языка, его орфография.
Семантика — это смысл кода, его логика.
Если написать одну и ту же программу на разных языках программирования, то синтаксически код будет разным, но семантически - одинаковым.
Оба термина можно встретить в контексте статического анализа кода. Анализаторы, благодаря строгим правилам языка, легко находят синтаксические ошибки. Сематические ошибки найти сложнее. Например, здесь в C++:
if ( x = 5 )
// ...
🔼 Синтаксически вопросов нет. Но по смыслу это операция присваивания, а не сравнения. Но может быть программист как раз и хотел присваивать? Не понятно. И в этом сложность.
—
Ранее в сериале #Словарь 👉 Эмулятор или симулятор
👍2
Почему в CLI есть короткие и длинные ключи
При работе с командной строкой вы наверняка замечали, что у многих опций есть два формата:
- Короткий (например,
- Длинный (например,
🔸 Зачем это нужно?
Очевидно, что короткие ключи (
Длинные ключи (
Пример:
🔸 Тогда какой вариант использовать?
Короткие ключи используем при непосредственной работе с командной строкой. Здесь важна скорость и удобство.
Длинные ключи используем в скриптах. Скрипты, как и другой код, мы чаще читаем, чем пишем. Поэтому лучше, чтобы наша "писанина" была понятной.
—
В тему CLI еще можно вспомнить про путаницу в командах git.
При работе с командной строкой вы наверняка замечали, что у многих опций есть два формата:
- Короткий (например,
-h
) - Длинный (например,
--help
) 🔸 Зачем это нужно?
Очевидно, что короткие ключи (
-h
, -v
) обеспечивают быстрый ввод. При частом использовании самое то.Длинные ключи (
--help
, --version
) уже быстро не введешь, но они более понятные и читаемые.Пример:
# Короткая запись
ls -l
# Длинная запись
ls --format=long
🔸 Тогда какой вариант использовать?
Короткие ключи используем при непосредственной работе с командной строкой. Здесь важна скорость и удобство.
Длинные ключи используем в скриптах. Скрипты, как и другой код, мы чаще читаем, чем пишем. Поэтому лучше, чтобы наша "писанина" была понятной.
—
В тему CLI еще можно вспомнить про путаницу в командах git.
❤3
А Филосо́ф - Без огурцов
У Крылова есть басня "Философ и огородник", где мужик-огородник сажает огурцы, а философ-огородник над ним смеется, мол, не по науке всё. Но пока философ читает книжки, думает, прикидывает и вымеряет, огородник - работает на земле. В итоге "у огородника взошло всё и поспело", "а философ - без огурцов".
🔽
Не знаю как вы, но я постоянно балансирую между "философом" и "огородником". Хочется посидеть, подумать как лучше, чтобы раз - и готово. Но опыт говорит: "Делай!"
Фишка в том, что разработка - это сложно. Редко получается учесть всё сразу. Да и получается ли вообще? Нет, позитивный сценарий возможен, если текущая задача очень похожа на какую-то из предыдущих. Но так бывает не всегда. А в инновациях - практически никогда. Поэтому приходится думать, планомерно превращаясь в "философа" и рискуя "огурцами".
Конечно, "философ" и "огородник" - это крайности. А в нашем интеллектуальном труде нужен баланс.
💡 Зная о своей склонности пофилософствовать, я стараюсь балансировать, придерживаясь цикла: "подумал - сделал - оценил". И чем короче цикл, тем лучше.
Да, после оценки будут коррекции. Да, какие-то идеи не прокатят. Да, из-за этого будет казаться, что если бы подольше подумал, то получше бы придумал...🙄 Не придумал бы. Лучшая идея как раз и пришла в голову от того, что предыдущую ты уже попробовал и оценил.
Поэтому когда в очередной раз повиснете в мыслях над своей лучшей задачей, вспомните про "филосо́фа - без огурцов" и спокойно переходите к делу 😉
—
Текст басни оставил в комментах. Палец вверх тому, кто найдет строки про навязчивое желание переписать проект на новомодную технологию!
#КнижнаяПолка
У Крылова есть басня "Философ и огородник", где мужик-огородник сажает огурцы, а философ-огородник над ним смеется, мол, не по науке всё. Но пока философ читает книжки, думает, прикидывает и вымеряет, огородник - работает на земле. В итоге "у огородника взошло всё и поспело", "а философ - без огурцов".
🔽
Не знаю как вы, но я постоянно балансирую между "философом" и "огородником". Хочется посидеть, подумать как лучше, чтобы раз - и готово. Но опыт говорит: "Делай!"
Фишка в том, что разработка - это сложно. Редко получается учесть всё сразу. Да и получается ли вообще? Нет, позитивный сценарий возможен, если текущая задача очень похожа на какую-то из предыдущих. Но так бывает не всегда. А в инновациях - практически никогда. Поэтому приходится думать, планомерно превращаясь в "философа" и рискуя "огурцами".
Конечно, "философ" и "огородник" - это крайности. А в нашем интеллектуальном труде нужен баланс.
Да, после оценки будут коррекции. Да, какие-то идеи не прокатят. Да, из-за этого будет казаться, что если бы подольше подумал, то получше бы придумал...
Поэтому когда в очередной раз повиснете в мыслях над своей лучшей задачей, вспомните про "филосо́фа - без огурцов" и спокойно переходите к делу 😉
—
Текст басни оставил в комментах. Палец вверх тому, кто найдет строки про навязчивое желание переписать проект на новомодную технологию!
#КнижнаяПолка
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
Исходный код:
Аргументы командной строки:
#include <iostream>
#include <string>
int main(int argc, char* argv[])
{
std::string words[argc];
for (int i = 1; i < argc; ++i)
words[i - 1] = std::string(argv[i]);
for (int i = 0; i < (argc - 1); ++i)
std::cout << words[i] << " ";
return 0;
}
Аргументы командной строки:
hello world
Выше дан код C++. Какой вывод будет у программы?
Anonymous Quiz
40%
hello world
5%
world
10%
hello
45%
Зависит от реализации компилятора
Почему я не люблю auto в C++
Когда мы переходили на стандарт C++11, я еще тогда воротил нос от
C++ — язык со статической типизацией. Это значит, что типы известны на этапе компиляции, и это даёт предсказуемость и удобство чтения.
Ну например. В этом коде понятно, что мы имеем дело с вектором целых чисел:
А здесь у нас что?
🔸 Можете, конечно, возразить: "Есть ситуации, где тип реально не важен".
И я вам скажу: "Да, есть. Когда вы только-только пишете код". Но когда вы будете его читать через лет, эдак, полгода, то на момент зависните, пытаясь в уме разыменовать этот
Да что в уме? Даже среда разработки иной раз не может подсветить методы у объекта, так как не соображает, что там за тип внутри)
И если так классно не знать тип, то зачем в языках с динамической типизацией городить огороды из аннотаций типов? Ответ есть. Потому что это удобно, читаемо и безопасно.
🔸 Вы снова возразите: "А как же сложные типы? Шаблоны и прочие бутерброды".
И я вам скажу: "Вы снова правы". Ну очень не хочется писать этот длиннющий
Как видите, лично у меня
А у вас какие с
👍 - дружу
🔥 - скорее так, знакомые
😁 - ох уж этот ваш C++
Когда мы переходили на стандарт C++11, я еще тогда воротил нос от
auto
. Было не понятно, зачем он мне. Какой смысл?C++ — язык со статической типизацией. Это значит, что типы известны на этапе компиляции, и это даёт предсказуемость и удобство чтения.
Ну например. В этом коде понятно, что мы имеем дело с вектором целых чисел:
std::vector<int> numbers = {1, 2, 3};
А здесь у нас что?
vector
, list
, initializer_list
или еще чего-нибудь? auto numbers = {1, 2, 3};
🔸 Можете, конечно, возразить: "Есть ситуации, где тип реально не важен".
И я вам скажу: "Да, есть. Когда вы только-только пишете код". Но когда вы будете его читать через лет, эдак, полгода, то на момент зависните, пытаясь в уме разыменовать этот
auto
.Да что в уме? Даже среда разработки иной раз не может подсветить методы у объекта, так как не соображает, что там за тип внутри)
И если так классно не знать тип, то зачем в языках с динамической типизацией городить огороды из аннотаций типов? Ответ есть. Потому что это удобно, читаемо и безопасно.
🔸 Вы снова возразите: "А как же сложные типы? Шаблоны и прочие бутерброды".
И я вам скажу: "Вы снова правы". Ну очень не хочется писать этот длиннющий
std::unordered_map<MySpace::MyType>::iterator
. Набрал auto
и все дела. Я и сам так делаю. Но потом, перед коммитом, скорее всего заменю auto
если не на оригинальный тип, то на какой-нибудь более осмысленный алиас. Если, конечно, не забуду 🤭Как видите, лично у меня
auto
не прижился. С ним так много вопросов и так мало ответов. А у вас какие с
auto
отношения? Дружите?👍 - дружу
🔥 - скорее так, знакомые
😁 - ох уж этот ваш C++
👍3😁2🔥1
Выше дан код C++. Какой вывод будет у программы?
Anonymous Quiz
32%
1 4 5 7 9
32%
1 5 7 9
16%
1 4 7 9
21%
Неопределенное поведение
Как не утонуть в правках. Git-стратегия для сложных задач с риском откатов
Ситуация: пилите большую фичу, написали много кода и вдруг замечаете, что сломали то, что час назад работало. Что случилось, почему? Открываете git diff, а там правки не за последний час, а за последний день... Где была допущена ошибка? Начинаем вспоминать, запускаем отладчик и забываем про текущую работу.
🔸 Как не доводить до такой ситуации?
Правильно - чаще коммитить. Но как коммитить то, что еще не готово? Коммит же должен быть завершенным!
Решение есть у нас: промежуточные коммиты.
Мы просто коммитим все, что хотим и когда хотим, а по окончании работ "схлопываем" промежуточные коммиты в один, используя interactive rebase.
Промежуточные коммиты оформляются с префиксом WIP (от Work In Progress), чтобы потом их не пропустить. В git log получается примерно так:
🔸 Когда делать промежуточный коммит?
Я руководствуюсь общим правилом: если мой следующий шаг приведет к правкам незакоммиченного кода, я делаю промежуточный коммит. У меня должна быть возможность быстро понять, где допущена ошибка, и откатиться.
Обычно такие коммиты случаются:
- при фиксировании логически завершенных правок,
- перед любым рефакторингом,
- в конце дня.
А чтобы вам проще было разобраться с подходом, я записал небольшое практическое видео. На простом примере показал как промежуточные коммиты помогают локализовать ошибку и как потом "схлопнуть" эти коммиты в один через interactive rebase. Ссылки здесь ⤵️
📱 Смотреть на YouTube
📱 Смотреть во ВКонтакте
#Troubleshooting
Ситуация: пилите большую фичу, написали много кода и вдруг замечаете, что сломали то, что час назад работало. Что случилось, почему? Открываете git diff, а там правки не за последний час, а за последний день... Где была допущена ошибка? Начинаем вспоминать, запускаем отладчик и забываем про текущую работу.
🔸 Как не доводить до такой ситуации?
Правильно - чаще коммитить. Но как коммитить то, что еще не готово? Коммит же должен быть завершенным!
Решение есть у нас: промежуточные коммиты.
Мы просто коммитим все, что хотим и когда хотим, а по окончании работ "схлопываем" промежуточные коммиты в один, используя interactive rebase.
Промежуточные коммиты оформляются с префиксом WIP (от Work In Progress), чтобы потом их не пропустить. В git log получается примерно так:
WIP: что-то сделал
WIP: еще что-то сделал
WIP: убрал кое-чего
WIP: зарефакторил то-то
🔸 Когда делать промежуточный коммит?
Я руководствуюсь общим правилом: если мой следующий шаг приведет к правкам незакоммиченного кода, я делаю промежуточный коммит. У меня должна быть возможность быстро понять, где допущена ошибка, и откатиться.
Обычно такие коммиты случаются:
- при фиксировании логически завершенных правок,
- перед любым рефакторингом,
- в конце дня.
А чтобы вам проще было разобраться с подходом, я записал небольшое практическое видео. На простом примере показал как промежуточные коммиты помогают локализовать ошибку и как потом "схлопнуть" эти коммиты в один через interactive rebase. Ссылки здесь ⤵️
#Troubleshooting
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
YouTube Analyzer 5.0
Мы выпустили новую версию YouTube Analyzer - анализатора поисковой выдачи YouTube на Python / PySide6 (Qt).
Из интересного в коде по сравнению с предыдущей версией:
🔜 Добавлены рабочие пространства. Теперь можно открыть неограниченное количество пространств во вкладках и работать одновременно с поиском, трендами и пр. Под капотом стандартные QTabWidget.
🔜 Реализована система плагинирования. Разные типы рабочих вкладок могут быть загружены из плагинов. Например, сейчас так работает autocomplete_plugin.
🔜 Добавлена галерея превью (на скрине). Ютуб-менеджеры используют ее для выявления трендов в превью видео. Реализовано на QListView с включенным режимом
—
YouTube Analyzer - это desktop-приложение, написанное на Python / PySide (Qt).
Его решения можно использовать как шаблон для своих приложений на Python / PySide или на C++ / Qt.
Мы выпустили новую версию YouTube Analyzer - анализатора поисковой выдачи YouTube на Python / PySide6 (Qt).
Из интересного в коде по сравнению с предыдущей версией:
QListView.ViewMode.IconMode
.—
YouTube Analyzer - это desktop-приложение, написанное на Python / PySide (Qt).
Его решения можно использовать как шаблон для своих приложений на Python / PySide или на C++ / Qt.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
#include <iostream>
void foo(int a, int b) {
std::cout << a << " " << b;
}
int main() {
int count = 0;
foo(++count, ++count);
return 0;
}
Выше дан код C++. Какой вывод будет у программы?
Anonymous Quiz
70%
1 2
5%
2 1
5%
0 1
20%
Неопределенное поведение
🤯2
Корректное или принудительное завершение работы
Сегодня речь пойдет о завершении работы программы.
Корректное завершение (graceful shutdown) — это когда система закрывается правильно. То есть сохраняет все данные, освобождает ресурсы, завершает процессы в правильном порядке.
Принудительное завершение (hard shutdown) — это резкое закрытие процесса, минуя стандартную процедуру. Словно вызвали "
На практике не стоит забывать, что программа может завершиться неправильно. Но повторный запуск при этом не должен быть нарушен. Может остаться какой-то мусор, незакрытые соединения, битые файлы. Нам это все нужно "переварить", корректно запуститься и восстановить целостность данных.
Вот так все просто на словах 😊
—
Ранее в сериале #Словарь : Синтаксис или семантика
Сегодня речь пойдет о завершении работы программы.
Корректное завершение (graceful shutdown) — это когда система закрывается правильно. То есть сохраняет все данные, освобождает ресурсы, завершает процессы в правильном порядке.
Принудительное завершение (hard shutdown) — это резкое закрытие процесса, минуя стандартную процедуру. Словно вызвали "
kill -9
" или выдернули шнур из розетки.На практике не стоит забывать, что программа может завершиться неправильно. Но повторный запуск при этом не должен быть нарушен. Может остаться какой-то мусор, незакрытые соединения, битые файлы. Нам это все нужно "переварить", корректно запуститься и восстановить целостность данных.
Вот так все просто на словах 😊
—
Ранее в сериале #Словарь : Синтаксис или семантика
🔥2
Luanti. Хранение карты в опенсорсном Minecraft
Luanti - это опенсорсный аналог Minecraft, написанный на C++. Мощный проект, к которому можно заглянуть под капот ⤵️
Мне в первую очередь было интересно как хранятся данные мира. Ведь мир свободный и большой. Казалось, простых решений тут быть не может. Но на самом деле решение довольно элегантное.
🔸 Какая структура данных?
В Luanti данные мира хранятся в единственной базе данных SQLite в единственной таблице blocks:
Каждая строка таблицы - это чанк (множество блоков) размером 16х16х16 блоков. Информация о состоянии каждого из 4096 блоков чанка хранится в специальной структуре в поле
🔸 Как и когда данные записываются в базу?
Мир в Luanti генерируется динамически по мере его исследования игроком. Каждый сгенерированный чанк сразу же сохраняется в базу данных. Это нужно для поддержания целостности мира, чтобы при следующей загрузке он выглядел так же. При изменении мира игроком сохранение в базу данных происходит с задержкой через определенный интервал времени. Оптимизация, куда без неё 🙂
🔸 Как и когда данные читаются из базы?
Чтение мира из базы данных тоже выполняется чанками. Если чанк существует в базе, то он загружается, если нет - генерируется. При движении игрока по миру чанки автоматически подгружаются или генерируются.
🔸 Изменение структуры данных в 2025 году
Начиная с версии 5.12, координаты чанков хранятся в отдельных полях
Свое решение разработчики объясняют упрощением запроса чанков по координатам, а также уходом от сложной логики свертки координат. Кому интересно, вот этот пулл-реквест.
—
В перспективе сделаю еще посты с занимательными решениями из опенсорса. Предыдущий был про условное логгирование в quagga (почти 2 года назад 😳)
Luanti - это опенсорсный аналог Minecraft, написанный на C++. Мощный проект, к которому можно заглянуть под капот ⤵️
Кто не в курсе, Minecraft (и Luanti) - это компьютерная игра с открытым миром из кубических блоков, где можно свободно строить свои миры.
Мне в первую очередь было интересно как хранятся данные мира. Ведь мир свободный и большой. Казалось, простых решений тут быть не может. Но на самом деле решение довольно элегантное.
🔸 Какая структура данных?
В Luanti данные мира хранятся в единственной базе данных SQLite в единственной таблице blocks:
CREATE TABLE "blocks" (
"pos" INT NOT NULL PRIMARY KEY,
"data" BLOB);
Каждая строка таблицы - это чанк (множество блоков) размером 16х16х16 блоков. Информация о состоянии каждого из 4096 блоков чанка хранится в специальной структуре в поле
data
. А поле pos
хранит свёртку координат x, y, z, определяющих положение данного чанка в пространстве.🔸 Как и когда данные записываются в базу?
Мир в Luanti генерируется динамически по мере его исследования игроком. Каждый сгенерированный чанк сразу же сохраняется в базу данных. Это нужно для поддержания целостности мира, чтобы при следующей загрузке он выглядел так же. При изменении мира игроком сохранение в базу данных происходит с задержкой через определенный интервал времени. Оптимизация, куда без неё 🙂
🔸 Как и когда данные читаются из базы?
Чтение мира из базы данных тоже выполняется чанками. Если чанк существует в базе, то он загружается, если нет - генерируется. При движении игрока по миру чанки автоматически подгружаются или генерируются.
🔸 Изменение структуры данных в 2025 году
Начиная с версии 5.12, координаты чанков хранятся в отдельных полях
x
, y
, z
таблицы blocks
. От свертки координат pos
отказались:CREATE TABLE "blocks" (
"x" INTEGER, "y" INTEGER, "z" INTEGER,
"data" BLOB NOT NULL,
PRIMARY KEY ("x", "z", "y")
);
Свое решение разработчики объясняют упрощением запроса чанков по координатам, а также уходом от сложной логики свертки координат. Кому интересно, вот этот пулл-реквест.
—
В перспективе сделаю еще посты с занимательными решениями из опенсорса. Предыдущий был про условное логгирование в quagga (почти 2 года назад 😳)
🔥2👏1
#include <iostream>
int main() {
constexpr int a = 3 - 2;
switch (a) {
case 1:
std::cout << "1";
case 2:
std::cout << "2";
break;
case 3:
std::cout << "3";
break;
default:
std::cout << "0";
}
return 0;
}
Без права на ошибку
Жутко не люблю сверлить отверстия в стенах. Просверлил не там, ошибся, и уже не отменишь.
Недавно мне нужно было повесить тумбочку на стене в определенном месте. Дело осложнялось потайными креплениями. Нужно было точно вымерить место креплений, просверлить отверстия, установить крепеж и только после этого повесить тумбочку.
Вот уж воистину "семь раз отмерь, один раз отрежь".
Ошибаться нельзя: стены чистовые, на виду. Мне повезло, я не ошибся. Но дискомфорт, который возник при этом, меня удивил.
🔽
Получается, что работа программиста приучила меня к мысли, что ошибаться можно? Да, ошибаться можно - исправим. Не сработало - починим. Все можно отменить, все можно быстро переделать.
Конечно, я говорю о коде, о низком уровне. Гораздо "больнее" ошибаться в проекте, в задании, в требованиях, в цели. Чем выше уровень, тем больше это похоже на сверление отверстия в стене.
Да, и неправильное отверстие можно заделать, и требования исправить. Только какой ценой?
Но как же хорошо иметь право на ошибку! Как же хорошо!
Жутко не люблю сверлить отверстия в стенах. Просверлил не там, ошибся, и уже не отменишь.
Недавно мне нужно было повесить тумбочку на стене в определенном месте. Дело осложнялось потайными креплениями. Нужно было точно вымерить место креплений, просверлить отверстия, установить крепеж и только после этого повесить тумбочку.
Вот уж воистину "семь раз отмерь, один раз отрежь".
Ошибаться нельзя: стены чистовые, на виду. Мне повезло, я не ошибся. Но дискомфорт, который возник при этом, меня удивил.
🔽
Получается, что работа программиста приучила меня к мысли, что ошибаться можно? Да, ошибаться можно - исправим. Не сработало - починим. Все можно отменить, все можно быстро переделать.
Конечно, я говорю о коде, о низком уровне. Гораздо "больнее" ошибаться в проекте, в задании, в требованиях, в цели. Чем выше уровень, тем больше это похоже на сверление отверстия в стене.
Да, и неправильное отверстие можно заделать, и требования исправить. Только какой ценой?
Но как же хорошо иметь право на ошибку! Как же хорошо!
👏2👍1