How can I keep the mathematical order in this function?

Viewed 72

I have this function which returns the wrong result

def calc(a): return lambda op: {
    '+': lambda b: calc(a+b),
    '-': lambda b: calc(a-b),
    '*': lambda b: calc(a*b),
    '/': lambda b: calc(a/b),
    '=': a}[op]

calc(1)('+')(2)('*')(10)('=') # 30 -> should be 21

Does anyone have an idea how I can keep the functional style and follow the correct mathematical order?

1 Answers

Please note that I lack a firm understanding of Python hence the answer is going to be in JS. Hope it's still helpfull.

A proper solution needs to cover the following properties:

  • operator precedence
  • operator associativity (left/right/none)
  • operator arity (unary/binary)
  • round parenthesis

Operator associativity must not be confused with the mathematical property. An operator must either be left, right or not associative at all. The latter means the operator is not composable.

Precedence determines the evaluation order in case of different operators, associative in case of the same.

a + b - c = a + (b - c) :: - exceeds precedence of +
a - b - c = (a - b) - c :: - is left associative

This solution is just a rough sketch. It doesn't use string symbols as operators, which can be easily changed though. More importently it takes neither operator associativity nor parenthesis into account but always assumes left associativity and binary operators. It's just a start to get a notion for the complexity to be expected:

const prec = n => o => (o.prec = n, o);

const prec1 = prec(1);
const prec2 = prec(2);
const prec3 = prec(3);
const prec4 = prec(4);

const add = prec1(x => prec1(y => x + y));
const sub = prec2(x => prec2(y => x - y));
const mul = prec3(x => prec3(y => x * y));
const div = prec4(x => prec4(y => x / y));

const infix = x => f => infix_(f(x));

const infix_ = partialF => y => {
  const go = g => {
    if (partialF.prec >= g.prec)
      return infix_(g(partialF(y)));

    else {
      const partialG = g(y);
      return infix_(z => partialF(partialG(z)));
    }
  };

  Object.defineProperty(
  go,
  "run",
  {get() {return partialF(y)}}); // lazy property getter
  
  return go;
};

const r1 = infix(2) (mul) (3) (sub) (4) (add) (5) (div) (2); // 2*3-4+5/2 = 4.5
const r2 = infix(2) (add) (3) (sub) (4) (mul) (5) (div) (2); // 2+3-4*5/2 = -5

console.log(r1.run);
console.log(r2.run);

Related