...

понедельник, 16 июня 2014 г.

[Из песочницы] Интересная задачка для интервью, карринг и частичное применение функции

Хожу по job interview. Где-то скучно, где-то весело. Где-то интересно. На одном из таких меня попросили написать функцию, которая умеет складывать два числа. Я написал:

it ('should add two numbers', function () {
var add = function (a,b) {
return a + b;
};

assert.equal(add(2,3), 5);
});

А если, говорят, сигнатура функции должна быть типа такой: add(num1)(num2)? Не вопрос, говорю. Думая, что хитрый буржуин хочет проверить, знаю ли я про то, что можно возвращать функции из функций, пишу вот такое:



it ('should be called like add(num1)(num2)', function () {
var add = function (a) {
return function (b) {
return a + b;
};
};

assert.equal(add(2)(3), 5);
});

А вдруг нам первое слагаемое известно заранее, а вот второе будет известно потом, что делать? Ага, думаю, про currying разговор ведут. Вот:



var add3 = add(3);
assert.equal(add3(4), 7);
assert.equal(add3(5), 8);

Тут вдруг в комнату набежало еще двое, и начали они вчетвером меня распрашивать, махают руками, говорят громко. Не дают сосредоточиться, хотят посмотреть на то, как я думаю. Началось самое интересное.


Спрашивают — а вдруг нужно сложить три числа? Или четыре? Говорю, что надо тогда запоминать состояние, примерно так:



it ('should take random number of digits', function () {
var add = function (a) {
var sum = a;
var inner = function (b) {
if (b) {
sum += b;
return inner;
} else {
return sum;
}
};
return inner;
};

assert.equal(add(2)(3)(), 5);
assert.equal(add(2)(3)(6)(), 11);
});

Зачем, спрашивают, у тебя внутри if есть? А чтоб внутренняя функция знала, как она вызывается — в цепочке или в самом конце и, соответственно, возвращала бы себя или число. Ладно, говорят, пока ладно. А если опять надо частичное применение? Пишу:



var add2 = add(2);
assert.equal(add2(6)(), 8);

А можно, спрашивают, как-нибудь избавиться от пары пустых скобок вконце? Задумался… Это же функция должна как-то сообразить, в каком контексте ее вызывают… А, есть же волшебный `.valueOf`! И от лишнего if заодно можно избавиться. Ну-ка:



var add = function (a) {
var sum = a;

var inner = function (b) {
sum += b;
return inner;
};

inner.valueOf = function () {
return sum;
};

return inner;
};

assert.equal(add(3)(4), 7);
assert.equal(add(3)(5), 8);
assert.equal(add(9)(-5), 4);
assert.equal(add(1)(2)(3), 6);

а теперь примени-ка эту add2 к другому числу, скажем, 10 — и чтоб 2+10=12 получилось. Добавляю строку, получаю:



var add2 = add(2);
assert.equal(add2(6)(), 8);
assert.equal(add2(10)(), 12);

Не работает! Возвращает 18. Это, объясняю, так и задумано — оно внутри запоминает результат последнего сложения и использует для последующих операций. Они — надо исправить, чтоб так явно не запоминало. Хорошо, говорю. Хотите чистых функций? Хотите совсем интересно? Нате цепочку conditional identities:



var add = function (orig) {
var inner = function (val) {
return add(parseInt(val+'', 10) == val ? inner.captured+val : inner.captured);
};
inner.captured = orig;
inner.valueOf = function () {return inner.captured;};

return inner;
};

assert.equal(add(3)(4), 7);
assert.equal(add(3)(4)('aa')(5)(), 12);

var three = add(3);
var four = add(4);
assert.equal(three, 3);
assert.equal(four, 4);
assert.equal(three(5), 8);
assert.equal(three(6), 9);
assert.equal(three(four), 7);
assert.equal(three(four)(three(four)), 14);

А зачем, спрашивают, нужна вот эта пустая строка:



... parseInt(val+'', 10) ...

Это для принудительного запуска `.valueOf`. Потому что, говорю, если `val` — это функция (что верно для случая, скажем, `three(four)`), то `parseInt` не станет запускать механизм преобразования типов, который в конце концов вызовет `.valueOf`. А `parseInt(func)` — всегда `NaN`.


Смотрю на них — молчат. Не заметили лишнего присваивания, значит. Ладно, надо довести оптимизацию до логического конца. Пишу последний вариант:



var add = function (orig) {
var inner = function (val) {
return add(parseInt(val+'', 10) == val ? orig+val : orig);
};
inner.valueOf = function () {return orig;};

return inner;
};

Симпатично и минималистично. Тесты в точности те же самые.


Вообще все четырехчасовое интервью получилось весьма полезным. Но окончилось не очень — говорят, тебе у нас не интересно будет. Нам нужны люди, которые будут сидеть с утра до вечера и делать, что сказано, не выпендриваясь и не креативя. Креативом у нас начальство занимается, и у нас его вон уже сколько, новых не надо. Так что тебе вскорости станет скучно, будешь искать себе новую работу. А нам, говорят, текучка не к чему. А что же тогда, говорю, на интервью позвали, вопросы интересные задавали, задачки решали? А, говорят, звал тебя отдел кадров, а мы думали тебя завалить и тогда тебе не так обидно было бы — не взяли потому, что глупый. А сейчас вот выходит, что не взяли потому, что умный. Легче тебе от этого, спрашивают?


И поехал я домой…


Полный исходник в виде теста на гитхабе: http://ift.tt/1pzNK87


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий