В практике программирования иногда встречаются задачи, которые на первый взгляд кажутся лишенными практического смысла, но на самом деле они отлично проверяют знание платформы, логическое мышление и понимание того, как данные обрабатываются на низком уровне. Одной из таких классических задач является суммирование двух целых чисел без использования стандартного оператора + и других арифметических операций. В этой статье мы подробно разберем, какие инструменты 1С позволяют обойти это ограничение, проанализируем различные подходы — от математических до чисто платформенных.
Это наиболее «программистский» подход, основанный на битовой алгебре. Любое сложение в компьютере выполняется через логические вентили. Мы можем эмулировать этот процесс, используя встроенные функции 1С для работы с битами. Основная идея заключается в том, что исключающее ИЛИ (XOR) дает результат сложения без учета переноса, а логическое И (AND) определяет биты, которые должны быть перенесены в следующий разряд.
Рассмотрим итеративный вариант реализации этого алгоритма. Разберем по шагам, как работает этот цикл:
Функция СложитьБитово(Знач Число1, Знач Число2)
Результат = ПобитовоеИсключительноеИли(Число1, Число2);
Перенос = ПобитовыйСдвигВлево(ПобитовоеИ(Число1, Число2), 1);
Пока Перенос <> 0 Цикл
ВременнаяПеременная = Результат;
Результат = ПобитовоеИсключительноеИли(Результат, Перенос);
Перенос = ПобитовыйСдвигВлево(ПобитовоеИ(ВременнаяПеременная, Перенос), 1);
КонецЦикла;
Возврат Результат;
КонецФункции
Проанализируем ситуацию: этот метод идеально работает для целых чисел, однако стоит помнить об ограничении платформы 1С. Побитовые операции в языке 1С работают с 32-битными целыми числами со знаком. Это означает, что если сумма превысит 2 147 483 647, результат может стать некорректным (произойдет инверсия знака или переполнение).
Одним из самых оригинальных способов решения задачи в 1С является использование объекта ПотокВПамяти. Этот объект позволяет управлять «курсором» (позицией) внутри буфера данных. Поскольку позиция в потоке — это целое число, мы можем использовать метод Перейти(), чтобы суммировать смещения.
Посмотрим на пример реализации:
Функция СложениеЧерезПоток(Число1, Число2)
Поток = Новый ПотокВПамяти;
// Устанавливаем начальную позицию, равную первому числу
Поток.Перейти(Число1, ПозицияВПотоке.Начало);
// Сдвигаем позицию относительно текущей на величину второго числа
Поток.Перейти(Число2, ПозицияВПотоке.Текущая);
// Текущая позиция теперь равна сумме
Возврат Поток.ТекущаяПозиция;
КонецФункции
Важный момент: данный метод изящен тем, что он вообще не использует логических или арифметических вычислений в коде, перекладывая задачу на механизм управления памятью платформы. Это решение обходит многие ограничения и демонстрирует глубокое знание объектной модели 1С.
Платформа 1С позволяет выполнять вычисления на уровне СУБД. Хотя в самом тексте запроса можно написать &Число1 + &Число2, условие задачи часто запрещает использование знака «плюс» даже там. В таком случае мы можем воспользоваться механизмом группировки.
Рассмотрим алгоритм решения через временные таблицы:
СУММА() при группировке.
Функция СложитьЧерезЗапрос(Число1, Число2)
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Слагаемое", Новый ОписаниеТипов("Число"));
ТЗ.Добавить().Слагаемое = Число1;
ТЗ.Добавить().Слагаемое = Число2;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Слагаемое КАК Значение
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЗ КАК ТЗ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СУММА(ВТ_Данные.Значение) КАК Результат
|ИЗ
| ВТ_Данные КАК ВТ_Данные";
Запрос.УстановитьПараметр("ТЗ", ТЗ);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Результат;
КонецЕсли;
КонецФункции
Этот метод хорош тем, что он универсален и работает с любыми числами, которые поддерживает тип Число в 1С, включая дробные и очень большие значения, не ограничиваясь 32 битами.
Если нам нужно сложить два небольших положительных целых числа, мы можем прибегнуть к «геометрическому» методу — измерению длины объединенного объекта. Выясним, как это реализовать через массивы:
Функция СложитьЧерезМассив(Число1, Число2)
Коллекция = Новый Массив;
Для Счетчик = 1 По Число1 Цикл
Коллекция.Добавить(Истина);
КонецЦикла;
Для Счетчик = 1 По Число2 Цикл
Коллекция.Добавить(Истина);
КонецЦикла;
Возврат Коллекция.Количество();
КонецФункции
Аналогичный подход можно применить и к строкам, соединяя две строки определенной длины и измеряя итоговую длину функцией СтрДлина(). Однако помните, что при работе с числами в миллионы и миллиарды этот метод вызовет переполнение оперативной памяти или критическую ошибку приложения.
Интересной особенностью встроенного языка 1С является поведение переменной-счетчика в цикле Для ... По .... После завершения цикла значение переменной-счетчика становится на единицу больше, чем конечное значение. Это можно использовать для инкрементирования (прибавления единицы) без арифметики.
Разберем пример, где мы прибавляем второе число к первому по единице за итерацию:
Функция СложитьЦиклом(Число1, Число2)
Результат = Число1;
Для СчетчикВнешний = 1 По Число2 Цикл
// Данный цикл выполнится 1 раз, но увеличит 'Результат' на 1
Для Итератор = Результат По Результат Цикл
КонецЦикла;
Результат = Итератор;
КонецЦикла;
Возврат Результат;
КонецФункции
Здесь мы видим, что переменная Итератор после выхода из внутреннего цикла принимает значение Результат + 1. Повторяя это действие Число2 раз, мы получаем итоговую сумму. Это очень специфичный для 1С способ, демонстрирующий понимание нюансов работы интерпретатора кода.
В 1С реализованы мощные функции работы с датами, которые внутри себя уже содержат логику сложения временных интервалов. Мы можем использовать функцию ДобавитьМесяц() или работу с секундами (хотя работа с секундами напрямую часто требует использования оператора +, функции работы с датами позволяют этого избежать).
Проанализируем пример с объектом Дата:
Функция СложитьЧерезДаты(Число1, Число2)
БазоваяДата = '00010101'; // Начало времен
// Добавляем к дате количество секунд, равное Число1
ДатаСПервымСлагаемым = ДобавитьМесяц(БазоваяДата, 0); // Просто инициализация
// В 1С разница дат дает количество секунд.
// Но нам нужно именно сложение без знака плюс.
// Можно использовать внешние компоненты или специализированные функции.
КонецФункции
Хотя работа с датами чаще всего используется для вычитания (получение разницы), программное добавление периодов в цикле через методы платформы также является валидным способом решения подобных задач на собеседованиях.
Мы рассмотрели несколько принципиально разных подходов к решению задачи суммирования в 1С без арифметических операторов:
Выбор конкретного метода зависит от того, какую именно цель преследует интервьюер: проверить ваше знание битовой логики, умение работать с объектами платформы или просто оценить креативность мышления.