Всем привет, сразу
DISCLAIMER: Данная статья носит исключительно развлекательный характер, не несет в себе никакой практической пользы и написана только для того, чтобы срубить плюсов just for fun:)
Как-то раз с коллегой зашел разговор о том почему в платформе не реализована функция получения модуля числа, ведь для учетной системы это довольно нужная вещь (скорее всего разработчики платформы еще заняты написанием функции инкрементации и им не до этого, кстати если у вас есть предположения, то пишите в комментариях) и мы обсуждали, что способов написания такой функции может быть несколько и я решил посчитать, а сколько же их можно придумать. Конечно большинство из них "высосаны из пальца" и не могут использоваться на практике в виду здравого смысла, но нас это не останавливает), итак, поехали!
1.Наиболее логичный и распространенный способ, проверяем на превышение нуля и инвертируем значение при необходимости
Функция МодульЧисла1(Число)
Возврат ?(Число >= 0, Число, -Число);
КонецФункции // МодульЧисла()
2.Также довольно распространенный способ, используем функцию Макс()
Функция МодульЧисла2(Число)
Возврат Макс(Число, -Число);
КонецФункции // МодульЧисла()
На этом адекватные способы закончились) и начинаются извращения начинаем использовать
3.Перевести в строку, избавиться от минуса и конвертировать обратно в число?! Легко!
Функция МодульЧисла3(Число)
ЧислоСтрокой = Формат(Число, "ЧРД=.; ЧН=; ЧГ=");
ЧислоСтрокой = СтрЗаменить(ЧислоСтрокой, "-", "");
Возврат Число(ЧислоСтрокой);
КонецФункции // МодульЧисла()
4.Далее вспоминаем, что можем использовать com-объекты, а это открывает нам удивительные возможности
Изначально я использовал такой код:
ЧислоСтрокой = Формат(Число, "ЧРД=.; ЧН=; ЧГ=");
Script = Новый COMОбъект("MSScriptControl.ScriptControl");
Script.Language = "javascript";
Script.Eval(СтрШаблон("var abs = Math.abs(%1);",ЧислоСтрокой));
Возврат Script.CodeObject.abs;
Но в серверном контексте я получил ошибку отсутствия класса из-за различия в разрядности, а делать обертку на корпоративном сервере не хотелось, поэтому я решил использовать другой com, а именно regexp
Функция МодульЧисла4(Число)
RegExp = Новый COMОбъект("VBScript.RegExp");
RegExp.Global = Истина;
RegExp.IgnoreCase = Истина;
RegExp.Pattern = "d+[.,]?d+";
Matches = RegExp.Execute(Число);
Если Matches.Count() > 0 Тогда
Возврат Число(Matches.Item(0).Value);
Иначе
ВызватьИсключение("Нет вхождения строки");
КонецЕсли;
КонецФункции // МодульЧисла()
5.Используем список значений и сортируем, почему нет?
Функция МодульЧисла5(Число)
СписокЗначений = Новый СписокЗначений;
СписокЗначений.Добавить(Число);
СписокЗначений.Добавить(-Число);
СписокЗначений.СортироватьПоЗначению(НаправлениеСортировки.Убыв);
Возврат СписокЗначений[0].Значение;
КонецФункции // МодульЧисла()
6.Используем запрос, в принципе это нормальный способ и т.к. вычисление производится на стороне СУБД я решил его тоже включить
Функция МодульЧисла6(Число)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВЫБОР
| КОГДА &Число >= 0
| ТОГДА &Число
| ИНАЧЕ -&Число
| КОНЕЦ КАК Модуль";
Запрос.УстановитьПараметр("Число", Число);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
ВыборкаДетальныеЗаписи.Следующий();
Возврат ВыборкаДетальныеЗаписи.Модуль;
КонецФункции // МодульЧисла()
7.Далее вспоминаем из курса школьной программы гуглим, что
Модуль числа a – это арифметический квадратный корень из квадрата числа a, исходя из этого пишем функцию
Функция МодульЧисла7(Число)
Квадрат = Pow(Число, 2);
Корень = Sqrt(Квадрат);
Возврат Корень;
КонецФункции // МодульЧисла()
8.Используем Формат, чтобы избавиться от минуса и используем функцию Вычислить
Функция МодульЧисла8(Число)
ЧислоСтрокой = Формат(Число, "ЧРД=.; ЧН=; ЧГ=; ЧО=0");
Возврат Вычислить("0+"+ЧислоСтрокой);
КонецФункции
Да остановись уже!
9.Здесь вообщем то уже читерство, используем ОписаниеТипов с допустимым знаком "+" и если после приведения результат = 0, то инвертируем значение
Функция МодульЧисла9(Число)
ЧислоСтрокой = Формат(Число, "ЧРД=.; ЧН=; ЧГ=");
Подстроки = СтрРазделить(ЧислоСтрокой, ".");
ЧислоРазрядов = СтрДлина(Подстроки[0]);
ЧислоРазрядовДробнойЧасти = 0;
Если Подстроки.Количество() > 1 Тогда
ЧислоРазрядовДробнойЧасти = СтрДлина(Подстроки[1]);
КонецЕсли;
КвалификаторЧисла = Новый КвалификаторыЧисла(ЧислоРазрядов, ЧислоРазрядовДробнойЧасти, ДопустимыйЗнак.Неотрицательный);
ТипЧисло = Новый ОписаниеТипов("Число", КвалификаторЧисла);
Если ТипЧисло.ПривестиЗначение(Число) = 0 Тогда
Возврат -Число;
Иначе
Возврат Число;
КонецЕсли;
КонецФункции // МодульЧисла()
Все? Нет
10.И напоследок используем такой же подход, перехватываем исключение в функции, где параметр положительное число, например, СлучайноеЧисло
Функция МодульЧисла10(Число)
ГСЧ = Новый ГенераторСлучайныхЧисел;
Попытка
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, Число);
Возврат Число;
Исключение
Возврат -Число;
КонецПопытки;
КонецФункции
И в заключение этой "шедевральной" статьи график (обработка во вложении) по скорости выполнения на 10000 итерациях. Версия платформы: 8.3.13.1513
Ожидаемо запрос и com самый медленный ввиду инициализации объектов, а математика самая быстрая.
Знаешь еще способы — пиши в комментах!
На этом дорогие друзья у меня все, и кстати это моя первая статья (я долго копил свой опыт, чтобы поделиться им с сообществом), надеюсь, ее шуточный характер не встанет мне боком)
Функция МодульЧисла1(Число)
Возврат ?(Число >= 0, Число, -Число);
КонецФункции // МодульЧисла()
С точки зрения математики
.
чисто как еще один способ
(1) Либо ?(Число < 0, -Число, Число).
Чтобы произносить было короче 😀
(1)Согласен, исправил
Возврат ?(СтрДлина(Число) > СтрДлина(-Число), -Число, Число)
(2) Так быстрее:
А еще прикольно с БСП, правда только для целых от -99 до 999
СтроковыеФункцииКлиентСервер.ПреобразоватьЧислоВАрабскуюНотацию(СтроковыеФункцииКлиентСервер.ПреобразоватьЧислоВРимскуюНотацию(Число))
Вы каждую итерацию их инициализировали? Так не пойдёт, такие вещи кэшировать надо.
Мои результаты на 1 млн. итераций (в мс):
Число > 0 391
Макс 393
СтрЗаменить 4356
Число(СтрЗаменить(XMLСтрока 2179
СтрДлина 3873
Лев 1200
Sqrt 1115
(8) Была такая мысль, но мы же пишем независимую функцию, работающую без дополнительных настроек, естественно, я знаю про кэш, да и в контексте «несерьезности» задачи вряд ли это имеет смысл 🙂
Вот два варианта на основе «индикаторной» функции:
Показать
И для разнообразия
(0) шуточные задачки развивают логическое мышление 🙂
Все, о чем молчат гусары…
Неее, это другое: «Баловство это, барин, жениться вам надо…» (с)
Якубович и Спанчбоб — самые главные персонажи в этой статье.
шутки ради
спасибо, повеселили с утра пораньше =)
(15) Ну как бы в самом начале написал just for fun)
Запросы рулят, говорили они 🙂
Pow(10,0,5*Log10(Число*Число))
Для особо искушенных: (x #k8SjZc9Dxk (x >> 31)) — (x >> 31)
Можно еще создать макет, в котором заполнить все положительные числа, потом в процессе загрузить колонку в массив и проверить на наличие числа в массиве, если нету — умножить на (-1).
(25)
идеально:) какое последнее число?)
(10)
это можно упростить:
(26) 99999999999999999999999999999999,99999999999999999999999999999999
[32,32]
Опять же, можно воспользоваться АПИ (типаhttp://mobmath.appspot.com) , передать туда уравнение и получить ответ
Модуль=?(Pow(10, число)>1,1,-1)*число;
Какие то жуткие цифры на диаграмме)) Вы не в отладке тест сделали случайно?)) Я советую провести тест без нее и выложить результаты, т.к. первый метод должен выигрывать у второго, пусть и немного.