1

I would like to understand how the make_binary_op() function works. It takes two different parameters

  1. a function parameter addop or mulop
  2. an array parameter (my_little_stack/my_little_array)

I've used the firefox debugger to watch the execution but still do not understand how the make_binary_op() function works. I get all the pops and pushes, etc.

function make_binary_op(func) {
  return function(my_little_array) {
    let wunth = my_little_array.pop();
    let zeroth = my_little_array.pop();
    my_little_array.push(func(zeroth, wunth));
    return my_little_array;
  };
};

let addop = make_binary_op(function(zeroth, wunth) {
  return zeroth + wunth;
});

let mulop = make_binary_op(function(zeroth, wunth) {
  return zeroth * wunth;
});

let my_little_stack = [];
my_little_stack.push(3);
my_little_stack.push(5);
my_little_stack.push(7);
mulop(my_little_stack); //  my_little_stack is  [3, 35]
addop(my_little_stack); //  my_little_stack is  [38]
let answer = my_little_stack.pop(); //  my_little_stack is  []
console.log(answer);

The code works as specified. The value answer is 38 and my_little_stack is [] at the end.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • This question belongs to https://codereview.stackexchange.com/ – Jorge Fuentes González Apr 27 '19 at 20:11
  • Could you be more specific? What exactly is the part you need help with? – trincot Apr 27 '19 at 20:14
  • 5
    @JorgeFuentesGonzález no it doesn't. OP isn't asking for feedback on how to improve it, OP is asking to understand how it works. – Patrick Roberts Apr 27 '19 at 20:14
  • There can't be a single correct answer when you ask for tutoring. – Reactgular Apr 27 '19 at 20:17
  • Does [this code](https://tio.run/##zY/NDoIwEITvfYo9Un/gQIgmxCchhBCsUq0taReJGp4dW8ELiBcvnjbZb3Zm9pRfc1NoXuFaqj3rOsEQLrdMcETBMoN5cYYdJGlMRlu/qk3phXQGRHNgY8GDALigQy0L6@4GciW9O9MKyxU0tcSSgpMBaIa1ltAzWPQwtqiNB5vXxvpM8lTl0bdouP@i@ljXdRv1suKWBMHkALgBSMIVhFH6y4vLv39xmxKXl0vTMD2bB7MGKSmUNEowX6ij19vQuOue) make sense to you? – Patrick Roberts Apr 27 '19 at 20:22
  • I'm am trying to understand how the first function 'make_binary_op(func)' works ... it accepts mulop(zeroth,wunth) as the function parameter and it also is called via the mulop(my_little_stack) ... if I am in the wrong place i will move my question elsewhere – Herb Swanson Apr 27 '19 at 20:26
  • You're not in the wrong place. And it doesn't accept `mulop` as the function parameter, it accepts `function(zeroth, wunth) { return zeroth * wunth; }` as the function parameter, and the `function(my_little_array) ...` that it returns _is_ `mulop`. – Patrick Roberts Apr 27 '19 at 20:34
  • 1
    You just need to understand higher order functions. `make_binary_op` only accepts one argument, and it returns a function. The function it returns is what gets assigned to `addop` and `mulop`. – Paul Apr 27 '19 at 20:35
  • Related: https://stackoverflow.com/q/18234491/772035 – Paul Apr 27 '19 at 20:36
  • This is basically a calculator using "polish reversed notation" – Jonas Wilms Apr 27 '19 at 21:50

1 Answers1

1

It may help to first look at this simpler variant of the algorithm, where there is no such magic as a function that returns a function:

function binary_op(func, my_little_array) { // Two arguments
    let wunth = my_little_array.pop();
    let zeroth = my_little_array.pop();
    my_little_array.push(func(zeroth, wunth));
    return my_little_array;
};

let add = function(zeroth, wunth) { // No call to make_binary_op
  return zeroth + wunth;
};

let mul = function(zeroth, wunth) {
  return zeroth * wunth;
};

let my_little_stack = [];
my_little_stack.push(3);
my_little_stack.push(5);
my_little_stack.push(7);
binary_op(mul, my_little_stack); // we need to pass two arguments
binary_op(add, my_little_stack);
let answer = my_little_stack.pop();
console.log(answer);

I think you will be able to understand how this works: the new binary_op function takes both a callback function (that performs a operation on two arguments) and a stack. It then pops two values from the stack, passes them to the callback function, gets the result from it, and pushes that result (possibly a sum or a product) on the stack. The stack has thus reduced in size by 1: two operands were replaced by the func's result on them.

Assuming you follow how this works, now see how we could make it that instead of this:

binary_op(mul, my_little_stack);

... we could write this:

mulop(my_litte_stack);

mulop would need to be a function that can combine what mul does and what the above binary_op does, in one go.

That is where the function make_binary_op comes in: it creates (and returns) a function that is specifically tailored to the operator you have in mind (and which you pass as argument). If you pass mul to make_binary_op, it will produce a function that implements the above binary_op function, specifically tailored to mul: when that created function is invoked it will call mul. But note how that dynamically created function only needs one argument (the stack), because the other argument (func) is already known to it. It is present in the "closure" in which that function was returned.

Addendum

One critique on this pattern could be the following observation: while items are added to my_little_array using dot-notation (my_little_array.push), the operations mul/add have to be expressed like function calls where my_little_array is passed as argument. Why could it not be made to work with the dot-notation also, so that you could write my_little_array.mul()?

In the current state of the JS language you could do that with a class (constructor) that extends Array, so that besides push and pop it can also support add and mul:

class PolishCalc extends Array {
    static registerBinaryOp(func) {
        this.prototype[func.name] = function () {
            let wunth = this.pop();
            let zeroth = this.pop();
            this.push(func(zeroth, wunth));
            return this;
        }
    }
}

// Extend the prototype with add and mul methods:
PolishCalc.registerBinaryOp(function add(zeroth, wunth) {
    return zeroth + wunth;
});

PolishCalc.registerBinaryOp(function mul(zeroth, wunth) {
    return zeroth * wunth;
});

let polishCalc = new PolishCalc;
polishCalc.push(3, 5, 7);
let answer = polishCalc.mul().add().pop(); // the method calls can be chained...
console.log(answer);
trincot
  • 317,000
  • 35
  • 244
  • 286