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

I go for 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 signature of the function should be like this: add (num1) (num2)? No question, I say. Thinking that the cunning bourgeois wants to check whether I know that functions can be returned from functions, I write this:

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




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

    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 interrogate me, wave their hands, speak loudly. They don’t let me concentrate, they 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 we must then 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, say, okay. And if again partial application is needed? Writing:

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


And can, they ask, somehow get rid of a couple of empty brackets at the end? Thought ... This same function should somehow figure out in what context it is called ... Ah, there is a magical `.valueOf`! And you can get rid of excess if at the same time. Well:

    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 will work out. Add a line, get:

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


Does not work! Returns 18. This, I explain, is as intended - it internally remembers the result of the last addition and uses it for subsequent operations. They - must be corrected so that it is clearly not remembered. OK, I say. Want clean features? Want quite interesting? Here's a chain of 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);


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


This is to force `.valueOf` to run. 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, which will eventually call `.valueOf`. And `parseInt (func)` is always `NaN`.

I look at them - they are silent. Didn’t notice unnecessary assignment, then. Well, we need to bring optimization to its 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;
    };


Nice and minimalistic. The tests are exactly the same.

In general, the entire four-hour interview turned out to be very useful. But it didn’t end very much - they say that it will not be interesting for us. We need people who will sit from morning to evening and do what is said without showing off and not being creative. Our bosses are engaged in creative work, and we already have so many, we don’t need new ones. So you will soon become bored, you will look for a new job. And we, they say, no need for work. And then what, I say, they called for an interview, asked interesting questions, solved problems? And, they say, the personnel department called you, and we thought you were to fail, and then you would not be so offended - they didn’t take it because it was stupid. And now it turns out that they didn’t take it because it’s smart. Easier for you from this, they ask?

And I went home ...

Full source code as a test on github: github.com/nmakarov/excercises

Also popular now: