Создаём систему, которая транскрибирует речь с микрофона в текст в режиме реального времени, с автоматической расстановкой знаков препинания и заглавных букв.
Каждую секунду микрофон передаёт аудио в AssemblyAI, которая распознаёт речь пословно. По завершении предложения ИИ понимает смысл и добавляет пунктуацию.
pip install pyaudio
pip install websockets
Mac:
brew install portaudio — всё настроится автоматическиWindows: Скачайте wheel-файл с [неофициального сайта Python](https://www.lfd.uci.edu/~gohlke/pythonlibs/), затем:
pip install pyaudio‑0.2.11‑cp39‑cp39‑win_amd64.whl
Импорты:
import websockets
import asyncio
import base64
import json
from configure import auth_key
import pyaudio
Создайте файл
configure.py:auth_key = 'ваш_API_ключ_из_AssemblyAI'
Ключ можно получить на сайте AssemblyAI. Для real-time транскрибации нужен платный план.
FRAMES_PER_BUFFER = 3200
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
p = pyaudio.PyAudio()
stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=FRAMES_PER_BUFFER
)
URL = "wss://api.assemblyai.com/v2/realtime/ws?sample_rate=16000"
Частота дискретизации 16000 синхронизирована с AssemblyAI API.
Главная функция подключения:
async def send_receive():
async with websockets.connect(
URL,
extra_headers=(("Authorization", auth_key),),
ping_interval=5,
ping_timeout=20
) as _ws:
session_begins = await _ws.recv()
print(session_begins)
Функция отправки аудио:
async def send():
while True:
try:
data = stream.read(FRAMES_PER_BUFFER)
data = base64.b64encode(data).decode("utf-8")
json_data = json.dumps({"audio_data": str(data)})
await _ws.send(json_data)
except websockets.exceptions.ConnectionClosedError as e:
break
await asyncio.sleep(0.01)
return True
Функция получения текста:
async def receive():
while True:
try:
result_str = await _ws.recv()
print(json.loads(result_str)['text'])
except websockets.exceptions.ConnectionClosedError as e:
break
send_result, receive_result = await asyncio.gather(send(), receive())
while True:
asyncio.run(send_receive())
Запустите командой:
python speech_recognition.py
1. Микрофон захватывает аудио каждые 3200 фреймов
2. Аудио кодируется в base64 и отправляется через WebSocket
3. AssemblyAI транскрибирует в реальном времени
4. Получаете два типа результатов:
- Частичные: посекундная транскрипция
- Финальные: с заглавными буквами и пунктуацией
Пример вывода:
hello
hello world
hello world this
Hello world, this is a test.
- ИИ автоматически форматирует предложения
- Добавляет знаки препинания
- Исправляет грамматику
- Работает в реальном времени с минимальной задержкой
- pyAudio — захват аудио с микрофона
- websockets — двусторонняя связь с API
- asyncio — асинхронная обработка
- AssemblyAI — ML-движок для распознавания речи
С развитием ИИ создание таких систем стало гораздо проще. Раньше точное распознавание речи было сложной задачей, теперь это реализуется несколькими десятками строк кода.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Пакеты NPM — компоненты многократно используемого кода, которые делают JavaScript-разработку более управляемой. Разбираемся в истории, преимуществах и сценариях использования.
Пакет NPM — многократно используемый код JavaScript, которым можно делиться через реестр NPM. От простых утилит до полноценных фреймворков.
Ключевые особенности:
• Многократное использование — написал один раз, используй везде
• Контроль версий через семантическое версионирование (1.0.0)
• Совместная работа через публичный реестр
Начальный период (до 2009):
• Copy-paste — копировали код с форумов, что вело к несоответствиям
• Ручная загрузка — скачивали jQuery с сайта и подключали через
<script>• Git-репозитории — клонировали вручную, управляли зависимостями самостоятельно
• Браузерные войны — адаптировали код под IE или Netscape
Рождение NPM (2009):
Выход Node.js позволил JavaScript работать на серверах. Появилась потребность в эффективном обмене кодом. NPM решил проблему:
• Централизованный реестр пакетов
• Простая установка:
npm install <package-name>• Автоматическое управление зависимостями
1. Многократное использование
Если код решает общую проблему в нескольких проектах:
// Валидатор email
function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// Публикуем как пакет
npm install my-email-validator
2. Межкомандное сотрудничество
Для больших организаций, где фронтенд, бэкенд и мобильные команды используют общий код:
// Общие типы TypeScript
export type User = {
id: string;
name: string;
email: string;
};
npm install @company/shared-types
3. Вклад в open source
Публикация помогает другим разработчикам и укрепляет вашу репутацию.
4. Заполнение пробела
Если для вашей проблемы нет решения — создайте пакет, который интегрируется со сложным API или форматирует данные уникальным образом.
1. Код слишком специфичен
Скрипт, работающий только с вашей уникальной схемой БД, не имеет смысла публиковать.
2. Слишком простой код
// Слишком просто для пакета
const reverseString = (str) => str.split('').reverse().join('');
Лучше поделиться как фрагментом или включить в существующую библиотеку.
3. Уже есть лучшее решение
Перед созданием проверьте реестр NPM. Зачем дублировать
date-fns?// Ваша функция
function formatDate(date) {
const d = new Date(date);
return `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`;
}
// Уже есть date-fns
import { format } from "date-fns";
console.log(format(new Date("2023-01-01"), "MM/dd/yyyy"));
4. Нет ресурсов на поддержку
Публикация = обязательства: обновления, исправление багов, поддержка пользователей. Пакет с хакатона, который никто не будет поддерживать, лучше не публиковать.
NPM-пакеты — основа современной JavaScript-разработки. Они позволяют:
• Переиспользовать код
• Обеспечивать консистентность в командах
• Делиться решениями с сообществом
Но помните: не каждый код должен стать пакетом. Оцените масштаб задачи и готовность к долгосрочной поддержке.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Современный JavaScript развивается стремительно. Разбираем 6 фич, которые делают код короче и чище.
1. Опциональные цепочки (?.)
Безопасная навигация по объектам без множественных проверок:
const someObject = {
profile: {
firstName: 'Nicky',
lastName: 'Christensen'
}
}
// ✅ С optional chaining
if (someObject?.profile?.firstName) {
console.log(someObject.profile.firstName)
}
// ❌ Старый способ
if (someObject && someObject.profile && someObject.profile.firstName) {
console.log(someObject.profile.firstName)
}
// Не выполнится, если свойства нет
if (someObject?.profile?.name) {
console.log('Not executed')
}2. Оператор нулевого слияния (??)
Возвращает правый операнд только если левый
null или undefined:const falsy = false;
const emptyString = '';
const nullish = null;
console.log(falsy ?? 'Default'); // false
console.log(emptyString ?? 'Default'); // "" (не заменяется!)
console.log(nullish ?? 'Default'); // "Default"
// VS оператор ||
console.log(falsy || 'Default'); // "Default"
console.log(emptyString || 'Default'); // "Default"
console.log(nullish || 'Default'); // "Default"
Преимущество `??`: не заменяет
false, 0 или "" — только null/undefined.3. Динамический импорт
Асинхронная загрузка модулей (code splitting):
let someAsyncModule = await import('/modules/my-module.ts');4. Promise.allSettled()
Узнать результат всех промисов, даже отклонённых:
const promise1 = Promise.resolve("OK");
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve("Done");
Promise.allSettled([promise1, promise2, promise3])
.then((results) => console.log(results));
// Результат:
// [
// { status: 'fulfilled', value: 'OK' },
// { status: 'rejected', reason: 'Error' },
// { status: 'fulfilled', value: 'Done' }
// ]Отличие от `Promise.all()`: не падает при reject, показывает статус каждого промиса.
5. Spread-оператор (...)
Для массивов:
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2]; // [1,2,3,4,5,6]
Для объектов:
const basePerson = {
name: 'Nicky C',
country: 'DK'
}
const footballer = {
...basePerson,
team: 'Man UTD',
shirtNumber: '11'
}
// { name: 'Nicky C', country: 'DK', team: 'Man UTD', shirtNumber: '11' }6. Деструктуризация объектов
Распаковка значений из объекта в переменные:
const footballer = {
name: 'Nicky C',
team: 'Man UTD',
shirtNumber: '11'
}
const { team, shirtNumber } = footballer;
console.log(team, shirtNumber); // "Man UTD" "11"- Сокращают код
- Улучшают читаемость
- Снижают количество багов
- Стандарт в современном JavaScript
Используйте уже сегодня!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Зачем моделировать данные, если Snowflake всё обработает сам? Разбираемся на примере.
Проанализировать элементы, которые пользователи видят в списке (товары, результаты поиска, посты в ленте).
Аналитикам нужно:
- Оценить CTR
- Определить влияние позиции (1-я vs 10-я)
Этап 1: Сбор данных
Фронтенд отправляет JSON-событие:
{
"user_id": 3299223,
"event_time": "2022-04-21 12:31:55 UTC",
"items_shown": [1037287139, 1727228887, ..., 1665899805]
}Этап 2: Доставка
API → Kafka/Airbyte/Fivetran → Snowflake → таблица
RAW_EventsЭтап 3: Готово!
Преимущества:
- Не нужно думать о масштабируемости
- Не нужно преобразовывать данные
- JSON работает из коробки
Запрос 1: Развернуть массив
SELECT
user_id,
value::NUMBER as item_id,
index as position
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)
Запрос 2: Сколько пользователей видели item на позиции 1?
SELECT COUNT(DISTINCT user_id)
FROM (
SELECT user_id, value, index
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)
)
WHERE value = 1037287139
AND index = 0
AND DATE(event_time) = CURRENT_DATE
- Сложный синтаксис
- Читает ВСЕ JSON из таблицы
- Распаковывает и разворачивает каждый раз
- Медленно и дорого
- При JOIN'ах всё повторяется
Создаём таблицу Events:
CREATE TABLE Events AS
SELECT
user_id,
event_time,
value::NUMBER as item_id,
index as position
FROM RAW_Events,
LATERAL FLATTEN(input => items_shown)
Обновляем инкрементально (ежедневно/ежечасно).
Запрос 4: Тот же вопрос, но проще
SELECT COUNT(DISTINCT user_id)
FROM Events
WHERE item_id = 1037287139
AND position = 0
AND DATE(event_time) = CURRENT_DATE
Запрос 2 (без модели) | 47 сек | X-Small |
Запрос 4 (с моделью) | 0.5 сек | X-Small |
Разница: 94x быстрее!
При миллиардах JSON разница в 1000+ раз по времени и деньгам.
1. Данные достаточно большие
2. Запросы достаточно частые
3. Количество аналитиков ограничено
4. Нужна консистентность результатов
10-20 лет назад: аналитик не мог работать с сотнями миллионов строк без инженера данных.
Сегодня: современный стек позволяет исследовать данные самостоятельно. Моделирование не обязательно, но:
- Ускоряет запросы
- Упрощает SQL
- Снижает затраты
- Повышает производительность
Современный стек даёт свободу. Моделирование даёт скорость.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Создаём одинаковое приложение на двух языках и сравниваем подходы.
CLI-приложение с 4 командами:
-
list — показать все задачи-
add — добавить задачу-
done — отметить выполненные-
clear — удалить всеФормат хранения:
[ ] заголовок задачи
[*] выполненная задача
pnpm init
Node v20, ESM модули, без сторонних библиотек. Файл
todo.mjs.Добавить задачу:
async function addTodo(filePath, todoTitle) {
return await useTodoFile(filePath, async (fd) => {
fd.appendFile(`[ ] ${todoTitle}\n`)
})
}Форматированный список:
async function formattedTodos(filePath) {
return await useTodoFile(filePath, async (fd) => {
const content = await fd.readFile('utf8')
return content
.split('\n')
.filter((title) => title)
.map((title, index) => {
const isDone = /^\[\*\]/.test(title)
const rawTitle = title.match(/^\[[\s*]\]\s(.+)/)?.[1]
return `${index + 1} ${isDone ? strikethrough(rawTitle) : rawTitle}`
})
.join('\n')
})
}Отметить выполненной:
async function markDone(filePath, ids) {
return await useTodoFile(filePath, async (fd) => {
const content = await fd.readFile('utf8')
const lines = content.split('\n')
ids.forEach((id) => {
lines[id - 1] = lines[id - 1].replace(/^\[\s\]/, '[*]')
})
await fd.truncate()
await fd.writeFile(lines.join('\n'))
})
}gleam new gleam_todos
gleam add argv simplifile gleam_community_ansi
Добавить задачу:
pub fn add_todo(file_path: String, title: String) -> Result(Nil, Nil) {
simplifile.append(file_path, "[ ] " <> title <> "\n")
|> result.replace(Nil)
|> result.nil_error
}Форматированный список:
pub fn formatted_todos(file_path: String) -> Result(String, Nil) {
simplifile.read(file_path)
|> result.map(split_to_lines)
|> result.map(format_todo_lines)
|> result.map(join_lines)
|> result.nil_error
}
fn format_todo_lines(lines: List(String)) -> List(String) {
list.index_map(lines, fn(line, index) {
case line {
"[ ] " <> rest -> int.to_string(index + 1) <> " " <> rest
"[*] " <> rest -> int.to_string(index + 1) <> " " <> ansi.strikethrough(rest)
_ -> line
}
})
}Отметить выполненной:
pub fn mark_done(file_path: String, ids: List(Int)) -> Result(Nil, Nil) {
simplifile.read(file_path)
|> result.map(split_to_lines)
|> result.map(fn(lines) { mark_todo_done(lines, ids) })
|> result.map(join_lines)
|> result.try(fn(content) { simplifile.write(file_path, content) })
|> result.nil_error
}JavaScript:
node todo.mjs add "first todo"
node todo.mjs list
Gleam:
gleam run add "first todo"
gleam run list
Итог
JavaScript:
-
-
-
-
Gleam:
-
-
-
-
|> для цепочек операций-
Автор предпочитает Gleam за красивый код, мощную обработку ошибок и простые зависимости.
Gleam код читается как описание того, что делает программа, а не как она это делает.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Массивы — фундаментальные структуры данных C++. Разбираем эффективные способы их вывода для отладки и визуализации.
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i];
if (i < size - 1) std::cout << ", ";
}
}for (int i : std::array<int, 5>{arr, arr + size}) {
std::cout << i << " ";
}std::copy(arr, arr + N,
std::ostream_iterator<T>(std::cout, ", "));
void print2DArray(int arr[][3], int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
}Вложенные циклы для матриц.
template<typename T, size_t N>
void printArray(const std::array<T, N>& arr) {
for (size_t i = 0; i < N; ++i) {
std::cout << arr[i];
if (i < N - 1) std::cout << ", ";
}
}
struct Person {
std::string name;
int age;
friend std::ostream& operator<<(
std::ostream& os, const Person& p) {
return os << p.name << " (" << p.age << ")";
}
};Переопределяем
operator<< для кастомных объектов.struct SensorReading {
double temperature;
double humidity;
friend std::ostream& operator<<(
std::ostream& os, const SensorReading& sr) {
return os << "Temp: " << sr.temperature
<< "°C, Humidity: "
<< sr.humidity << "%";
}
};• Простой for — полный контроль
• Range-based — современный код
• std::copy — STL-стиль
• std::array — фиксированный размер
• operator<< — свои типы
Эффективный вывод массива — это представление данных в понятном формате для отладки, анализа и работы с пользователем.
Please open Telegram to view this post
VIEW IN TELEGRAM
Написание CSS с нуля отнимает много времени. Эти бесплатные инструменты помогут ускорить разработку и повысить продуктивность.
Генератор модного стиля "мягкого UI". Создаёт плавные тени и объёмные элементы.
Настройки:
Автоматическая генерация CSS-кода для неоморфного дизайна.
Создание сложных фигур без знания свойства
clip-path:Визуальный редактор + готовый CSS-код.
Коллекция готовой CSS-анимации:
Выбираете анимацию → редактируете → копируете код.
Удаление неиспользуемого CSS-кода.
Зачем нужен:
Особенно полезен с Bootstrap, Tailwind и другими фреймворками.
Генератор плавных теней.
Настройки:
Создаёт реалистичные многослойные тени одним кликом.
Создание градиентных фонов:
Визуальный редактор + копирование кода.
Премиум-инструмент для просмотра CSS любого элемента.
Возможности:
Идеально для изучения чужого кода и поиска вдохновения.
Генератор макетов CSS Grid.
Настройки:
Получаете CSS + HTML-код сетки.
Создание SVG-волн для фона.
Параметры:
Готовый SVG + CSS для вставки в проект.
Итог
Эти инструменты:
Используйте их для быстрого создания красивых страниц без написания CSS с нуля.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Команда Whitespectre создала библиотеку rn-modal-presenter для решения проблем с множественными модальными окнами в React Native.
1. Управление состояниями — нужна переменная для каждого модального окна
2. iOS ограничение — нельзя показать >1 модального окна одновременно
3. Размонтирование — при навигации модалки исчезают
4. Тихие ошибки — вторая модалка не появляется без предупреждений
Пример проблемы:
const App = () => {
const [showRateAppModal, setShowRateAppModal] = useState(false);
const [showAppRatedPositiveModal, setShowAppRatedPositiveModal] = useState(false);
const [showAppRatedNegativeModal, setShowAppRatedNegativeModal] = useState(false);
const [showActivateGadgetModal, setShowActivateGadgetModal] = useState(false);
return (
<SafeAreaView>
{showActivateGadgetModal && <ActivateGadgetModal />}
{showRateAppModal && <RateAppModal />}
{showAppRatedPositiveModal && <PositiveFeedbackModal />}
{showAppRatedNegativeModal && <NegativeFeedbackModal />}
</SafeAreaView>
);
};Проблемы:
Почему сторонние библиотеки не помогли
react-native-modal:
react-native-modalfy:
Преимущества:
1. Императивный API — вызов из любого места
2. Без состояний — не нужны useState
3. Множественные модалки — из коробки
4. Гибкость — любой компонент как модалка
5. 100% JavaScript — работает кроссплатформенно
Установка:
yarn add @whitespectre/rn-modal-presenter
Использование:
import { ModalPresenterParent, showModal } from '@whitespectre/rn-modal-presenter';
const App = () => (
<ModalPresenterParent>
<SafeAreaView>
<Button
title="Activate Gadget"
onPress={() => showModal(ActivateGadgetModal, {})}
/>
</SafeAreaView>
</ModalPresenterParent>
);Модалка управляет собой:
const ActivateGadgetModal = ({dismiss}: ModalContentProps) => (
<View style={styles.modal}>
<Text>Your Gadget has been activated</Text>
<Button
title="Close"
onPress={() => {
dismiss();
showModal(RateAppModal, {});
}}
/>
</View>
);Сравнение подходов
Standard Modal:
react-native-modal:
react-native-modalfy:
rn-modal-presenter:
Планы развития
Итог
rn-modal-presenter упрощает работу с множественными модальными окнами, убирая управление состояниями и предоставляя императивный API. Идеально для сложных флоу с несколькими последовательными модалками.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Сортировка строк — фундаментальная операция в программировании. Разбираем все методы от базовых до продвинутых.
#include <algorithm>
#include <string>
std::string str = "zycbwvutsrqponmlkjihgfedcba";
std::sort(str.begin(), str.end());
// Результат: "abcdefghijklmnopqrstuvwxyz"
Символы сортируются по значениям ASCII в порядке возрастания.
std::vector<std::string> words = {"banana", "apple", "cherry", "date"};
std::sort(words.begin(), words.end());
// Результат:
// apple
// banana
// cherry
// dateЛексикографическая сортировка (словарный порядок).
bool case_insensitive_compare(const std::string& a, const std::string& b) {
return std::lexicographical_compare(
a.begin(), a.end(),
b.begin(), b.end(),
[](char a, char b) {
return std::tolower(a) < std::tolower(b);
}
);
}
std::vector<std::string> words = {"Banana", "apple", "Cherry", "date"};
std::sort(words.begin(), words.end(), case_insensitive_compare);
// Результат:
// apple
// Banana
// Cherry
// dateПользовательский компаратор игнорирует регистр, но сохраняет исходное написание.
std::vector<std::string> words = {"short", "very long string", "medium"};
std::sort(words.begin(), words.end(),
[](const std::string& a, const std::string& b) {
return a.length() < b.length();
});
// Результат:
// short
// medium
// very long stringЛямбда-функция сравнивает длину строк.
struct Name {
std::string first;
std::string last;
};
std::sort(names.begin(), names.end(),
[](const Name& a, const Name& b) {
if (a.last != b.last) {
return a.last < b.last;
}
return a.first < b.first;
});
// Входные данные:
// John Doe, Jane Doe, Alice Smith, Bob Johnson
// Результат:
// Jane Doe
// John Doe
// Bob Johnson
// Alice SmithМногокритериальная сортировка: сначала по фамилии, затем по имени.
#include <unicode/coll.h>
bool compare_unicode(const std::string& a, const std::string& b) {
UErrorCode status = U_ZERO_ERROR;
icu::Collator* collator = icu::Collator::createInstance(icu::Locale(""), status);
icu::UnicodeString ua = icu::UnicodeString::fromUTF8(a);
icu::UnicodeString ub = icu::UnicodeString::fromUTF8(b);
UCollationResult result = collator->compare(ua, ub, status);
delete collator;
return result == UCOL_LESS;
}
std::vector<std::string> words = {"café", "cafe", "résumé", "resume"};
std::sort(words.begin(), words.end(), compare_unicode);
Корректная обработка диакритических знаков.
1. std::string_view
Избегайте копирования для строк только для чтения.
2. std::stable_sort
Сохраняет относительный порядок эквивалентных элементов.
3. Параллельная сортировка (C++17+)
Для больших наборов данных.
Бенчмарк:
100,000 строк по 10 символов:
• std::sort: 0.245 сек
• std::stable_sort: 0.298 сек
Простая сортировка:
→ std::sort
Без учёта регистра: → Кастомный компаратор
По длине: → Лямбда-функция
Unicode: → ICU библиотека
Сохранить порядок: → std::stable_sort
Большие данные: → Параллельная сортировка (C++17+)
Итог
Сортировка строк в C++ — от базовой лексикографической до сложной многокритериальной — позволяет эффективно обрабатывать любые задачи.
Выбирайте метод исходя из требований: регистр, Unicode, критерии, производительность.
Please open Telegram to view this post
VIEW IN TELEGRAM
При создании UI-библиотек один из главных вызовов — найти баланс между гибкостью и простотой API. Разберём, как правильно работать с проекцией контента в Angular.
Два основных подхода:
Отлично подходит, когда:
• Слоты всегда определены
• Не нужен условный рендеринг
• Контекст не требуется
<app-field>
<span slot="label">Label</span>
<input>
</app-field>
Используйте, когда нужно:
• Передать контекст в слот
• Ленивая инициализация контента
• Условный рендеринг
<ng-template #suffix let-control>
<button [class.bg-red]="control.invalid">
Search
</button>
</ng-template>
Создайте универсальную директиву для унификации работы со слотами:
@Directive({
selector: 'ng-template[slot]'
})
export class Slot {
readonly template = inject(TemplateRef);
readonly name = input.required<string>({ alias: 'slot' });
}Это даёт:
Компонент Accordion может комбинировать оба подхода:
Итог
Правильная работа со слотами — это не выбор между ng-content и ng-template, а понимание, когда использовать каждый инструмент. Комбинируйте оба подхода в зависимости от задачи, и ваши компоненты станут по-настоящему переиспользуемыми и удобными для других разработчиков.
Please open Telegram to view this post
VIEW IN TELEGRAM
Автоматизируйте развертывание serverless функций с Infrastructure as Code!
Что будем делать:
Создадим полностью автоматизированную инфраструктуру:
Структура проекта:
cloud_function_project/
├── terraform/
│ ├── backend.tf
│ ├── function.tf
│ ├── main.tf
│ ├── storage.tf
│ └── variables.tf
└── src/
├── main.py
└── requirements.txt
Ключевые шаги:
def hello_gcs(event, context):
print('Event ID:', context.event_id)
print('Bucket:', event['bucket'])
print('File:', event['name'])
Объявляем провайдера, переменные и ресурсы GCP
data "archive_file" "source" {
type = "zip"
source_dir = "../src"
}
resource "google_cloudfunctions_function" "function" {
runtime = "python37"
entry_point = "hello_gcs"
event_trigger {
event_type = "google.storage.object.finalize"
}
}cd ./terraform
terraform init
terraform plan
terraform apply
Итог
Terraform + Cloud Functions = идеальная комбинация для serverless архитектуры. Описываете инфраструктуру декларативно, а Terraform заботится о развертывании и управлении состоянием. Код становится единственным источником истины для вашей инфраструктуры.
Бонус: Поддержка Python, Node.js, Go, Java, .NET, Ruby и PHP!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Разбираем фундаментальную концепцию ООП, которая делает ваш C++ код гибким и расширяемым!
Что такое Upcasting?
Преобразование указателя/ссылки производного класса в указатель/ссылку базового класса — движение вверх по иерархии наследования.
Dog* dog = new Dog();
Animal* animal = dog; // Приведение вверх
animal->makeSound(); // "The dog barks"
Два способа приведения:
Animal* animal1 = dog;
Animal* animal2 = static_cast<Animal*>(dog);
Пишите код для базового класса, работайте с любыми производными:
std::vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));
for (const auto& shape : shapes) {
shape->draw(); // Вызов правильного метода
cout << shape->area();
}
class Base {
public:
virtual ~Base() {} // ОБЯЗАТЕЛЬНО virtual!
};Без виртуального деструктора не вызовется деструктор производного класса при удалении через базовый указатель → утечки памяти!
class GameEngine {
std::vector<std::unique_ptr<GameObject>> objects;
public:
void addObject(std::unique_ptr<GameObject> obj);
void updateAll(); // Обновляет всё единообразно
void renderAll(); // Рисует всё единообразно
};
engine.addObject(std::make_unique<Player>());
engine.addObject(std::make_unique<Enemy>());
engine.addObject(std::make_unique<Projectile>());Храним Player, Enemy, Projectile в одном контейнере и работаем с ними единообразно!
Derived1* d1 = dynamic_cast<Derived1*>(basePtr);
if (d1) {
d1->derived1Method(); // Безопасно!
}
class C : public A, public B {};
C* c = new C();
A* a = c; // Приведение к A
B* b = c; // Приведение к B
// Оба работают корректно!Итог
Приведение вверх — основа полиморфизма в C++. Используйте его для:
Помните: всегда делайте базовый деструктор виртуальным при использовании полиморфизма!
Please open Telegram to view this post
VIEW IN TELEGRAM
TypeScript делает код надежным, но неправильное использование приводит к багам. Следуйте лучшим практикам!
any отключает проверку типов — это как @ts-ignore для всей переменной. Опасно!
// ❌ Плохо
function process(data: any) {
return data.someMethod(); // Никаких проверок!
}
// ✅ Хорошо
function process(data: unknown) {
if (typeof data === 'object' && data !== null) {
// Безопасная проверка перед использованием
}
}
Правило: unknown заставляет вас явно проверить тип перед использованием. Это безопаснее!
// ❌ Плохо
let str: String = new String("hello");
let num: Number = new Number(42);
let bool: Boolean = new Boolean(true);
// ✅ Хорошо
let str: string = "hello";
let num: number = 42;
let bool: boolean = true;
Используйте примитивные типы: string, number, boolean, bigint, symbol.
Обычные enum генерируют дополнительный код во время выполнения.
const enum встраивает значения напрямую!// ❌ Генерирует runtime код
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE"
}
// ✅ Встраивается при компиляции
const enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE"
}
Результат: Меньше кода, быстрее выполнение!
TypeScript поддерживает опциональную цепочку (
?.) и нулевое слияние (??):// ❌ Старый способ
const name = user && user.profile && user.profile.name || 'Guest';
// ✅ Современный способ
const name = user?.profile?.name ?? 'Guest';
Преимущества:
TypeScript предоставляет мощные инструменты для преобразования типов:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Все поля опциональны
type PartialUser = Partial<User>;
// Все поля только для чтения
type ReadonlyUser = Readonly<User>;
// Выбрать определенные поля
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;
// Исключить определенные поля
type UserWithoutPassword = Omit<User, 'password'>;
// Сделать определенные поля обязательными
type RequiredName = Required<Pick<User, 'name'>>;Комбинируйте их:
type SafeUser = Readonly<Omit<User, 'password'>>;
Итог
TypeScript — мощный инструмент, но важно использовать его правильно:
Следуя этим рекомендациям, вы пишете не просто типизированный код, а надежный и поддерживаемый TypeScript!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Ключевое слово, которое часто упускают из виду, но оно критически важно для элегантного кода!
Что такое mutable?
Позволяет изменять член данных константного объекта, не нарушая его логическую неизменяемость.
class Example {
public:
void setValue(int val) const {
value = val; // Работает благодаря mutable!
}
private:
mutable int value;
};Четыре основных сценария использования:
Сохраняйте результаты дорогостоящих вычислений:
class DataProcessor {
public:
int getSum() const {
if (!cachedSum_) {
cachedSum_ = std::accumulate(data_.begin(),
data_.end(), 0);
}
return *cachedSum_;
}
private:
std::vector<int> data_;
mutable std::optional<int> cachedSum_;
};Преимущество: Вычисляем один раз, используем многократно!
class LazyString {
public:
const std::string& getUpperCase() const {
if (!upperCase_) {
upperCase_ = original_;
std::transform(upperCase_->begin(),
upperCase_->end(),
upperCase_->begin(),
::toupper);
}
return *upperCase_;
}
private:
std::string original_;
mutable std::optional<std::string> upperCase_;
};Экономия: Вычисляем только когда действительно нужно!
class ThreadSafeCounter {
public:
int getValue() const {
std::lock_guard<std::mutex> lock(mutex_);
return value_;
}
private:
int value_ = 0;
mutable std::mutex mutex_; // Можем блокировать в const методах
};class BigData {
public:
int get(size_t index) const {
++accessCount_; // Не меняет логическое состояние
return data_[index];
}
size_t getAccessCount() const {
return accessCount_;
}
private:
std::vector<int> data_;
mutable size_t accessCount_ = 0;
};class Configuration {
public:
Configuration(const std::string& filename)
: filename_(filename) {}
std::string getValue(const std::string& key) const {
loadConfigIfNeeded(); // Загружаем только при первом обращении
return config_[key];
}
private:
void loadConfigIfNeeded() const {
if (!isLoaded_) {
// Загружаем конфигурацию из файла
isLoaded_ = true;
}
}
std::string filename_;
mutable bool isLoaded_ = false;
mutable std::unordered_map<std::string, std::string> config_;
};Использование:
const Configuration config("settings.conf");
// Файл загрузится только здесь ↓
std::cout << config.getValue("log_level");1. Потокобезопасность
// ❌ НЕ потокобезопасно!
mutable int count_ = 0;
void increment() const { ++count_; }
// ✅ Потокобезопасно
mutable int count_ = 0;
mutable std::mutex mutex_;
void increment() const {
std::lock_guard<std::mutex> lock(mutex_);
++count_;
}
2. Не нарушайте логическую неизменяемость
// ❌ Плохо - меняет логическое состояние
class Bad {
void setData(int d) const { data_ = d; }
mutable int data_;
};
// ✅ Хорошо - только внутренняя оптимизация
class Good {
int getData() const {
++accessCount_; // Метаданные, не логика
return data_;
}
int data_;
mutable size_t accessCount_ = 0;
};
3. Документируйте использование
class Example {
private:
// mutable используется для кэширования
// с целью повышения производительности
mutable std::optional<int> cachedValue_;
};Итог
mutable — мощный инструмент для:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Обрабатываете миллионы записей и приложение тормозит? 11 проверенных техник оптимизации Java Spring для работы с большими данными!
Проблема:
Приложение должно обработать 10 000+ транзакций по каждому счету. Как не упасть в OutOfMemoryError?
// ❌ Плохо
for (int i = 0; i < list.size(); i++)
// ✅ Хорошо
int size = list.size();
for (int i = 0; i < size; i++)
Экономия: вызов size() выносится за цикл
Делим данные на батчи по 100-1000 записей
for (int i = 0; i < size; i += 100) {
List<T> batch = data.subList(i, min(i+100, size));
repository.saveAll(batch);
repository.flush(); // освобождаем память
}// ❌ Создает промежуточные коллекции
.collect(Collectors.toList())
// ✅ Прямая обработка
.forEach(System.out::println)
• ArrayList вместо LinkedList (меньше памяти)
• int вместо Integer (без автобоксинга)
• HashMap с начальной емкостью
// Используем все ядра процессора
list.parallelStream().forEach(this::process);
// ❌ В цикле
new String("Result")
// ✅ Переиспользование
String result = "Result";
Не используйте Optional в полях класса — это лишние объекты-обертки!
java -Xms1g -Xmx2g -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200
Включите на проде для мониторинга:
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
// Предотвращаем утечки памяти
WeakHashMap<String, T> cache = new WeakHashMap<>();
Не сериализуйте лишние поля:
private transient int salary;
Банковское приложение обрабатывало остатки по счетам с 10К+ транзакций. После применения этих техник:
Частые ошибки:
Измеряйте производительность! Используйте профайлеры (JProfiler, VisualVM) перед оптимизацией.
Чек-лист перед продом
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Строки в JS — это не просто текст, а швейцарский нож для разработчика! Вот 10 приемов, которые сделают ваш код чище и быстрее.
const reverse = str => str.split('').reverse().join('');
reverse("hello"); // "olleh""hello world".includes("world"); // trueconst toTitleCase = str => str.split(' ')
.map(word => word[0].toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
toTitleCase("javascript tricks");
// "Javascript Tricks"" hello world ".trim();
// "hello world"
"hello ".repeat(3);
// "hello hello hello "
const count = (str, sub) => str.split(sub).length - 1;
count("hello world, hello JS", "hello"); // 2
"42".padStart(5, "0"); // "00042"
"7".padEnd(3, "0"); // "700"
"banana".replaceAll("a", "o"); // "bonono"
// Или через regex:
"banana".replace(/a/g, "o");const onlyLetters = str => str.replace(/[^a-zA-Z]/g, '');
onlyLetters("a1b2c3"); // "abc"
[..."hello"];
// ['h', 'e', 'l', 'l', 'o']
// Подсчет уникальных символов
const uniqueChars = str => [...new Set(str)].length;
uniqueChars("hello"); // 4
// Удаление дубликатов
const removeDupes = str => [...new Set(str)].join('');
removeDupes("hello"); // "helo"
Почему это важно
Перед тем как писать свою функцию для работы со строками, проверьте — возможно, встроенный метод уже решает вашу задачу!
Частые ошибки:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В C++ нет нативной поддержки switch для строк, но есть 4 мощных способа это обойти! Разбираем от простого к продвинутому.
Проблема:
// ❌ Так нельзя в C++
switch(str) {
case "hello": ...
case "quit": ...
}
Самый простой и быстрый способ:
enum class StringCode {
hello, quit, help, unknown
};
StringCode hashString(const string& str) {
if (str == "hello") return StringCode::hello;
if (str == "quit") return StringCode::quit;
if (str == "help") return StringCode::help;
return StringCode::unknown;
}
switch (hashString(command)) {
case StringCode::hello:
cout << "Hello there!" << endl;
break;
case StringCode::quit:
cout << "Goodbye!" << endl;
break;
}Для динамической обработки:
class CommandProcessor {
unordered_map<string, function<void()>> commands;
public:
CommandProcessor() {
commands["help"] = []() {
cout << "Help info" << endl;
};
commands["add"] = []() {
cout << "Adding..." << endl;
};
}
void execute(const string& cmd) {
auto it = commands.find(cmd);
if (it != commands.end()) {
it->second();
}
}
};Хеширование на этапе компиляции:
constexpr uint64_t hash(string_view str) {
uint64_t hash = 0;
for (char c : str) {
hash = (hash * 131) + c;
}
return hash;
}
constexpr uint64_t operator""_hash(const char* str, size_t len) {
return hash(string_view(str, len));
}
switch (hash(action)) {
case "save"_hash:
cout << "Saving..." << endl;
break;
case "load"_hash:
cout << "Loading..." << endl;
break;
}Для высоконагруженных систем:
class StringPool {
unordered_set<string> pool;
public:
const string* intern(const string& str) {
auto [it, inserted] = pool.insert(str);
return &(*it);
}
};
// Сравниваем указатели, а не строки!
if (str == pool.intern("start")) {
// быстрее обычного сравнения строк
}class TextEditor {
enum class CommandType {
insert, delete_line, move, print, unknown
};
CommandType getCommandType(const string& cmd) {
if (cmd == "insert" || cmd == "i")
return CommandType::insert;
if (cmd == "delete" || cmd == "d")
return CommandType::delete_line;
// ...
}
void executeCommand(const string& cmdLine) {
switch (getCommandType(cmd)) {
case CommandType::insert: /* ... */ break;
case CommandType::delete_line: /* ... */ break;
}
}
};Сравнение производительности:
Выбор метода:
Небольшой фикс. набор строк → Enum hash
Динамические команды → unordered_map
Критична скорость → Constexpr hash
Много повторений → String Pool
Важные моменты
Pro-tip:
Комбинируйте подходы! Например:
Реальные кейсы применения:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Пишете код без тестов? Готовьтесь часами искать баги! Разбираем, как unittest спасет ваше время и нервы.
Простыми словами: это проверка, что ваш код работает правильно. Большинство делает это через print() и запуск в терминале. Но есть способ лучше!
Вместо часов отладки — мгновенное обнаружение проблем
Заставляет продумывать входы, выходы и граничные случаи
Копируете функцию? Копируйте и тесты!
Можно даже писать тесты ДО написания кода (test-driven development)
Типы тестов:
Проверка отдельных функций/компонентов
Проверка взаимодействия всех частей
Сегодня фокус на модульном!
def add(x, y):
return x + y
# Базовый тест
assert add(2, 4) == 6, "Should be 6"
Если результат не 6 → получите AssertionError
Unittest: профессиональный подход
Встроенный в Python тестировщик. Без установки пакетов!
Структура проекта:
project/
├── functions.py
└── tests.py
functions.py:
def add(x, y):
return x + y
tests.py:
import unittest
from functions import add
class TestAdd(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 4), 6)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)
if __name__ == '__main__':
unittest.main()
Запуск:
python tests.py
Результат:
...
-------------------------------
Ran 1 test in 0.001s
OK
Каждая точка (.) = успешный тест
E = ошибка
F = провал теста
# ✅ Работает
def test_add():
pass
# ❌ НЕ работает (не начинается с test_)
def add_test():
pass
Название ДОЛЖНО начинаться с test_!
Сначала очевидные случаи:
def test_add_positive():
self.assertEqual(add(2, 3), 5)
Потом граничные:
def test_add_zero():
self.assertEqual(add(0, 5), 5)
self.assertEqual(add(5, 0), 5)
Что может сломать ваш код?
def test_edge_cases():
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(0, 0), 0)
self.assertEqual(add(-5, -3), -8)
Каждый тест — отдельная вселенная!
class TestCalculator(unittest.TestCase):
def setUp(self):
# Выполняется ПЕРЕД каждым тестом
self.calc = Calculator()
def tearDown(self):
# Выполняется ПОСЛЕ каждого теста
self.calc = None
self.assertTrue(add(2, 3) == 5)
# Вывод: Expected True, but was False
self.assertEqual(add(2, 3), 5)
# Вывод: AssertionError: 5 != 6
Второй вариант даёт НАМНОГО больше информации!
def round_plus_ten(x):
return round(x) + 10
class TestRound(unittest.TestCase):
def test_round(self):
self.assertEqual(round_plus_ten(4.3), 14) # ✅
self.assertEqual(round_plus_ten(4.7), 15) # ✅
self.assertEqual(round_plus_ten(4.5), 15) # ❌ FAIL!
Почему упало?
round(4.5) = 4, а не 5! Python округляет к ближайшему чётному.
Группировка тестов:
class TestAddition(unittest.TestCase):
def test_positive(self): ...
def test_negative(self): ...
class TestMultiplication(unittest.TestCase):
def test_basic(self): ...
Пропуск тестов:
@unittest.skip("Пока не работает")
def test_future_feature(self):
passПроверка исключений:
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
divide(10, 0)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Best Practices:
✅ Один тест = одна проверка
✅ Тесты должны быть быстрыми (<1 сек)
✅ Понятные названия: test_add_negative_numbers
✅ Не тестируйте сторонние библиотеки
✅ Покрытие кода 70-80% — отличный результат
✅ Коммитьте тесты вместе с кодом
❌ Не делайте тесты зависимыми друг от друга
❌ Не используйте случайные данные без seed
❌ Не игнорируйте упавшие тесты
🔸 Workflow разработки:
1. Пишете функцию
2. Сразу пишете тест
3. Запускаете тест
4. Если упал → чините код
5. Повторяете до зелёных тестов
6. Добавляете edge cases
7. Профит!💫
🔸 Альтернативы unittest:
🔸 pytest — более простой синтаксис
🔸 nose2 — расширение unittest
🔸 doctest — тесты в docstrings
Но unittest — это стандарт Python!
💡 Золотое правило:
«Если нашли баг — напишите тест, который его воспроизводит. Потом чините код. Так этот баг никогда не вернётся!»
Реальная статистика⬇️
🔸 Проекты с тестами имеют на 40% меньше багов
🔸 Рефакторинг с тестами = безопасность
🔸 Тесты = документация вашего кода
🔸 Код с тестами легче поддерживать
📎 Статья
🎙 Новости
📝 База вопросов
1. Пишете функцию
2. Сразу пишете тест
3. Запускаете тест
4. Если упал → чините код
5. Повторяете до зелёных тестов
6. Добавляете edge cases
7. Профит!
Но unittest — это стандарт Python!
«Если нашли баг — напишите тест, который его воспроизводит. Потом чините код. Так этот баг никогда не вернётся!»
Реальная статистика
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Забываете отмечать рабочее время? Делаем простой бот на Node.js + TypeScript!
Жене нужен был отчёт за 3 месяца работы, но инструмента для учёта времени не было. Сначала писала "начало"/"конец" в WhatsApp группу. Решил автоматизировать это через Telegram Bot!
• Node.js + TypeScript
• node-telegram-bot-api
• CSV для хранения
• Docker для деплоя
npm i --save node-telegram-bot-api
npm i --save @types/node-telegram-bot-api
Идём к @BotFather → /newbot → получаем токен
import TelegramBot from 'node-telegram-bot-api';
const token = 'YOUR_TOKEN';
const bot = new TelegramBot(token, { polling: true });
// Начать задачу
bot.onText(/\/work (.+)/, async (msg, match) => {
const chatId = msg.chat.id;
const work = match[1];
const startDate = new Date();
const entries = await getEntries();
entries.push({
work,
startDate: startDate.toJSON(),
endDate: null,
duration: null,
});
writeFile(entries);
bot.sendMessage(chatId,
`Started ${work} at ${startDate.toLocaleString()}`);
});
// Завершить задачу
bot.onText(/\/done(.*)/, async (msg) => {
const chatId = msg.chat.id;
const endDate = new Date();
const entries = await getEntries();
const targetEntry = entries[entries.length - 1];
targetEntry.endDate = endDate.toJSON();
const startDate = new Date(targetEntry.startDate);
const diff = endDate - startDate;
targetEntry.duration = formatDuration(diff);
writeFile(entries);
bot.sendMessage(chatId,
`Finished ${targetEntry.work}. Took ${diff}!`);
});
const getEntries = async () => {
const val = fs.readFileSync(csvFilePath).toString();
const entries = [];
val.split("\n").forEach((row, index) => {
if (row === "" || index == 0) return;
const [work, startDate, endDate, duration] =
row.split(';');
entries.push({ work, startDate, endDate, duration });
});
return entries;
}
const writeFile = (entries) => {
let str = "work;startDate;endDate;duration;\n";
entries.forEach(e => {
str += Object.keys(e).map(k => e[k]).join(';') + '\n';
});
fs.writeFileSync(csvFilePath, str);
};/work Написать статью
→ Started at 9:00 AM
/done
→ Finished. Took 3h 25m!
→ work;startDate;endDate;duration;
Статья;2025-01-20T09:00...;3h 25m
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install && npm run build
CMD ["node", "build/main/index.js"]
docker build -t timetracker .
docker run -ti timetracker
Не коммитьте токен! Используйте
.env:import dotenv from 'dotenv';
dotenv.config();
const token = process.env.TELEGRAM_BOT_TOKEN;
Итог
За один вечер — полнофункциональный тайм-трекер! ~200 строк кода, работает везде, данные в удобном формате.
Please open Telegram to view this post
VIEW IN TELEGRAM
Изолированная среда для запуска приложений!
Изолированная «песочница», где процессы работают отдельно от ОС.
Внутри: все зависимости, библиотеки, конфигурации.
Результат: запускаешь везде — ничего устанавливать не нужно.
Правило: 1 контейнер = 1 процесс
Цепочка:
Dockerfile → Image (шаблон) → Container (экземпляр)
Аналогия: Image = класс, Container = объект
ВМ: гипервизор + полная ОС + много ресурсов
Контейнер: только нужное + разделяемая ОС + минимум ресурсов
• Микросервисы
• CI/CD
• Масштабирование
• Разработка ("Works on my machine" решено)
• Облако (AWS, Google, Azure)
docker pull node:18-alpine
docker run -p 3000:3000 my-app
docker stop container_id
Без: установка → зависимости → настройка → молитва
С:
docker run my-appИтог
Контейнеры: легкие, быстрые, портативные, эффективные, изолированные.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1