Задачки с собеседований. Сборник решений

Задача про переворот строки вызвала интерес у многих читателей, как оказалось. И даже стали писать в личку. Например, Леонид Лебедев, верно подметил, что задачу на переворот числа можно решить без приведения к строке.

Да, можно. Но я не стал писать решения про переворот числа в тот пост, так как в мире фронтенд задавая задачку на переворот чаще ждут, что кандидат покажет владение синтаксисом и знание стандартных методов. И ждут именно те решения в 90% случаев. Если же брать разработчиков из мира C/С++, Java, то там эта же задача уже решается другими способами и проверяют там умение работать с памятью, знание алгоритмов и т.д.

Напомню условие задачи:

Написать функцию, которая переворачивает число. Например, если мы передаем на вход 1234, то возвращает 4321. При этом нельзя приводить исходное число к строке.

Действительно, число можно перевернуть не приводя его к строке:

function revert(n) {
var x = 0;
while (n > 0) {
x = x*10 + n%10;
n = ~~(n / 10);
}
return x
}
let n = 12367001109;
revert(n) // 90110076321

Работает. Но, в отличие от строки, работа с числами очень зависит от реализации чисел в движке. В зависимости от алгоритмов, могут быть такие, где будут погрешности при математических операциях, в результате чего исходная последовательность будет ломаться. И надо понимать еще то, что числа с нулями на конце при перевороте будут терять эти нули:

let n = 123670011090000;
revert(n) // 90110076321

Можно придумать множество вариаций на переворот числа без смены типа (не считая что int приводится к float, но у нас в JS есть только number, а посему…).

Усложняем

Если вспоминать условие задачи про переворот строки, то там были еще и ограничения:

При решении нельзя использовать циклы (for, while, do, etc…), итерационные методы типа map и forEach.

Можем решить? В принципе почти любой цикл можно представить в виде рекурсии (вопрос только эффективно ли это).

Решение 2: карирование и рекурсия

А где же решение №1? Решение №1 было в предыдущем посте, где мы число предварительно приводили к строке. А это решение без приведения входного значения к строке:

function revert(n) {
var res = [];
void function _(n) {
if (n < 1) return;
res.push( ~~(n % 10) );
_(n/10);
}(n);
return +res.join('');
}

Если хочется избавиться от join, чтобы прям уж совсем было тру и не придирались, что использовали “перебирающий” метод, то можно сразу работать со строкой:

function revert(n) {
var res = '';
void function _(n) {
if (n < 1) return;
res += ~~(n % 10);
_(n/10);
}(n);
return +res
}

Условие выполнено.

Вариация на ES6+ — выпендрежно-молодежная

const revert = (n, res='', _) =>
(
(_ = (n) =>
(
(n < 1) ? (null) : ( res += ~~(n % 10), _(n/10) )
)
)(n), +res
);

Скобочный маньяк и программист на Lisp просто словили каеф от такого =)

А если вы еще и человек-парсер, то вы легко пишите и читаете все в 1 строку, а питонисты вас яро ненавидят:

const revert=(n,r='')=>~~n?revert(n/10,r+~~(n%10)):+r

Perl программистам пламенный привет!

Итого

В принципе вот в таком варианте задача уже сложна и здесь мы можем проверить склонность к математике и алгоритмам. В таком варианте задача уже не про синтаксис. Опять же, помним что проверяем. Что вы хотите от человека, которого берете к себе в команду? Если вы покопаетесь на форумах С++ разработчиков, вы сможете найти уйму разных решений, не все из которых получится хорошо портировать на JS. Но зачем фронтендеру это может понадобиться? (надо понимать что я отделаю фронтендеров от Node.js бэкендеров, где такими задачами могут мучать).