Дед опять забыл выпить таблетки

Сохраняй посты, комментируй и ставь оценки

Все началось с рабочей задачи по реализации весьма специфичной web-панели мониторинга и управления различным оборудованием, в которой нужно было получать данные и отправлять команды по разнообразным сценариям (периодический опрос, подписка на websocket-события, https-запросы и т.д.), а также выводить состояние и графики в реальном времени, динамически комбинируя различные источники данных в разных виджетах.
Кроме того, у панели было предусмотрено несколько настраиваемых режимов работы, которые переключались по запросу пользователя, что требовало массового управления маршрутами потоков данных внутри системы.
В первой итерации с использованием RxJS получилось много неструктурированного и сложно поддерживаемого кода, так как архитектура на RxJS вынуждала императивно описывать перестроение топологии при изменении правил маршрутизации «на лету». В нашем специфичном кейсе с динамическими виджетами это приводило к сайд-эффектам и сложностям в отладке. Кроме того, местами размывалась строгая типизация и мы лишались compile-time гарантий. Так появилась идея разработать собственное решение на основе альтернативной концепции — модели графа потоков данных.
В результате получившаяся модель продемонстрировала предсказуемое поведение и низкую связность компонентов, а кодовая база сократилась на ~30% и приобрела более декларативный вид. Убедившись в эффективности решения, мы решили оформить его в виде отдельной библиотеки с открытым исходным кодом — Transferum.
Не совсем. Классические FRP-библиотеки (RxJS, Bacon, Most) построены вокруг единственного примитива (Observable). Transferum же основан на композиции различных типов узлов с явно определенным поведением.
Каждый узел в графе потоков распространения данных явно декларирует свои способности: может ли он принимать данные через push, отдавать через pull, распространять полученный сигнал подписчикам, опрашивать источник, фильтровать, блокировать поток и т.д.
Объявленные узлом возможности являются одновременно флагами для использования в runtime и compile-time гарантиями наличия соответствующих методов, определяющих его поведение.
Ключевая идея: поведение системы описывается как композиция независимых возможностей, которые одновременно определяют тип, реализацию и правила взаимодействия.
Концептуальная и архитектурная основа — capability flags system. Каждый трансфер реализует CommunicationContractInterface — набор булевых флагов, определяющих его возможности.
Флаги isPushable, isPullable, isSubscribable, isGate и другие — это не просто свойства объекта. Это метаданные, которые:
Один набор флагов — три потребителя. Это единый источник истины для всей системы.
Когда флаг равен true, соответствующий метод входит в TypeScript-интерфейс трансфера. Это позволяет предоставлять трансфер пользователю вот так:
Transfer
// гомогенный трансфер: T -> [ Transfer ] -> TИли вот так:
Transfer
// гетерогенный трансфер: TInput -> [ Transfer ] -> TOutputИменно в таком формате типов фабрики в библиотеке возвращают трансферы. Например:
const pushChannel: Transfer;
const converter: Transfer;
// содержат методы push(), subscribe()
const manualBuffer: Transfer;
// содержит методы push(), pull(), trigger()
// trigger() в данном случае нужен, чтобы сделать запушенное значение
// доступным для чтения через pull()
const pollingProxy: Transfer
// содержит методы pull(), subscribe(), trigger(), activate(), deactivate(), toggle()
// может быть связан с pullable-источником благодаря поддержке поллингаЭта «магия» работает в compile-time благодаря несколько замысловатой системе вычислимых типов:
type FeatureMap =
F extends { readonly isPushable: true } ? Pushable & { readonly isInput: true } :
F extends { readonly isPollingProxy: true } ? PollingProxy & { readonly isInput: true, readonly isOutput: true } :
F extends { readonly isPullable: true } ? Pullable & { readonly isOutput: true } :
F extends { readonly isSubscribable: true } ? Subscribable & { readonly isOutput: true } :
F extends { readonly isTriggerable: true } ? Triggerable :
F extends { readonly isGate: true } ? Gate :
F extends { readonly isAsyncPushable: true } ? AsyncPushable & { readonly isInput: true } :
F extends { readonly isAsyncPollingProxy: true } ? AsyncPollingProxy & { readonly isInput: true, readonly isOutput: true } :
F extends { readonly isAsyncPullable: true } ? AsyncPullable & { readonly isOutput: true } :
F extends { readonly isAsyncTriggerable: true } ? AsyncTriggerable :
unknown;
type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type ResolveFeatures = UnionToIntersection;
type IsDuplexFeatures =
ResolveFeatures extends { readonly isInput: true, readonly isOutput: true }
? { readonly isDuplex: true }
: unknown;
type ExplicitTransfer = BaseTransferInterface
& ResolveFeatures
& IsDuplexFeatures;
export type Transfer<
TInOrAll,
TOutOrFeatures extends any[] | any,
TFeaturesOrUndefined extends any[] | undefined = undefined,
> = TFeaturesOrUndefined extends any[]
? ExplicitTransfer
: TOutOrFeatures extends any[]
? ExplicitTransfer
: never;Трансфер определяет своё поведение (push, pull, subscribe и др.), но никогда не ссылается и не проверяет класс другого трансфера. Он не знает, что является upstream или downstream — лишь выполняет свой контракт. Пользователь может создать свой трансфер, объявить и реализовать его возможности — и он органично и бесшовно впишется в экосистему.
Мост инспектирует capability flags, а не имена классов. Нет цепочки instanceof, нет переключения по имени класса. Любой output-трансфер может быть соединен с любым input-трансфером — при условии совместимости их флагов, о чем мы поговорим чуть ниже. Это применимо и к тем узлам, которые еще не существуют и будут созданы пользователем.
В Transferum undefined означает «нет данных», а не «пустое значение». Оно подавляется на уровне внутренней реализации менеджера подписок — подписчики никогда не уведомляются с undefined. При этом для явных маркеров пустых значений можно использовать null. Мы сознательно пошли на этот компромисс, чтобы избежать runtime-оверхеда и сохранить нативную скорость работы на плотных потоках данных.
Функция linkTransfers(lhs, rhs) соединяет output-трансфер (lhs) с input-трансфером (rhs) с автоматическим выбором стратегии связывания на основе возможностей этих трансферов:
Protocol-oriented design: механизм не спрашивает «какой это класс?» — он выясняет, какие у него есть возможности. Любая пара трансферов с совместимыми возможностями является linkable. Добавление нового класса трансфера требует только объявления его флагов и реализации соответствующих методов — как связать его с другим трансфером, связующий алгоритм разберется сам.
import { createPushChannelTransfer, createSinkTransfer, linkTransfers } from 'transferum';
const source = createPushChannelTransfer();
const target = createSinkTransfer({ callback: (x) => console.log(x) });
const link = linkTransfers(source, target);
source.push(22);
link.unsubscribe(); // разорвать связьСинхронные и асинхронные трансферы сосуществуют и могут быть связаны между собой. linkTransfers() предпочитает sync-связывание, когда это возможно, а async-стратегии применяет только когда sync неприменим. Нет отдельного «асинхронного мира».
Ряд асинхронных трансферов (AsyncSinkTransfer, AsyncWriteTransfer, AsyncConvertTransfer, AsyncConditionTransfer) поддерживают необязательные поля в конфигурации: maxConcurrency, bufferSize и onBufferOverflow — для ограничения параллельных async-операций, очереди избыточных данных и graceful-обработки переполнения. По умолчанию — неограниченная обработка, без буферизации.
Transferum использует единую модель обработки ошибок для всех трансферов. Каждый трансфер, который может столкнуться с runtime-ошибкой, принимает опциональный onError-хэндлер в своей конфигурации.
Вот так можно просто и декларативно описать опрос и агрегирование данных из нескольких источников:
import {
createMergeTransfer,
createConditionTransfer,
createThrottleTransfer,
createAsyncSinkTransfer,
createAsyncPollingSourceTransfer,
CompositeTransferBuilder,
} from 'transferum';
const sensor1 = createAsyncPollingSourceTransfer({
fetcher: () => Promise.resolve({ sensorId: 1, temperature: 25, humidity: 50 }),
interval: 50,
activated: true,
});
const sensor2 = createAsyncPollingSourceTransfer({
fetcher: () => Promise.resolve({ sensorId: 2, temperature: 26, humidity: 55 }),
interval: 50,
activated: true,
});
const sensorsAggregator = createMergeTransfer({
sources: [sensor1, sensor2],
});
const alertPipeline = CompositeTransferBuilder
.start(sensorsAggregator)
.to(createConditionTransfer({
shouldAccept: (d) => d.temperature > 95,
}))
.to(createThrottleTransfer({ interval: 5000 }))
.finish(createAsyncSinkTransfer({
callback: (sensorData) => {
console.warn(Alert! Sensor ${sensorData.sensorId}: temperature is ${sensorData.temperature});
}
}));
sensorsAggregator.subscribe((sensorData) => console.log('Debugging', sensorData));А вот как можно организовать динамический роутинг:
import {
createPushStoredChannelTransfer,
createBridgeMultiSelector,
createBridgeSelector,
createPassBridge,
} from 'transferum';
// Вариант 1. Выбор одного реципиента
const commandRouter = createBridgeSelector({
bridges: {
light: createPassBridge({ source: commandChannel, target: lightController, activated: false }),
thermostat: createPassBridge({ source: commandChannel, target: thermostatController, activated: false }),
lock: createPassBridge({ source: commandChannel, target: lockController, activated: false }),
},
initialKey: 'thermostat', // выбор по умолчанию
activated: true,
owned: true,
});
commandRouter.select('lock'); // выберем другой вариант
// Вариант 2. Выбор нескольких реципиентов
const loggersRouter = createBridgeMultiSelector({
bridges: {
prometheus: createPassBridge({ source: metricsChannel, target: prometheusWriter, activated: false }),
elk: createPassBridge({ source: metricsChannel, target: elkWriter, activated: false }),
sentry: createPassBridge({ source: metricsChannel, target: sentryWriter, activated: false }),
},
initialKeys: ['prometheus', 'elk'], // будут активированы по умолчанию
activated: true,
owned: true,
});
loggersRouter.check('elk'); // теперь выбраны все три
loggersRouter.uncheck('prometheus'); // выбраны elk, sentry
loggersRouter.select(['prometheus', 'sentry']); // выбраны prometheus, sentryА вот пример c организацией игровой механики:
import {
createDebounceTransfer,
createConvertTransfer,
createMapOperator,
createBridgeSelector,
createPassBridge,
linkTransfers,
} from 'transferum';
// Источник: debounce нажатий клавиш (16ms ~ 1 кадр при 60 FPS)
const rawInputSource = createDebounceTransfer({ delay: 16 });
// Конвертер: KeyboardEvent -> код клавиши
const inputConverter = createConvertTransfer({
operator: createMapOperator((event) => event.code),
});
// Целевые системы-потребители пользовательских событий
const carSystem = { push: (cmd: string) => console.log(🚗 Car executing: ${cmd}) };
const planeSystem = { push: (cmd: string) => console.log(✈️ Plane executing: ${cmd}) };
const menuSystem = { push: (cmd: string) => console.log(📋 Menu processing: ${cmd}) };
// Роутер: динамическое переключение между игровыми контекстами
const gameplayRouter = createBridgeSelector({
bridges: {
driving: createPassBridge({ source: inputConverter, target: carSystem }),
flying: createPassBridge({ source: inputConverter, target: planeSystem }),
ui: createPassBridge({ source: inputConverter, target: menuSystem }),
},
initialKey: 'driving', // игрок начинает в машине
activated: true,
});
// Связывание источника с конвертером с автовыбором стратегии
linkTransfers(rawInputSource, inputConverter);
// Демонстрация:
// Игрок в машине нажимает "W"
rawInputSource.push(new KeyboardEvent('keydown', { code: 'KeyW' }));
// Через 16ms: 🚗 Car: KeyW
// ==================
// Переключаемся на самолёт
gameplayRouter.select('flying');
// Та же клавиша теперь управляет самолётом
rawInputSource.push(new KeyboardEvent('keydown', { code: 'KeyW' }));
// Через 16ms: ✈️ Plane: KeyWБиблиотека подойдет для:
Буду рад, если вы заглянете в репозиторий, попробуете библиотеку в деле и поделитесь замечаниями — обратная связь поможет сделать Transferum лучше.
P. S. Если вы сталкивались с похожими задачами и решили их как-то иначе — буду рад прочитать о вашем опыте в комментариях.
1. Нельзя гуглить, надо помнить: можно и нужно. Профессионалы гуглят синтаксис, который знают десять лет. Память —она для логики, а не для команд.
2. Нельзя копировать чужой код: можно, если разбираете что скопировали и свой тоже пишете. Копипаста с пониманием — нормальная работа. Проблемой она становится без понимания.
3. Нельзя браться за проект, пока не выучил основы: можно. Все основы выучить невозможно. Проект сам покажет чего не хватает — и тогда вы это выучите с интересом.
4. Нельзя изобретать велосипеды: на этапе учёбы — нужно. Собранный своими руками велосипед учит в сто раз больше купленного.
5. Нельзя задавать глупые вопросы: глупых вопросов нет. Есть люди, которым лень объяснять — ну так идите к другим, таких много. У меня вот есть целое сообщество таких людей, которые на любой вопрос готовы ответить, и оно не одно такое, я уверен.
6. Нельзя писать плохой код: можно. Работающий кривой код лучше идеального ненаписанного, особенно когда только учитесь. Улучшите потом — когда будет что улучшать. Да и переписывание кода это одна из вещей, которые профессионалы постоянно делают, потому важно тренироваться это делать
Решил я поиграться в ардуинки. Выбор пал на ESP32-C3 с 0,42-Дюймовым OLED-Модулем за какие-то там 200 руб. Пока оно едет из Китая думаю, дай потыкаюсь со средой разработки. Раньше дела с ней не имел.
Оказывается есть две версии под линухой. Одна на электроне v. 2.3.8 и старая 1.8.19 на Java. Но об ентом я ещё не знал, естественно выбрал поновее и получил засаду - вечно крутится на заставке. Думал наши разрабы АльтЛинухи накосячили с опакечиванием, перепробовал кучу сборок со стороны (не из репов Альта) - везде одно и тоже - вечно крутится заставка. Запустил иде из консоли и увидел туеву хучу ошибок с невозможностью чего-то там скачать. Направление гугления тут же изменилось и выяснилось, что www.arduino.cc лочит россиян из-за Cloudflare (эдакий сервис для защиты сайтов от хакерских атак). Что и и как произошло между Cloudflare и РФ вспоминать не стал и начал думать как пустить Ардуино-ИДЕ через тор-сеть. Придумать не удалось
Вот тогда я и узнал, что разница между старой и новой ИДЕ исключительно в интерфейсе, более того, настроек сети в новой версии я не нагуглил (возможно пальчиками где-то в конфигах и прописывается). А вот в старой версии пожалуйста Файл-->Настройки->Сеть:

При этом все библиотеки и "платы" начали загружаться. И да, версия из репозиториев Альта. Между прочим, ардуинки часто используются не только как хобби, но и в образовательных целях. Но политиканам насрать на это.
Всем доброго времени суток, с вами на связи Lis-ST. Это мой первый пост, не на данном сайте, а вообще, (не зря же я состоял в "Лиге лени"), но на общем фоне нейроистерии и всё большего распространения нейросетей кто-то должен хотя бы минимально объяснить, что же такое "Нейросеть", а так-же как правильно с ними работать и чего в принципе от них можно ожидать. В данном посте затронем только самые основы.
Здесь можно было бы надергать текста с вики, различных ресурсов, прогнать всё это через нейронку и вставить тысячи нудных строк на 2 часа чтения, но мне лень, поэтому пробежим вкратце по основным моментам.
Сама идея, создать что-то, что работало бы схожим образом с мозгом, как ни удивительно, появилась задолго до появления компьютеров. Первая математическая модель нейрона была создана ещё в 1943 году.
Первая же нейросеть появилась в 1950 году, примерно в это время начали появляться первые предшественники современных компьютеров. Умела она немного, могла только отличить круг от квадрата, или квадрат от круга, может ещё от треугольника, но ничего сложнее. И на этом всё развитие данной идеи практически остановилось по банальной причине - недостаток вычислительной мощности. Но, это не значит, что от идеи полностью отказались. Её постепенно развивали, предлагались новые модели, способы тренировки но очень существенный толчок в развитии нейронок я связываю с 2015 годом, именно в этот год была основана OpenAI и в 2016 году она положила начало нейроистерии, выпустив в публичный доступ, нет, не нейросеть, а платформу для разработки и обучения нейронок.
Тут определённо стоило бы начать с определения того, что такое нейрон, но чем больше будет определений, формул и подобного, тем хуже для восприятия будет текст, поэтому простыми словами - это то, что может принимать, обрабатывать, хранить и передавать информацию. Нейронная сеть - попытка повторить данные свойства нейрона.

На сегодняшний день придумано множество разных нейронок, разберём самую примитивную, как на картинке выше, она состоит из трёх слоёв: входной, скрытый и выходной.
Входной слой: на него в нейронку поступают какие либо данные - текст, числа, изображения, координаты и т.п.. Здесь данные приводятся к удобному для нейронки виду (обычно это числа от 0 до 1), чтобы слишком большие значения не «свели сеть с ума» или не спалили ваш компьютер. У вас закономерно возникнет вопрос, как можно фото котика представить в виде обычного числа? Всё просто, это не одно число, а тысячи, на каждый пиксель выделяется по входному нейрону, который просто задаёт удерживает числовый значения. Так, для картинки 1024 на 1024 пикселя нам потребуется 1 048 576 входных нейронов.
Скрытый слой, на нем остановимся намного подробней, так как он и ответственен за всю магию нейронных сетей. Это по сути «сито», которое фильтрует данные, чтобы найти в них смысл. Нейрон скрытого слоя берет данные на входе, преобразованные в числовые значения и умножает их на «важность» (вес). Например, при поиске кошки на фото — острые уши и хвост важны, а цвет фона — нет. Затем все результаты складывает в одно число, а полученная сумма сравнивается с порогом, который сформировался при обучении нейронки. Если число достаточно большое, нейрон «просыпается» и передает сигнал дальше. Если нет — молчит.
Выходной слой — это финал, где нейросеть выдает готовый ответ. Он превращает сложные вычисления скрытых слоев в понятный формат. Если в скрытых слоях нейроны «ищут признаки», то в выходном слое они просто «голосуют» за итоговый результат.
В попытках получить наилучший результат, уменьшить требуемые вычислительные мощности для работы нейронок, постоянно придумывались новые варианты их архитектуры, способов тренировки, обработки данных. Так появились на свет нейросети с несколькими скрытыми слоями, каскады нейросетей, свёрточные нейронки и многие другие.
Здесь всё очень сложно, пока на этом не будем заострять внимания, но для общего понимания вот очень краткий список основных архитектур и более подробная картинка для наглядности:

Пока мы коснулись только того, из чего состоит нейросеть, самые основы её работы, но без более подробного разбора мы не придём к пониманию того, почему же у меня от некоторых постов, связанных с нейросетями, весьма не слабо горит кресло. К данному вопросу мы перейдём завтра.
Как-то не так я себе это представлял, капля текста, две картинки, куда два часа исчезли то?
Короткий познавательный пост, основанный на моей больной фантазии. Все совпадения случайны.
Обратился как-то один человек на профильном форуме с вопросом, как запустить (не сказать чтобы старую) игру на Windows 10. Игра (хотя я бы назвал это технодемо) была разработана на легендарном XNA 4.0 (земля пухом) и планировалась к выпуску через Steam Greenlight (и ему земля пухом) еще в далеком 2012, после чего заброшена и убрана со всех магазинов.
Игра была куплена тем человеком еще в тот момент, когда она легально продавалась. А спустя годы она просто перестала подавать признаки жизни. $5 за полную версию уплочены, а поиграть нельзя. Обидно.

Так как типовые решения по типу установки XNA Redistributable не помогли, то был предложен единственно возможный выход из ситуации - отладка через dnSpy.
Внимание: обратная разработка хоть прямо и не запрещена законодательно, но разрешенные случаи весьма ограниченны. В других странах разрешен явный запрет любых манипуляций через лицензию, превращающий это занятие во вполне наказуемое. Не повторяйте дома.
Так как ПО было приобретено легально и оно не работает, то этот теоретический случай допустим в соответствии со статьей 1280 гражданского кодекса.
Демонстрировать буду на бесплатной демоверсии, скачать можно здесь.

Театр начинается с вешалки, а программа на C# с атрибутов сборки. Они нам сейчас не сильно интересны, поэтому пытаемся сразу запускать отладку. С неизвестными программами лучше использовать виртуальную машину, так как может быть встроена какая-нибудь пакость для кулхацкеров, но мне можно и так.

Ошибка. Открываем стек вызовов и ищем место, где произошел провальный запрос в сеть.

А вот и виновник торжества. По сообщению из исключения можно сделать вполне однозначный вывод, что проблемой является скупердяйство и непредусмотрительность автора, который решил перестать платить за домен и хостинг, а вместе с этим случайно (а может и намеренно) сделал запуск игры невозможным без интернета. Так как на сайте заявлено отсутствие DRM, то это явно недосмотр.
Что тут происходит?

Если почитать документацию на XNA, то этот метод предназначен для загрузки контента и вызывается автоматически при запуске игры. Первым делом выполняется инициализация некоторых вещей, после чего открывается файл настроек (options.xml) и начинает последовательно считываться. Как только считываются параметры аккаунта для таблицы рекордов, то происходит отправка логина+пароля на официальный сайт и попытка получить ID. Если сервер возвращает Failure, то открывается диалог входа, который можно пропустить. Если сервер возвращает число, то игра запускается. А вот если сервер недоступен, то вылетает исключение, которое никак не обрабатывается и приводит к молчаливому вылету.

На этом этапе есть 2 варианта действий:
Нормальные герои всегда идут в обход, так что переходим к написанию небольшого патча. (На самом деле, о первом варианте я додумался не сразу. Более того, полная версия зависает при убирании этого пункта из настроек).
Выделяем строчку с запросом и нажимает "Изменить инструкции IL". Открывается окно с заголовком "Изменение тела метода".

Это - язык MSIL. Он очень прост и сильно напоминает Forth, на самом деле, но новичка может отпугнуть.
Ключевым элементом является стек. Что такое стек я уже когда-то рассказывал. Это некоторое место, на вершину которого можно последовательно добавлять значения (числа, строки и прочие объекты), а так же снимать их в обратном порядке.

Тут мы добавляем на стек:
Потом мы вызываем String::Concat(), принимающий в себя 4 строки и соединяющий их вместе. При вызове метода будут сняты 4 верхние строки со стека и добавлена новая строка.
Вызов следующего метода (WebClient::DownloadString()) снимает строку, полученную от String::Concat(), и веб-клиент. В результате должна остаться только 1 строка, возвращенная WebClient::DownloadString().
Следующим этапом мы должны снять эту строку со стека и сохранить её в локальную переменную 1, но этого не произойдет, так как WebClient::DownloadString() всегда будет проваливаться и выбрасывать исключение. Исключение, если его не обработать сразу, будет раз за разом проваливаться глубже по стеку вызовов, пока не достигнет обработчика или дна. Так как никаких обработчиков по пути нет, то исключение достигает дна и приводит к вылету.
Чтобы избежать получения исключения, мы можем убрать всё, что предназначено для запроса в сеть. Примерно вот так:

8 инструкций превратились в 2: загрузка строки с числом (которое может быть любым) и сохранение в локальную переменную под номером 1.
Сохраняем и проверяем результат.


Из декомпилированного кода полностью исчез кусок с запросом. Осталась лишь загрузка константной строки и сравнение с "Failure", которое всегда будет успешным.
Сохраняем модуль и пытаемся запускать опять.



С этой задачей успешно справился человек, который не смыслит в программировании вообще ничего. По его словам, он джва года ждал эту игру!

Но не всё так просто. Если попытаться пройти любой уровень, то вылетит то же самое исключение.
Для исправления точно так же выделяем строку и опять редактируем инструкции в теле метода. Но так как отправка статистики нонче ни на что не влияет, то просто удаляем выделенные инструкции без остатка.
А для надежности выделяем WebClient, нажимаем "Анализировать" и смотрим, где еще может ждать нас подлянка.

В Finish.Update() мы ошибку только что исправили, в Game1.LoadContent() исправили в самом начале, а Login.button1_Click(), Program.Main() и Title.Update() нас не интересуют, так как достаточно просто не делать того, что приведет к обращению в сеть (а конкретно: не открывать диалог входа, не указывать никакие аргументы при запуске и не пытаться авторизоваться через главное меню (вроде бы не реализовано)).
Вуаля. Игра работает как прежде.
Числа Фибоначчи - это последовательность, где каждое очередное число равно сумме двух предыдущих. 1 = 0+1, 2 = 1+1, 3 = 2+1, 5 = 3+2, 8 = 5+3, ...
В программировании для вычисления этой и подобных последовательностей (факториалы, степени, если нет прямой возможности...) используются циклы.
Цикл представляет собой некоторые условия: начальные условия, условия завершения, изменение данных для достижения условий завершения. (Заметка для тех кто в теме, это касается любых циклов, а не только цикла for)
А вот то, что делается внутри цикла называется ТЕЛОМ цикла.
P.S. Ага, очередной кликбейтный заголовок.
Привет. Я продолжаю разрабатывать сервер для Lineage 2 C1 на JavaScript Проект
Из-за ошибки в расчёте дистанции для атаки в поведении NPC произошёл сбой: он не может выбрать, атаковать ли ему или убегать.
Блог про разработку сервера для Lineage 2 в телеге https://t.me/lineage2js
Большинству пользователей известно, почему возник Вомбат. На этом я не стану заострять внимание.
А хочу обратить внимание на сложность разработки подобных сайтов. Даже на уровне MVP (минимально жизнеспособный продукт).
Даже если есть готовые наработки, то в любом случае надо продумать: модель, то есть схему базы данных, дизайн веб-морды, общее взаимодействие, то есть бизнес-модель,...
Независимо от всего разрабатываю своего бобра (это скорее будет что-то вроде Хабра, а не очередной аналог Пикабу). И уже раз десять сносил БД из-за невозможности внести изменения (миграции). Ну, может я такой рукожоп и просто не знаю как правильно сделать. 🤷♂️
Нужно продумать внешний вид сайта. Тут сильно помогает такой инструмент как Figma. Да, дизайнеры Figm-ой занимаются. 🙃После пары настроек пользоваться одно удовольствие. Но мало задизайнить. Надо это ещё и в код перенести. А это уже вёрстка. Довольно нудное занятие.
В общем, чего хотел сказать, захотите сделать свой Пикабу с блекджеком и плюхами, то готовьтесь к тому, что заебётесь...
Всем спасибо. Все свободны.
Всем доброго здравия, уважаемые вомбатовцы (я не знаю, как у вас тут принято обращаться друг к другу, надеюсь, никого не обидел).
Кто-то из вас знает, кто-то нет, но где-то месяц тому назад мы весьма немаленькой компанией были вынуждены покинуть насиженное место. Некоторых из нас, в частности меня, заклеймили злодеем вором сайтов, но я человек простой, мне во всей этой грязи возиться лень, я пару раз какашкой кинулся. да самоустранился. «Собака лает, караван идёт».
В итоге что получилось? Решили мы написать новый проект с нуля. Когда-то давно, ещё тогда, когда был большой исход с Пикабу (как раз когда и Вомбат появился на свет). Я решил написать свою площадку. В итоге довёл её за пару месяцев до состояния, что этим можно было пользоваться, но потом работа отъела ресурсы, а когда решил вернуться опять к проекту, уже вышли в открытый доступ варианты Того же Вомбата и Капибары. Свой я отложил в долгий ящик (искренне был уверен, что забросил навсегда).
Но тут вот как получилось.
Использовать код того проекта мне показалось не самой хорошей идеей, так как и технологии устарели и я чуть умнее стал, поэтому начал писать с нуля, но оглядываясь на опыт прошлой писанины.
В качестве основы был выбрал фреймворк Laravel. Почему? Да потому что я его знаю вдоль и поперёк и на нём подобные штуки поднимаются достаточно легко и быстро.
Большой вопрос по фронтэнду. Я вроде как фулстек, но JS фреймворки, аля VUE и React считаю излишеством, тем паче, что открытого АПИ у проекта всё равно нет и не планируется. Поэтому решил совместить удобное с быстрым. И тут мне под руки попалась такая интересная фигня (ну как попалась, чат ГПТ мне её посоветовал. Говорит: «Потыкай в неё палочкой, перспективная фигня»), как Livewire.
И вот, совместив стандартный ларавелевский шаблонизатор, вот эту вот вундервафлу и обычный ванильный JS, удалось собрать проект буквально за пару недель. Пришлось правда отложить все прочие свои поделки, но тут уж как получается 🤷♂️
Ещё две недели активный тестов небольшой, но очень умелой группой пользователей. И вот сегодня я открыл сайт для общего польования.
Согласно пункту правил 10, я, вроде как могу тут делиться ссылками на свои ресурсы, но с другой стороны, этот ресурс не полностью мой, там за ним целая толпа стоит. Поэтому не буду гневить ВомбатоМодераторов. Тем более, что цели переманить людей отсюда к нам у меня нет, я просто хвастаюсь ☺
Что могу сказать по итогу: задачка интересная. И работы предстоит по её доделыванию уйма, но и опыт это прям хороший. Особенно по работе с БД. Так что, готов ответить на вопросы, поделиться, если у кого-то есть что спросить. А если кто-то вдруг ещё и шарит в описанных мной технологиях, буду раз помощи)
Linux, а для меня основная ОС для дома (удевлены?), является продолжателем самых первых операционок семейства UNIX, фактически - родоначальника всех ОСей. И есть в ней такая замечательная "либа", некий аналог виндовой DLLки.
Практически во всех линухах (и частенько в MAC-OS/Windows) не только библиотека есть, но и её обёртка, что бы можно было использовать в командной строке.
Так вот, разраба данной библиотеки хакнули, причём конкретно его самого. Это наивысшый уровень социального инжиниринга.
Т.к. его библиотека была распростанённой, и достаточно эффективной, то хакеры решили "хакнуть" большинство дистров на базе Linux одним махом.
Там была цельная спецоперация.
Сначала долбили разраба фич-реквестави (пожелалками) - продолжалось енто пару лет. Потом появляются коммитеры, которые енти фичи реализуют. Их количество росло, а чел (ну не буду я его ФИО писать, что блы поисковики его опять пердолили ссылаясь на ентот пост - не хочу), просто устал, выгорел. И дал права над исходниками левому чуваку, который был наиболее активен среди коммитеров.
Результат не заставил себя ждать - в качестве очередного патча, в библиотеку был внедрён бэкдур, позволяющий получить права root.
Так будет тост: "Не давайте прав кому либо выше Ваших! Программисты - вы боги-создатели своего кода".
















Я не программист, но даже меня немного коробит. ))
Нуссс. Начну из далека. На заре вычислительной техники народ довольствовался механическими вычислительными машинками. Не буду на них останавливаться - это тема для длинющего поста, начиная с камешков на берегу и заканчивая машинкой "Энигма".
Потом, почти одновременно, появились аналоговые вычислительные машины - АВМ. Они имели крайне узкую направленность, и грубо говоря пытались электронными, а зачастую и физическими методами повторить некий физ. процесс и получить результат/расчёт того или иного физ. процесса. Головоломка уже не для программистов, а для физиков и математеков.

Про квантовые компы я промолчу тихи в тряпочку - сколько ни читал - муть мутная и мне непонятная. Стар я видать стал, а может стал и СуперСтар...
Говорить я буду о привычных компах - которые работают с единичками и ноликами (троичные погибли, хотя в СССР были нехилые наработки). Вроде всё норм, но комп, процессор, понимает как раз енти нули и единички. Ещё на этапе проектирования железяки народ понял енту проблему и начал объединять их в группы, кратные степени двойки. Так появился байт (2^3 = 8), а потом и слово (2^4 = 16). И тут же различные производители железа придумали первый геморрой для программистов - порядок следования байт в слове. Little-Endian и Big-Endial - эти слова знакомы каждому сетевику и привычно вызывают когнитивный диссонанс...
Но ладно, вернёмся к ассемблеру. На заре программирования под цифровые процессора народ вбивал программу тумблерами - битик за битиком. Производительность труда была на наименьшейшем уровне, а вероятность щёлкнуть не тот битик самой большой.
Нервы не выдержали и мисс Кэтлин Бут решила облегчить себе работу, создав первый в мире ассемблер для ARC2 (военный авиационный вычислитель).
Эту идею подхватили остальные. Что самое интересное - на языке Ассемблера можно добиваться максимальной производительности (самая популярная библиотека ffmpeg имеет множество ассемблерного кода под разные архитектуры) - выжать из процессора все соки. Такое же происходит в различных математических библиотеках.
Вроде всё с ассемблером хорошо, кроме одного - там команды процессору заменены на мнемокоды - краткие символьные представления команды процессора. Писать программу стало удобно, понятно. Появились кросс-ассемблеры (низкий уровень llvm)
Но оснавная засада осталась - трудоёмкость программирования и половина башки программиста занята архитектурой компа (да-да, там не только про проц надо помнить, но и про периферию).

А что итого? Я понимаю, что питонисты нифига не поймут - они даже не понимают, что каждая питон-либа это просто обёртка под высокоэффективным кодом на Си. Ассемблер жив и будет жив, те же "тысяча строк" ассемблера под линуксом - это и есть основа ядра ОС. Потом уже можно писать на Си, Си++, даже Расте (чёт ОС растоманов зачахла - видать пришло понимание, что язык высокого уровня не может использоваться наравне с Ассемблером или СИ).
Язык Ассемблера, как продился, так и будет жив. Есть яыки программирования близкие к АСМу - тот же Си, или Форт, но никто не даст программисту той мощи и контроля за компом как АСМ. Тот же Си в своих диалектах везде имеет возможность АСМ-вставок.
Что же до программирования на АСМе - мне он нравится, но... Всегда надо иметь баланс в голове. Поверьте strncpy() из Си будет более оптимизирована чем ваша реализаций на АСМе, а вот к-либо формула, вычисляющая интеграл, которого нет в clib - лучше уже на асме, особенно если оно вычисляется в цикле..
О проекте: Пишем один код - собираем на разные 8 бит МК!
https://vm5277.ru - это универсальное решение для embedded-разработки, которое позволяет сократить время создания прошивки для 8 бит микроконтроллеров в разы.
Как это работает?
Что входит в решение:
Ключевые преимущества:
Проект находится на ранней стадии, но я активно над ним работаю. Уже можно видеть, как высокоуровневый код на Java-подобном языке превращается в чистый и эффективный ассемблер! Это ещё не итоговый вариант, но прогресс уже есть.
Что уже работает в этом примере:



Также приведу одну из функций RTOS(код сырой, может содержать ошибки)

Ключевые фрагменты сгенерированного ассемблерного кода:
1. Метаданные класса:
Компилятор автоматически формирует структуру для поддержки RTTI (Run-Time Type Information), необходимую для instanceof.

2. Динамическое создание объекта в куче:
Код конструктора new Byte(0x08) транслируется в вызов менеджера динамической памяти (os_dram_alloc) и инициализацию полей.

3. Проверка типа (is / instanceof):
Оператор if(b1 is Byte) компилируется в вызов процедуры j8bproc_instanceof_nr, которая проверяет метаданные объекта.

4. Полиморфный вызов метода:
Вызов b1.toByte() через интерфейс Number преобразуется в универсальный механизм поиска и диспетчеризации метода.

5. Интеграция с системными сервисами:
Вывод в "консоль" (System.out) — это вызов системного сервиса ОСРВ.

Что это значит?
Это доказывает, что подход vm5277 работоспособен. Мы можем писать на высокоуровневом ООП-языке, а под капотом получать код, который:
Многие новички в программировании совершают эту ошибку — пытаются написать всё и сразу. Говорю им, что нужно разбивать на подзадачи и писать маленькими порциями — всё равно пишут всё сразу, а потом ищут ошибки сразу по всему коду. Вместо того, чтобы отлавливать их на маленьких кусочках.
Начинающим вообще каждые 2-3 строчки лучше запускать. 10 строк кода уже много, уже тяжеловато в них ориентироваться. Но вот пишут, делают ошибки, искать их и исправлять ещё не умеют, поэтому при исправлении часто только ещё сильнее ломают... Сколько мучений на ровном месте.

И нет, я не всегда специально затягиваю посты до последнего. Чаще просто лень и внезапно осознанный дедлайн.
Одна из частых ошибок молодых программистов — экономия времени на чтении. Читают текст недостаточно внимательно, не понимают, что требуется, как именно должен быть оформлен результат — и создают вообще не ту программу, тратя кучу времени.
Читать нужно раз 5. Даже если там 2 строчки — 5 раз хотя бы. Тогда шансов так ошибиться гораздо меньше.


Наверное невозможно найти человека, который бы никогда не встречал программ на Java. Сначала телефоны с J2ME, потом Android, который использует свои варианты реализации JVM и свой байткод. Даже многие сим-карты внутри используют свою оптимизированную разновидность Java.
Даже JavaScript обязан своим названием популярности Java, хотя и не имеет с ней прямого родства.
(При этом некоторый промежуток времени существовали Java-апплеты)

По официальной информации, 23 мая 1995 года, ровно 30 лет назад, Sun Microsystems выпустили первую бета-версию Java. Первая полноценная официальная версия JDK 1.0 выйдет только 23 января 1996.
Изначально язык должен был называться "Oak" (дуб, который рос рядом с офисом разработчиков), после чего был переименован "Green" (зеленый), а потом в честь кофе с острова Ява. А в качестве целей языка ставились:
Синтаксис был практически полностью позаимствован из C++, который уже был знаком многим программистам. Были выброшены процедурные артефакты C в виде "бродячих" глобальных переменных и функций, всё должно принадлежать классам. Также выбросили некоторые "неудачные" решения C++, к примеру дружественные классы, множественное наследование (его заменили интерфейсы), перегрузку операторов (зачем?!) и еще немного. Вместо ручного управления памятью было решено внедрить сборщик мусора, чтобы облегчить разработку и снизить количество ошибок и уязвимостей.
Главной особенностью языка стала концепция "напиши единожды, запускай везде" (хотя правильнее это будет назвать "напиши единожды, отлаживай везде" из-за различных реализаций и окружения), возможная благодаря тому, что код сначала компилируется в стандартизированный и независимый от процессора байткод для виртуальной машины (JVM), а в машинный код переводится только на машине потребителя, учитывая её архитектуру и особенности. Получается что-то среднее между интерпретируемым и компилируемым языком.

Язык получился... интересным.
То, что планировалось простым, в итоге превратилось в многословное нечто, а в сочетании со всякими архитектурными извращениями стало притчей во языцех о громоздкости корпоративного стиля. Потом появились совместимые с JVM альтернативы по типу Kotlin, ибо Java старательно игнорирует и не вводит ничего, что бы могло замазать многословность и неудобство. Android давно официально рекомендует использовать его вместо Java.
Добиться высокой скорости на интерпретируемом языке со сборкой мусора тоже проблемно. JIT замедляет запуск и жрет память, AOT поддерживается плохо и с ним невозможно реализовать некоторые вещи (кодогенерацию, к примеру), плюс остается сборщик мусора. Плюс некоторые не очень удачные решения и ограничения, тоже не способствующие высокой производительности (видно на контрасте с C#, в котором их исправили).
Тем не менее, Java на очень долгое время стала самым востребованным языком и отлично продолжает жить и сейчас, находясь на 4 месте популярности по версии TIOBE и дважды становясь языком года (в 2005 и 2015).



Хотел написать про работу системы управления памятью, но понял, что для полноценной статьи у меня не хватает практических знаний. Поэтому кратко

Помимо стека, о котором было рассказано в прошлой статье и который содержит локальные переменные, в "потребительской" программе в среднестатистической ОС еще бывают глобальные переменные и динамическая память. Глобальные переменные описываются внутри сегментов .bss и .data, резервируясь в памяти сразу после загрузки образа программы (по-правильному это называется процессом).
Динамическая память иначе называется кучей, так как представляет из себя кучу динамических данных, который могут появляться и исчезать во время работы.
Для работы с динамической памятью в C существует malloc(size_t), запрашивающий (выделяющий, аллоцирующий) у ОС кусок памяти указанного размера и возвращающий указатель на первый байт, и free(void *), принимающий этот указатель и возвращающий (освобождающий) кусок назад в ОС.
В C++ есть операторы new и delete, они устроены гораздо сложнее и я не буду их рассматривать, ибо не пишу на нем. Помимо запроса памяти у ОС они занимаются ее инициализацией.
Если по какой-то причине стандартной библиотеки языка в наличии не имеется, то в Windows за память отвечает HeapAlloc и устаревшие GlobalAlloc и LocalAlloc из времен Windows 3.11 (malloc к ним и обращается внутри. Еще есть парные HeapFree, GlobalFree и LocalFree). Они предоставляют больше контроля над результатом, но проприетарны для Windows. А полный список функций для работы с памятью в WinAPI есть здесь, их сотни на любой случай жизни. Можно даже внутри контекста чужого процесса выделить или освободить кусок памяти (обычно такое используется вирусами и прочей нечистью).
А что будет, если постоянно выделять, но не освобождать? А еще лучше: выделять, но терять адреса выделенной памяти. ОС достаточно быстро подскажет правильный ответ.
В теории есть несколько возможных выходов, можно даже обработать подобный случай и, к примеру, экстренно освободить всё выделенное (только если не терять указатели) или попытаться спасти нужные данные из разваливающейся программы, но если никаких действий не предпринять, то ОС заставит процесс экстренно прекратить свою деятельность и существование.
Случай с потерей указателей на выделенную память называется утечкой памяти и ее последствия необратимы. Но как только ОС завершает выполнение процесса, то вся его память, даже утекшая, становится снова свободной.

Отслеживание правильности выделения и освобождения памяти является достаточно сложной и опасной задачей, особенно если требуется обеспечивать стабильность и безопасность (лучше даже не думать, что будет, если рукожопый программист забьет на MISRA и устроит утечку памяти в подсистеме тормозов в Тесле), и практически невыполнимой в некоторых парадигмах (особенно в функциональном программировании). Поэтому еще с древнейших времен (с 1959 года) существуют языки со сборкой мусора:
Но несмотря не это, практически все современные языки используют сборку мусора, кроме C++ и Rust. C++ по большей части перешел на умные указатели, которые тоже считают количество активных ссылок, но делают это без сборщика мусора. А Rust использует свой уникальный Borrow Checker, который понимают только растофилы.
На этом вроде у меня всё.