Как я могу сохранить математический порядок в этой функции?

#python #functional-programming

#питон #функциональное программирование

Вопрос:

У меня есть эта функция, которая возвращает неправильный результат

 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 -gt; should be 21  

У кого-нибудь есть идея, как я могу сохранить функциональный стиль и следовать правильному математическому порядку?

Комментарии:

1. Если вы ищете карри, Haskell в основном построен на этой концепции, в то время как Python ее вообще не имеет. Вероятно, вы можете реализовать это самостоятельно, но это не стоит такой боли, ИМО

2. add(1, mul(2, 10)) . Не пытайтесь отразить инфиксную нотацию в своем выражении лица. ( add и mul предопределены в operator модуле, а также функции для других операторов.)

3. Python не является функциональным языком. Вы можете написать несколько функциональный код с lambda функциями и map и reduce , но это почти все. Если вам нужен чисто функциональный подход, вы ищете Хаскелла. Я думаю, что есть и другие чисто функциональные языки, но Python не входит в их число. Опять же, вы можете имитировать функциональный стиль в Python, но это все равно, что использовать вилку для супа — не очень эффективно или продуктивно.

4. @KellyBundy, я не уверен, имеет ли каррирование большое отношение к приоритету операторов. AFAIK, приоритет оператора выполняется во время синтаксического анализа, но каррирование выполняется во время выполнения. Если вы вызываете функции карри в правильном порядке, вы получите правильный приоритет оператора.

5. Поскольку a также может быть подвыражение, вы могли бы просто написать calc(1)(' ')(calc(2)('*')(10)('='))('=') . В противном случае вам нужно было бы назначить приоритет каждому оператору и собрать все переданные операторы calc (до их оценки), чтобы привести их в порядок, определенный приоритетом. Кроме того, вам придется ввести скобки в качестве составного оператора. Это может быть слишком много работы для веселого проекта..

Ответ №1:

Пожалуйста, обратите внимание, что мне не хватает четкого понимания Python, поэтому ответ будет в JS. Надеюсь, это все еще полезно.

Правильное решение должно охватывать следующие свойства:

  • приоритет оператора
  • ассоциативность операторов (слева/справа/нет)
  • арность оператора (унарный/двоичный)
  • круглые скобки

Ассоциативность операторов не следует путать с математическим свойством. Оператор должен быть либо левым, либо правым, либо вообще не ассоциативным. Последнее означает, что оператор не может быть составлен.

Приоритет определяет порядок оценки в случае разных операторов, ассоциативный в случае одного и того же.

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

Это решение — всего лишь грубый набросок. Он не использует строковые символы в качестве операторов, которые, однако, можно легко изменить. Более важно, что он не принимает во внимание ни ассоциативность операторов, ни скобки, но всегда предполагает ассоциативность слева и двоичные операторы. Это только начало, чтобы получить представление о ожидаемой сложности:

 const prec = n =gt; o =gt; (o.prec = n, o);  const prec1 = prec(1); const prec2 = prec(2); const prec3 = prec(3); const prec4 = prec(4);  const add = prec1(x =gt; prec1(y =gt; x   y)); const sub = prec2(x =gt; prec2(y =gt; x - y)); const mul = prec3(x =gt; prec3(y =gt; x * y)); const div = prec4(x =gt; prec4(y =gt; x / y));  const infix = x =gt; f =gt; infix_(f(x));  const infix_ = partialF =gt; y =gt; {  const go = g =gt; {  if (partialF.prec gt;= g.prec)  return infix_(g(partialF(y)));   else {  const partialG = g(y);  return infix_(z =gt; 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);