Это может помочь сначала взглянуть на этот более простой вариант алгоритма, где нет такой магии, как функция, которая возвращает функцию:
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);
Я думаю, вы сможете понять, как это работает: новая функция binary_op
принимает обе функции обратного вызова (которая выполняет операцию с двумя аргументами) и стек.Затем он извлекает два значения из стека, передает их в функцию обратного вызова, получает из нее результат и помещает этот результат (возможно, сумму или произведение) в стек.Таким образом, размер стека уменьшился на 1: два операнда были заменены результатом func
.
Предполагая, что вы следите за тем, как это работает, теперь посмотрим, как мы могли бы сделать это вместо этого:
binary_op(mul, my_little_stack);
... мы могли бы написать это:
mulop(my_litte_stack);
mulop
должна быть функцией, которая может объединять то, что mul
делает и то, что вышеупомянутый binary_op
делает, за один раз.
Вот где приходит функция make_binary_op
: она создает (и возвращает) функцию, которая специально предназначена для оператора, которого вы имеете в виду (икоторый вы передаете в качестве аргумента).Если вы передадите mul
в make_binary_op
, он создаст функцию, которая реализует вышеуказанную функцию binary_op
, специально адаптированную для mul
: когда эта созданная функция вызывается, она вызовет mul.Но обратите внимание, что для этой динамически создаваемой функции нужен только один аргумент (стек), потому что другой аргумент (func
) ей уже известен.Он присутствует в «замыкании», в котором эта функция была возвращена.
Приложение
Одним из критических замечаний по этому шаблону может быть следующее наблюдение: пока элементы добавляются в my_little_array
с помощью точкинотации (my_little_array.push
), операции mul / add должны быть выражены как вызовы функций, где my_little_array
передается в качестве аргумента.Почему его нельзя заставить работать с точечной нотацией, чтобы вы могли написать my_little_array.mul()
?
В текущем состоянии языка JS вы можете сделать это с помощью класса (конструктора), который расширяетсяArray
, так что кроме push
и pop
он также может поддерживать add
и 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);