Наши услуги

Мы предлагаем

serv2
Сайт визитка - лендинг
Одна шаблонная страница - когда надо сегодня
serv2
Сайт магазин с CRM
Главная страница, каталог товаров/услуг, личный кабинет продавца
serv2
Дизайн для своего сайта
Нарисуем вам новый дизайн
serv2
Доработка существующего сайта
Когда что-то сломалось
serv2
Дизайн печатной продукции
Визитки, баннеры, стикеры и прочее нецифровое, что можно будет потрогать :)
serv2
Не знаю как объяснить - посмотрите сами
И такое делаем :)

Почему мы?

Минимум взаимодействий
Минимум взаимодействий
Из материалов только ваша страница или сайт
Всегда на связи
Всегда на связи
Голосовые, текст, видео - как удобно :)
Бывает и дорого, но есть бесплатно
Бывает и дорого, но есть бесплатно
Качество не зависит от цены
Индивидуальный подход
Индивидуальный подход
Разумеется :)
business
Коротко

О нас

wow5
2015
работаем успешно с
wow3
5
Реализованных проектов

Это начало интересного текста обо мне

Чуть позже напишу тут интересный текст

Партнёров пока нет
Но, ты пиши если что :)

Наши работы

Работа 1
Работа 2

Ознакомьтесь с нашим сервисом и комфортным сотрудничеством

Тарифы

Сайтик фри
0
Точно такой лендинг
checkedВаши фото, контакты и тексты
checkedИмя сайта имя.dencompany.ru
checkedБез мелкого шрифта и камней
Лайт
от
5 000
₽/год
Когда сайт пробник понравился и надо расширяться
checkedСамостоятельное имя сайта
checkedИндивидуальные настройки
checkedCRM (личный кабинет продавца)
checkedБез мелкого шрифта и камней
Хочу всё
от
15 000
₽/мес
Когда налажен процесс или не очень, но надо много и не хочется вникать
checkedСамостоятельное имя сайта
checkedИндивидуальный дизайн
checkedCRM (личный кабинет продавца)
checkedПеренос товаров с текущего сайта
checkedБез мелкого шрифта и камней

Профессионалы своего дела

Наша команда

Фото
Fullstack ведущий программист
Денис

О нас говорят

Отзывы клиентов

Фото отзыв
Я — специалист по SMM
Linda
Дизайнер
Фото отзыв
Отзыв 1
Mark
Backend разработчик
Смотреть все

Все, что нужно знать о бизнесе

Блог

Читать блог
Фото блог
08
09.2026
5 основных инструментов для продвижения интернет-бизнеса
Я — специалист по SMM.
Фото блог
08
09.2026
5 основных инструментов для продвижения интернет-бизнеса
Раньше работала дома.
Фото блог
08
09.2026
5 основных инструментов для продвижения интернет-бизнеса
Я работала дома.

Все, что нужно знать о создании сайтов

Статьи

08.09.2026
Очереди в больших скриптах PL/I: практическое руководство для джуна Когда вы начинаете работать с большими скриптами на PL/I, одной из первых структур данных, с которой вы столкнетесь, будет очередь. В отличие от простых массивов, очереди позволяют эффективно управлять потоком данных, особенно когда порядок обработки критичен. В этой статье мы разберем, как реализовать очереди в PL/I, какие подводные камни вас ждут и как избежать типичных ошибок новичка. Что такое очередь и зачем она нужна в PL/I Очередь — это структура данных, работающая по принципу FIFO (First In, First Out). Представьте обычную очередь в магазине: кто пришел первым, тот и обслуживается первым. В программировании на PL/I очереди используются для буферизации задач, обработки событий, передачи данных между подпрограммами и организации асинхронных процессов. В больших скриптах очереди становятся незаменимыми, когда вы имеете дело с потоками данных, которые не могут быть обработаны мгновенно. Например, вы читаете записи из файла, но обрабатываете их медленнее, чем читаете. Без очереди вы либо потеряете данные, либо заблокируете чтение. Основные операции с очередью Любая очередь, независимо от реализации, должна поддерживать как минимум три базовые операции: Добавление элемента (enqueue) — помещает элемент в конец очереди. Извлечение элемента (dequeue) — забирает элемент из начала очереди и удаляет его. Проверка состояния — пуста ли очередь, сколько элементов в ней находится. В PL/I нет встроенного типа "очередь", поэтому вы будете реализовывать её через массивы или связные списки. Для джуна важно понять оба подхода, так как каждый имеет свои преимущества. Реализация очереди на основе массива Самый простой способ — использовать массив с двумя индексами: один указывает на начало очереди, другой на конец. Вот базовая структура: DECLARE 1 QUEUE_CTL, 2 Q_ARRAY(100) CHAR(100), 2 Q_HEAD BIN FIXED INIT(1), 2 Q_TAIL BIN FIXED INIT(1), 2 Q_COUNT BIN FIXED INIT(0); Здесь Q_HEAD — индекс первого элемента, Q_TAIL — индекс следующего свободного места, Q_COUNT — текущее количество элементов. Операция добавления выглядит так: ENQUEUE: PROCEDURE(ITEM); DECLARE ITEM CHAR(100); IF Q_COUNT = 100 THEN CALL HANDLE_OVERFLOW; /* Очередь полна */ ELSE DO; Q_ARRAY(Q_TAIL) = ITEM; Q_TAIL = MOD(Q_TAIL, 100) + 1; Q_COUNT = Q_COUNT + 1; END; END ENQUEUE; Обратите внимание на использование MOD — это создает так называемое "кольцевое" поведение. Когда индекс достигает конца массива, он перескакивает на начало. Это позволяет использовать массив многократно, не сдвигая элементы. Извлечение элемента происходит аналогично: DEQUEUE: PROCEDURE RETURNS(CHAR(100)); DECLARE RESULT CHAR(100); IF Q_COUNT = 0 THEN CALL HANDLE_UNDERFLOW; /* Очередь пуста */ ELSE DO; RESULT = Q_ARRAY(Q_HEAD); Q_HEAD = MOD(Q_HEAD, 100) + 1; Q_COUNT = Q_COUNT - 1; RETURN(RESULT); END; END DEQUEUE; Проблема фиксированного размера Главный недостаток массивной реализации — ограничение по размеру. В большом скрипте вы не всегда знаете заранее, сколько элементов попадет в очередь. Если вы зададите слишком маленький массив, произойдет переполнение. Если слишком большой — вы зря потратите память, что в PL/I критично, так как память выделяется статически. Решение для джуна: используйте динамические массивы через ALLOCATE. Например: DECLARE 1 QUEUE_CTL BASED(Q_PTR), 2 Q_ARRAY(1) CHAR(100), 2 Q_HEAD BIN FIXED, 2 Q_TAIL BIN FIXED, 2 Q_COUNT BIN FIXED; DECLARE Q_PTR POINTER; При добавлении элемента, если массив заполнен, вы выделяете новую память большего размера и копируете данные. Это сложнее, но дает гибкость. Реализация очереди через связный список Для больших скриптов, где размер очереди непредсказуем, лучше использовать связный список. Каждый элемент очереди — это структура, содержащая данные и указатель на следующий элемент. DECLARE 1 NODE BASED(NODE_PTR), 2 NODE_DATA CHAR(100), 2 NODE_NEXT POINTER; DECLARE HEAD_PTR POINTER INIT(NULL); DECLARE TAIL_PTR POINTER INIT(NULL); Добавление элемента в конец списка: ENQUEUE_LIST: PROCEDURE(ITEM); DECLARE ITEM CHAR(100); DECLARE NEW_NODE POINTER; ALLOCATE NODE SET(NEW_NODE); NODE_DATA = ITEM; NODE_NEXT = NULL; IF HEAD_PTR = NULL THEN HEAD_PTR = NEW_NODE; ELSE NODE_NEXT = NEW_NODE; /* Ошибка! Так нельзя */ TAIL_PTR = NEW_NODE; END ENQUEUE_LIST; Стоп! Здесь я допустил типичную ошибку джуна. Вы не можете просто присвоить NODE_NEXT, потому что нужно сначала перейти по указателю к последнему узлу. Правильная логика: ENQUEUE_LIST: PROCEDURE(ITEM); DECLARE ITEM CHAR(100); DECLARE NEW_NODE POINTER; ALLOCATE NODE SET(NEW_NODE); NODE_DATA = ITEM; NODE_NEXT = NULL; IF HEAD_PTR = NULL THEN HEAD_PTR = NEW_NODE; ELSE NODE_NEXT = NEW_NODE; /* Здесь NODE_NEXT относится к новому узлу, а не к старому */ TAIL_PTR = NEW_NODE; END ENQUEUE_LIST; Правильный вариант требует временного указателя на последний узел. Но поскольку мы храним TAIL_PTR, мы можем обратиться к его полю NODE_NEXT: ENQUEUE_LIST: PROCEDURE(ITEM); DECLARE ITEM CHAR(100); DECLARE NEW_NODE POINTER; ALLOCATE NODE SET(NEW_NODE); NODE_DATA = ITEM; NODE_NEXT = NULL; IF HEAD_PTR = NULL THEN HEAD_PTR = NEW_NODE; ELSE NODE_NEXT = NEW_NODE; /* Это все еще неверно! */ TAIL_PTR = NEW_NODE; END ENQUEUE_LIST; Я специально повторяю
06.09.2026
Выбор базы данных для Haskell-проекта: инженерный подход Для senior-разработчика, работающего с Haskell, выбор базы данных — это не просто поиск хранилища, а архитектурное решение, влияющее на всю систему типов, модель конкурентности и стратегию развертывания. В отличие от императивных языков, где драйвер БД — это просто библиотека, в Haskell интеграция с базой данных проходит через строгую систему типов, что требует особого внимания к слою доступа к данным. Критерии выбора для продакшн-систем Прежде чем рассматривать конкретные СУБД, определим жесткие требования, которые предъявляет senior-инженер к persistence-слою в Haskell-экосистеме. Безопасность типов на границе: драйвер должен минимизировать использование `String` и `Dynamic`, предоставляя типизированные запросы и результаты. Композиционность: возможность строить запросы из переиспользуемых частей без потери производительности (без динамической диспетчеризации на каждом вызове). Поддержка транзакций: атомарность операций должна быть гарантирована на уровне API, а не только на уровне SQL-команд. Потокобезопасность: пул соединений должен корректно работать с легковесными потоками (green threads) GHC. Миграции: встроенный механизм версионирования схемы, интегрированный в процесс сборки. Основные кандидаты: сравнение для Haskell PostgreSQL + `postgresql-simple` / `beam` PostgreSQL остается стандартом де-факто для сложных реляционных данных. Для Haskell это лучший выбор, если вам нужны сложные JOIN, оконные функции или полнотекстовый поиск. Библиотека postgresql-simple предоставляет низкоуровневый, но предсказуемый доступ. Для senior-уровня предпочтительнее beam — это полноценная EDSL (встраиваемый предметно-ориентированный язык), которая генерирует SQL на этапе компиляции, что исключает синтаксические ошибки и позволяет проверять корректность запросов статически. Ключевое преимущество: типобезопасность. Вы определяете схему как типы Haskell, и компилятор гарантирует, что запрос не обратится к несуществующему столбцу. Это критически важно для поддержки крупных проектов, где рефакторинг схемы без ошибок — задача нетривиальная. SQLite + `sqlite-simple` / `persistent` Для встраиваемых решений, прототипов или edge-устройств SQLite — идеальный кандидат. Библиотека persistent (из экосистемы Yesod) предлагает высокоуровневый DSL, который автоматически генерирует миграции и поддерживает несколько бэкендов. Однако для senior-разработчика важно понимать ограничения: SQLite не поддерживает конкурентную запись на уровне сервера, и при масштабировании на несколько процессов возникнут блокировки. Рекомендация: использовать SQLite только в том случае, если вы уверены, что нагрузка на запись не превысит однопоточный режим, или для тестовой среды, где важна скорость развертывания. Redis + `hedis` Если ваш проект — это кэш, сессии или очереди с высокой скоростью, Redis — правильный выбор. Библиотека hedis полностью асинхронна и хорошо интегрируется с `async` и `stm`. Однако помните: Redis не является системой хранения "источника правды". Для senior-инженера это означает, что необходимо продумать стратегию восстановления данных и инвалидации кэша на уровне бизнес-логики, а не полагаться на встроенные механизмы. Архитектурный паттерн: функциональное ядро и императивная оболочка Независимо от выбранной СУБД, для Haskell-проекта критически важен паттерн разделения чистого кода и эффектов. Рекомендуется следующая структура: Чистое ядро: все бизнес-правила, работающие с абстрактными типами данных (например, `User`, `Order`). Здесь нет ни одного упоминания SQL или соединений. Репозиторий: интерфейс (typeclass) с методами типа `getUser :: Id -> m (Maybe User)`, где `m` — монада эффектов. Реализация: конкретная реализация для PostgreSQL или Redis, которая инкапсулирует все SQL-запросы и управление транзакциями. Такой подход позволяет тестировать бизнес-логику с фейковыми репозиториями (in-memory), а также менять базу данных без изменения кода ядра. Для senior-уровня это обязательное требование, иначе вы получите связанный с драйвером код, который невозможно поддерживать. Управление транзакциями и конкурентность В Haskell транзакции должны быть явными. Использование `STM` (Software Transactional Memory) для управления конкурентным доступом к данным в памяти — хорошая практика, но не путайте это с транзакциями базы данных. Для БД используйте `withTransaction` из библиотеки драйвера. Важно: не выполняйте длительные IO-операции внутри SQL-транзакции, так как это блокирует пул соединений. Вместо этого проектируйте короткие транзакции, которые выполняются за миллисекунды. Также обратите внимание на пул соединений. Библиотека resource-pool является стандартом. Настройте размер пула равным количеству ядер процессора, умноженному на коэффициент ожидания (обычно 2-3). Не создавайте пул на каждое действие — это приведет к исчерпанию файловых дескрипторов. Миграции и версионирование схемы Для production-системы миграции должны быть идемпотентными и воспроизводимыми. Рекомендую использовать библиотеку squeal или postgresql-migrations. Ключевое правило: каждая миграция — это отдельный модуль Haskell, который экспортирует функцию `Migration`. Это позволяет запускать миграции в том же бинарнике, что и приложение, что упрощает деплой в Kubernetes. Избегайте генерации схемы на лету через `persistent` в production. Это удобно для разработки, но опасно для продакшена, так как может привести к неожиданным изменениям схемы при обновлении версии приложения. Заключение и рекомендация
03.09.2026
Оптимизация кода на VB.NET для начинающих Оптимизация кода — это не только ускорение работы программы, но и улучшение её читаемости, поддержки и надёжности. Для новичка в VB.NET важно понять базовые принципы, которые помогут писать эффективный код с самого начала. В этой статье мы разберём простые, но действенные приёмы, которые вы сможете применить сразу. 1. Используйте Option Strict и Option Explicit Первое, что стоит сделать в любом новом проекте — включить строгие проверки типов. В начале файла кода или в настройках проекта укажите: Option Strict On — запрещает неявные преобразования типов, которые могут замедлять выполнение и вызывать ошибки. Option Explicit On — требует обязательного объявления переменных. Это исключает опечатки в именах и делает код понятнее. Эти две строки заставят компилятор работать строже, но именно это помогает избежать скрытых проблем на раннем этапе. 2. Выбирайте правильные типы данных Для новичка типичная ошибка — использовать Integer или Long везде, где нужно число. Однако для дробных значений лучше подходит Double или Decimal. Если вы храните большие объёмы данных в памяти, тип Decimal работает медленнее, чем Double, но точнее. Для целых чисел всегда выбирайте самый маленький подходящий тип: Byte, Short, Integer. Это экономит память и ускоряет операции с массивами. 3. Избегайте конкатенации строк в циклах Сборка строк через оператор & внутри цикла — это самая частая причина медленной работы. Каждая операция создаёт новую строку в памяти, что очень затратно. Вместо этого используйте StringBuilder: Плохо: result = result & item внутри цикла. Хорошо: создайте Dim sb As New StringBuilder(), затем sb.Append(item) в цикле, а после цикла получите результат через sb.ToString(). Это ускорит код в десятки раз, если элементов много. 4. Работа с массивами и списками Для новичка важно понимать разницу между массивом фиксированной длины и динамическим списком List(Of T). Если вы часто добавляете или удаляете элементы, используйте List(Of T). Если количество элементов известно заранее и не меняется — используйте обычный массив. При обращении к элементам всегда используйте For Each вместо For i = 0 To ..., если вам не нужен индекс. Это делает код чище и часто быстрее. 5. Убирайте повторяющиеся вычисления Если вы вычисляете одно и то же значение несколько раз, сохраните его в переменную. Например, вместо вызова функции Math.Sqrt(x) в каждой итерации цикла, вычислите его один раз до цикла. Это особенно важно для сложных вычислений или обращений к базам данных. 6. Используйте структуры вместо классов для мелких объектов Если у вас есть небольшие объекты, которые содержат только пару полей (например, координаты точки), объявите их как Structure, а не как Class. Структуры хранятся в стеке, а не в куче, что снижает нагрузку на сборщик мусора и ускоряет работу. Однако помните: структуры не должны быть изменяемыми, если вы не уверены в их использовании. 7. Обработка исключений без потери производительности Исключения (Exception) — это дорогостоящая операция. Не используйте их для управления логикой программы. Например, не проверяйте наличие ключа в словаре через Try...Catch. Вместо этого используйте метод ContainsKey или TryGetValue. Исключения должны быть только для действительно исключительных ситуаций, таких как ошибки ввода-вывода. 8. Отложенная загрузка (Lazy Loading) Если у вас есть свойство, которое создаёт тяжёлый объект, но используется не всегда, примените Lazy(Of T). Это позволит создать объект только тогда, когда он действительно понадобится. Это не ускоряет выполнение каждой операции, но экономит память и время запуска программы. 9. Профилируйте свой код Не оптимизируйте вслепую. Сначала запустите профилировщик Visual Studio (меню Analyze → Performance Profiler), чтобы найти узкие места. Часто оказывается, что проблема не в том, где вы думали, а в обращении к базе данных или в ожидании ввода-вывода. Оптимизируйте только те участки, которые действительно замедляют работу. 10. Практический пример для новичка Представьте, что вам нужно посчитать сумму квадратов чисел от 1 до 10000. Плохой вариант — использовать конкатенацию строк или дробный тип. Хороший вариант: Объявите переменную Dim total As Long = 0. Запустите цикл For i As Integer = 1 To 10000. Внутри цикла добавьте total += i * i. Этот код будет работать мгновенно, потому что вы используете целочисленную арифметику и простой цикл без лишних операций. Заключение Оптимизация — это навык, который приходит с практикой. Начните с этих простых правил: включите строгие проверки, выбирайте правильные типы данных, избегайте конкатенации строк в циклах и не злоупотребляйте исключениями. Постепенно вы научитесь чувствовать, где код может работать лучше. Помните, что главная цель — писать понятный и поддерживаемый код, а скорость придёт следом за правильными привычками. Всегда измеряйте результат до и после изменений, чтобы убедиться, что ваша оптимизация действительно полезна. Следуя этим советам, вы сделаете свои программы на VB.NET быстрее и надёжнее уже сегодня.
Открыть все статьи
bg_1
Остались вопросы? Задавайте их немедленно.
мы проконсультируем совершенно бесплатно