Operações de ordem


13

Introdução

Chega um momento na infância em que você pensa que dominou a adição e a multiplicação, então alguém aparece e informa que:

a * b + c = (a * b) + c! = a * (b + c),

e que não era um processo tão simples ou linear como você aprendeu anteriormente. Você aprende que existe algo chamado de ordem das operações . Essa é uma maneira muito importante de manter algum nível de consistência e de expressão, sem que os parênteses atrapalhem tudo.

História genérica

Um dia, você acorda com o som do pânico nas ruas. Um grupo extremista sob o nome " The 2560 " (abreviação de "Organização Contra a Ordem das Operações", com uma torção idiota do hexágono) usou seus métodos malignos para controlar todas as armas nucleares do mundo. Eles mantêm refém o planeta inteiro e têm uma demanda simples: inverter a ordem de operações aceita ou enfrentar a erradicação (os parênteses devem manter sua prioridade). O novo sistema é chamado PSADME (parênteses, subtração / adição, divisão / multiplicação, expoentes) e as expressões avaliam da direita para a esquerda:

a - b - c = a - (b - c) = a + c - b

Os dias passam e a transição está em andamento. Enquanto matemáticos e físicos estão todos ocupados reescrevendo suas equações, os cientistas da computação enfrentam a tarefa de mudar a maneira pela qual as expressões matemáticas são interpretadas pelos computadores. Você pertence a um grupo secreto de programação rebelde que visa causar tanto tormento aos novos senhores globais - e, por acaso, é selecionado aleatoriamente pelo The 2560 e encarregado de produzir o programa de cálculo de benchmark.

Sua missão

Escreva um programa (ou função) que use uma expressão matemática (numérica) como entrada e calcule a expressão usando PSADME como a ordem das operações e produz o resultado. As expressões devem avaliar da direita para a esquerda, então

1-3+4=1-7=-6

Por simplicidade, todos os números fornecidos serão números inteiros e os cálculos produzirão resultados inteiros.

Regras e pontuação

  • O programa deve aceitar entradas com até 128 caracteres - se o seu idioma / plataforma tiver um tamanho máximo de entrada máximo mais baixo, isso é uma desculpa aceitável.
  • As brechas padrão são proibidas.
  • O código vencedor será escolhido em 18 de novembro (quatro semanas a partir desta data de postagem).
  • Sinta-se à vontade para publicar um código que não seria considerado digno de golfe. Isso é divertido. Se você tem uma maneira interessante de fazer isso, mas não pode jogar sozinho (ou pela natureza do seu método), você pode publicá-la de qualquer maneira.

Como de costume, o código vencedor é aquele com menor número de bytes, com alguns bônus de valor de entretenimento:

  • -5 para evitar qualquer uso dos caracteres na expressão fornecida: + , - , ( , ) , ^ , * , /
  • -5 para fazer os cálculos levar mais de 5 minutos (mas não mais que 10 minutos) para calcular em um computador padrão, sem que o método seja óbvio (usando o relógio ou loops desnecessários); O objetivo é convencer os novos senhores de que você não está tentando atrapalhar seus cálculos de destruição.
  • - (5 + N) para que uma mensagem ofensiva direta (de tamanho N, sem incluir espaços em branco à esquerda / à direita) sobre os membros do The 2560 seja escrita à vista de todo o seu código, com algumas explicações ridículas sobre por que ele precisa ser há. Se for removido, o código não deve funcionar corretamente. Sim, pontos gratuitos pelo valor do entretenimento.

Exemplos e explicações

[program] 2 - 2 - 2
2

2 - (2 - 2) = 2

[program] (2 + 2 * 3 + 3) / 3 + 3
4

(4 * 6) / (3 + 3) = 4

[program] 3 + 2 + 1 ^ 3
216

(3 + 2 + 1) ^ 3 = 216

[program] -5^2
25

(-5) ^ 2 = 25

[program] 32 / 8 * 3 - 1
2

32 / (8 * (3-1)) = 32/16 = 2


1 - 3 + 4 = 1 - 7? Direita para a esquerda sugeriria isso, mas isso coloca a adição à frente da subtração, ao contrário do PSADME, não?
LLlAMnYP

1
@LLlAMnYP Adição e subtração estão no mesmo "grupo", assim como no PEMDAS, e acontecem da direita para a esquerda. Mesmo com multiplicar / dividir. É mais como P(SA)(DM)E.
Geobits

2
A instrução não deve ser processada da direita para a esquerda - em vez disso, operações de igual precedência são avaliadas da direita primeiro. Portanto, 4/2 = 2, 2-1 = 1, mas a / b c = a / (b c) em vez do usual (a / b) * c. Eu espero que isso esclareça as coisas.
21415 Jake

Provavelmente, a maneira mais fácil de fazer isso é escrever uma gramática flex / bison ou lex / yacc.

5
Você deve alterar o acrônimo para PADME , pois os membros de uma organização tão maligna certamente gostariam mais da trilogia mais recente de Star Wars do que dos originais. Também é mais fácil de lembrar.
Mbomb007

Respostas:


9

Haskell, 134 bytes

import qualified Prelude as P
infixl 6 ^
(^)=(P.^)
infixr 8 + 
(+)=(P.+)
infixr 8 - 
(-)=(P.-)
infixr 7 /
(/)=P.div
infixr 7 *
(*)=(P.*)

Redefinindo os operadores matemáticos com novas fixações e prioridades. Agora:

*Main> 32 / 8 * 3 - 1
2

1
Uau. Apenas Uau. Isso é possível em qualquer outro idioma? +1
ETHproductions

Eu tinha certeza, era possível no Mathematica, ou pelo menos uma abordagem semelhante, mas rapidamente percebi que não tinha conhecimento para fazê-lo.
LLlAMnYP

1
Sou novo o suficiente aqui para não ter certeza se a sugestão a seguir é geralmente aceitável neste fórum. Ele é inteiramente baseado no seu código, mas é um script bash que usa o Perl para gerar o arquivo Haskell e passá-lo ao GHCi. Ao fazer isso, eu salvo um BYTE INTEIRO. perl -e'$_="import qualified Prelude as Pl 6^r 8+r 8-r 7*r 7/";s/(. \d(.))/\ninfix\1\n(\2)=(P.\2)/g;s~\./~.div~;print'>a.hs;ghci a.hs Infelizmente, um erro de digitação fez com que o código gerado não tivesse um espaço entre o dígito e o símbolo, mas ainda funcionou bem. Isso significa que seu código pode perder 5 bytes e supera minha 'melhoria'.
Jake #

@JArkinstall Quanto vale a pena, minha resposta está sendo usada efetivamente sedpara gerar e avaliar o código do shell. Provavelmente uma boa meta questão.
Digital Trauma

Isso é verdade, e eu realmente gosto da sua abordagem - no entanto, o uso de uma ferramenta (perl ou sed) para gerar um arquivo em um idioma que é lido em outro idioma parece um passo adiante. Eu não ficaria surpreso se houvesse uma maneira de produzir o código acima através de outro gerador (embora o método não seja óbvio para mim!), E nós nos encontraríamos em análise de exceção. Se isso for permitido, pode-se aplicar essa abordagem ao seu código (e alguns exemplos que eu já vi em algumas das respostas em linguagem mais legível para alguns desafios deste quadro).
Jake #

2

GNU sed -r com extensão exec, 398

s@ *\^ *@ ** @
:
s@\((-?[0-9]+)\)@\1@
t
s@(-?[0-9]+ - )+(-?[0-9]+ - -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [-+] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ / )+(-?[0-9]+ / -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [*/] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ \*\* )+(-?[0-9]+ \*\* -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ \*\* -?[0-9]+)(.*)@bash -c 'echo \1$[\3]\4'@e
t

Não é especialmente curto, mas faz o trabalho.

sed é bom para analisar a precedência, mas não faz aritmética. Portanto, usamos a extensão GNU sed exec no scomando para terceirizar a aritmética necessária para o shell.

Por enquanto, assume todos os operadores, com exceção de ^ter exatamente um espaço na frente e atrás.

Saída de teste:

$ cat psadme.txt 
2 - 2 - 2
(2 + 2 * 3 + 3) / 3 + 3
3 + 2 + 1 ^ 3
-5^2
32 / 8 * 3 - 1
$ sed -rf psadme.sed psadme.txt 
2
4
216
25
2
$ 

Bela foto de perfil. xD
Oliver Ni

1

JavaScript (ES6) 287 300

Editar bug corrigido (apenas um erro de digitação, 6 deveria ter sido 4) - Adicionada uma explicação completa no final do trecho

Edit 2 Encontrou alguma melhoria trabalhando em outro desafio

Mais uma portabilidade do mesmo analisador, com apenas uma diferença mínima. (compare com isso )

f=(x,W=[],Q=['('],z=1,h=p=>'+-*/^^))('.indexOf(p)>>1,C=n=>{for(;h(q=Q.pop())<h(n);W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))a=W.pop(b=W.pop());z&&Q.push(q,n)})=>((x+')').replace(/\d+|\S/g,t=>t>'('?t>'('?~h(t)?z&&t=='-'?z=-z:C(t,z=1):(W.push(z*t),z=0):C(t,z=0):(Q.push(t),z=1)),W.pop())

// More readable
U=(x,W=[],Q=['('],z=1,
  h=p=>'+-*/^^))('.indexOf(p)>>1,
  C=n=>{
    for(;h(q=Q.pop())<h(n);
        W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))
      a=W.pop(b=W.pop());
    z&&Q.push(q,n)
  }
)=>(
  (x+')')
  .replace(/\d+|\S/g,t=> 
       t>'('
       ?t>'('
       ?~h(t)
       ?z&&t=='-'?z=-z:C(t,z=1)
       :(W.push(z*t),z=0)
       :C(t,z=0)
       :(Q.push(t),z=1)
  ),
  W.pop()
)

// TEST
console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(f('1 - 3 + 4')) // -6
console.log(f('2-2-2')) // 2
console.log(f('(2 + 2 * 3 + 3) / 3 + 3')) // 4
console.log(f('3 + 2 + 1 ^ 3')) // 216
console.log(f('-5^2')) // 25
console.log(f('32 / 8 * 3 - 1')) // 2

// Explained
X=(x,W=[],Q=['('],z=1,
  h=p=> // operator priority '+-:1, */:3, ^:5, ):7, (:9. If an operand then -1
     '+-*/^^))('.indexOf(p)>>1,
  C=n=>{ // Evaluate operators present on stack if they have upper priority, then push new operator on stack
    //console.log('Operand '+n)
    while( h(q = Q.pop()) < h(n) ) // pop operator from op stack and compare priority with current
    {
      // Pop operands from stack and push result
      b = W.pop();
      a = W.pop();
      r = q=='^' ? Math.pow(a,b) : eval('a'+q+'b')
      // console.log('Evaluate '+a+q+b+'='+r)
      W.push(r);
    }
    // if z == 0 do nothing, because the current operands are '(' and ')' that must be discarded
    // else Push again the last operator popped and the current one
    z && Q.push(q, n) // 
  }
)=>(
  (x+')')
  .replace(/[\d.]+|\S/g,t=> {
    //console.log('Q:'+Q,'W:'+W,'T:'+t,'U:'+h(t),'Z:'+z), // display status
    if (t == '(') 
    { // open parenthesis
      z = 1
      Q.push(t) // push a operator, its the highest priority
    }
    else if (t == ')')
    { //close parenthesis
      z = 0
      C(t) 
    }
    else if (h(t) < 0)
    { // operand
      W.push(z*t) // push operand with right sign
      z = 0 // set z to 0 to mark that we just pushed an operand, so next '-' (if present) is a binary operator 
    }
    else
    { // operator
      if (z && t=='-') // the minus is an unary operator (the only unary operator allowed is '-', '+' will not work)
        z =-z // change the sign
      else
        z = 1, // no unary minus
        C(t)
    }    
  }),
  W.pop() // return the value at top of operand stack
)
<pre id=O></pre>

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.