📜 ⬆️ ⬇️

An interesting task for the interview, currying and partial application of the function

I go on job interview. Somewhere boring, somewhere fun. Somewhere interesting. At one of these I was asked to write a function that can add two numbers. I wrote:

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

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


And if, they say, the function signature should be of the following type: add (num1) (num2)? Not a question, I say. Thinking that the cunning burzhuin wants to check whether I know about the fact that you can return functions from functions, I write this:
')
   it ('should be called add (num1) (num2)', function () {
     var add = function (a) {
       return function (b) {
         return a + b;
       };
     };

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




What if we know the first term in advance, but the second will be known later what to do? Yeah, I think they talk about currying. Here is:

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


Then suddenly two more people came running into the room, and the four of them began to ask me, wave their hands, speak loudly. Do not give focus, want to look at how I think. The fun began.

They ask - what if you need to add three numbers? Or four? I say that it is necessary then to remember the state, something like this:

   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);
   });


Why, they ask, do you have if inside? And so that the internal function knows how it is called - in the chain or at the very end and, accordingly, would return itself or a number. Okay, they say, while okay. And if you again need a partial application? Writing:

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


Is it possible, they ask, how can I get rid of a pair of empty brackets at the end? I thought about it ... This function should somehow figure out the context in which it is called ... And, there is the magic `.valueOf`! And you can get rid of excess if at the same time. Come on:

     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);  


and now apply this add2 to another number, say, 10 - and so that 2 + 10 = 12 work out. I add a line, I receive:

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


Does not work! Returns 18. This, I explain, is so conceived - it remembers the result of the last addition inside and uses it for subsequent operations. They need to be corrected so that they do not so clearly remember. Okay, I say. Want clean features? Want quite interesting? Here is the conditional identities chain:

     var add = function (orig) {
       var inner = function (val) {
         return add (parseInt (val + '', 10) == val? inner.capture + 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);


And why, they ask, need this empty line:
   ... parseInt (val + '', 10) ...


This is to force the launch of `.valueOf`. Because, I say, if `val` is a function (which is true for the case of, say,` three (four) `), then` parseInt` will not start the type conversion mechanism that will eventually call `.valueOf`. And `parseInt (func)` is always `NaN`.

I look at them - they are silent. Not noticed too much assignment, it means. Well, it is necessary to bring the optimization to the logical end. I am writing the last option:

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

       return inner;
     };


Cute and minimalistic. The tests are exactly the same.

In general, all the four-hour interview was very useful. But the end is not very - they say, you will not be interested in us. We need people who will sit from morning till evening and do what is said, without showing off or creatively. Our bosses are engaged in creative work, and we have so much of it, new ones are not necessary. So you will soon get bored, you will look for a new job. And to us, they say, the turnover is useless. And what then, I say, they called for an interview, asked interesting questions, solved problems? And, they say, the personnel department called you, and we thought to overwhelm you, and then you wouldn't be so offended - they didn't take it because it was stupid. And now it turns out that they did not take it because it was smart. Easier for you from this, ask?

And I went home ...

Full source in the form of a github test: github.com/nmakarov/excercises

Source: https://habr.com/ru/post/226325/


All Articles