Quando vi o título dessa pergunta encerrada , pensei que parecia um desafio interessante para o código de golfe. Então deixe-me apresentá-lo como tal:
Desafio:
Escreva um programa, expressão ou sub-rotina que, dada uma expressão aritmética na notação infix , como 1 + 2
, emita a mesma expressão na notação postfix , ie 1 2 +
.
(Observação: um desafio semelhante foi postado no início de janeiro. No entanto, acho que as duas tarefas são suficientemente diferentes em detalhes para justificar esse desafio separado. Além disso, só notei o outro segmento depois de digitar tudo abaixo, e prefiro não apenas jogue tudo fora.)
Entrada:
A entrada é constituído por uma expressão aritmética infixa válido que consiste em números (números inteiros não negativos representada como sequências de um ou mais dígitos decimais), equilibrado parênteses para indicar uma subexpressão agrupados, e os quatro infixa binários operadores +
, -
, *
e /
. Qualquer um deles pode ser separado (e toda a expressão envolvida) por um número arbitrário de caracteres de espaço, que deve ser ignorado. 1 1
Para quem gosta de gramáticas formais, aqui está uma gramática simples, semelhante a BNF, que define entradas válidas. Por questões de clareza e clareza, a gramática não inclui os espaços opcionais, que podem ocorrer entre dois tokens (exceto dígitos dentro de um número):
expression := number | subexpression | expression operator expression
subexpression := "(" expression ")"
operator := "+" | "-" | "*" | "/"
number := digit | digit number
digit := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
1 O único caso em que a presença de espaços pode afetar a análise é quando eles separam dois números consecutivos; no entanto, como dois números não separados por um operador não podem ocorrer em uma expressão de infixo válida, esse caso nunca pode ocorrer em entrada válida.
Saída:
A saída deve ser uma expressão postfix equivalente à entrada. A expressão de saída deve consistir apenas de números e operadores, com um único carácter de espaço entre cada par de símbolos adjacentes, como na seguinte gramática (que não incluem os espaços) 2 :
expression := number | expression sp expression sp operator
operator := "+" | "-" | "*" | "/"
number := digit | digit number
digit := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
sp := " "
2 Novamente, por simplicidade, a number
produção nesta gramática admite números com zeros à esquerda, mesmo que sejam proibidos na saída pelas regras abaixo.
Operador precedente:
Na ausência de parênteses, as seguintes regras de precedência se aplicam:
- Os operadores
*
e/
têm maior precedência que+
e-
. - Os operadores
*
e/
têm precedência igual entre si. - Os operadores
+
e-
têm precedência igual entre si. - Todos os operadores são associativos à esquerda.
Por exemplo, as duas expressões a seguir são equivalentes:
1 + 2 / 3 * 4 - 5 + 6 * 7
((1 + ((2 / 3) * 4)) - 5) + (6 * 7)
e ambos devem produzir a seguinte saída:
1 2 3 / 4 * + 5 - 6 7 * +
(Essas são as mesmas regras de precedência que no idioma C e na maioria dos idiomas dele derivados. Provavelmente se assemelham às regras que você aprendeu no ensino fundamental, exceto, possivelmente, pela relativa precedência de *
e /
.)
Regras diversas:
Se a solução fornecida for uma expressão ou sub-rotina, a entrada deve ser fornecida e a saída retornada como uma única sequência. Se a solução for um programa completo, ele deve ler uma linha contendo a expressão infix da entrada padrão e imprimir uma linha contendo a versão do postfix na saída padrão.
Os números na entrada podem incluir zeros à esquerda. Os números na saída não devem ter zeros à esquerda (exceto o número 0, que deve ser exibido como
0
).Não é esperado que você avalie ou otimize a expressão de forma alguma. Em particular, você não deve assumir que os operadores necessariamente satisfazem qualquer identidade associativa, comutativa ou outras identidades algébricas. Ou seja, você não deve assumir que, por exemplo,
1 + 2
igual2 + 1
ou1 + (2 + 3)
igual a(1 + 2) + 3
.Você pode supor que os números na entrada não excedam 2 31 - 1 = 2147483647.
Essas regras visam garantir que a saída correta seja definida exclusivamente pela entrada.
Exemplos:
Aqui estão algumas expressões de entrada válidas e as saídas correspondentes, apresentadas no formulário "input" -> "output"
:
"1" -> "1"
"1 + 2" -> "1 2 +"
" 001 + 02 " -> "1 2 +"
"(((((1))) + (2)))" -> "1 2 +"
"1+2" -> "1 2 +"
"1 + 2 + 3" -> "1 2 + 3 +"
"1 + (2 + 3)" -> "1 2 3 + +"
"1 + 2 * 3" -> "1 2 3 * +"
"1 / 2 * 3" -> "1 2 / 3 *"
"0102 + 0000" -> "102 0 +"
"0-1+(2-3)*4-5*(6-(7+8)/9+10)" -> "0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -"
(Pelo menos, espero que tudo isso esteja correto; fiz a conversão manualmente, para que erros possam ter surgido.)
Só para esclarecer, as seguintes entradas são todas inválidas; ele não importa o que sua solução não se lhes deu (embora, é claro, por exemplo, retornando uma mensagem de erro é mais agradável do que, digamos, consumindo uma quantidade infinita de memória):
""
"x"
"1 2"
"1 + + 2"
"-1"
"3.141592653589793"
"10,000,000,001"
"(1 + 2"
"(1 + 2)) * (3 / (4)"
1 2 3 4 +
significa `1 + 2 + 3 + 4`.1 2 3 4 + *
?Respostas:
Shell utils - 60 caracteres
Corrigimos os vários problemas, mas demorou muito mais :(
fonte
sed -re's/[:@iKWr]+/ /g'
corrige o custo de 1 caractere.C,
250245236193185 caracteresAqui está uma versão legível da fonte não destruída, que ainda reflete a lógica básica. Na verdade, é um programa bastante simples. O único trabalho real que ele precisa fazer é colocar um operador de baixa associatividade em uma pilha quando um operador de alta associatividade for encontrado e, em seguida, recolocá-lo no "final" dessa subexpressão.
fonte
if
. Por exemploif(!*p||*p==41)return p;s[++t]=*p;}
->return*p&&*p-41?s[++t]=*p:p;
*f(p,s)char*p,s;{
if
teste falhar. 2. Eu sei, mas a função K&R decls é onde eu traço a linha. Eu simplesmente não posso voltar para eles.}}
efor
. Mas aqui está uma melhoria:printf(" %ld"+!a,...
p
global (a chamada recursiva apenas atribui o chamado dep
volta ao chamador). Então façaf(p=gets(b))
.Bash com Haskell com
Pré-processador Csed, 180195198275Por fim, não é mais do que a solução C. A parte crucial de Haskell é quase tão preguiçosa quanto a solução bc ...
Recebe a entrada como parâmetros da linha de comando. Um arquivo
w
com algumas mensagens de aviso do ghc será criado, se você não gostar dessa alteraçãorunghc 2>/dev/null
.fonte
Python 2,
290272268250243238 bytesAgora finalmente mais curto que a resposta JS!
Este é um programa completo que utiliza uma implementação básica do algoritmo shunting yard . A entrada é fornecida como uma string entre aspas e o resultado é impresso em
STDOUT
.Experimente online!
Explicação:
A primeira coisa que precisamos fazer é converter a entrada em tokens. Fazemos isso usando todas as correspondências da regex
\d+|\S
, traduzidas aproximadamente para "qualquer grupo de dígitos e qualquer caractere que não seja espaço". Isso remove o espaço em branco, analisa os dígitos adjacentes como tokens únicos e analisa os operadores separadamente.Para o algoritmo do shunting yard, existem 4 tipos de token distintos que precisamos tratar:
(
- Parêntese esquerdo)
- Parêntese direito+-*/
- Operadores9876543210
- Literais numéricosFelizmente, os códigos ASCII desses estão todos agrupados na ordem mostrada, para que possamos usar a expressão
(t>'/')+(t>')')+(t>'(')
para calcular o tipo de token. Isso resulta em 3 para dígitos, 2 para operadores, 1 para parêntese direito e 0 para parêntese esquerdo.Usando esses valores, indexamos a tupla grande após
exec
a execução do snippet correspondente, com base no tipo de token. Isso é diferente para cada token e é a espinha dorsal do algoritmo do shunting yard. Duas listas são usadas (como pilhas):O
(pilha de operação) eB
(buffer de saída). Após a execução de todos os tokens, os operadores restantes naO
pilha são concatenados com o buffer de saída e o resultado é impresso.fonte
Prolog (SWI-Prolog) , 113 bytes
Experimente online!
O SWI Prolog tem um conjunto muito melhor de componentes para isso do que o GNU Prolog, mas ainda é um pouco retido pela verbosidade da sintaxe do Prolog.
Explicação
term_to_atom
, se for executado ao contrário, analisará uma expressão de notação de infixo (armazenada como um átomo) em uma árvore de análise (obedecendo às regras de precedência usuais e excluindo zeros à esquerda e espaços em branco). Em seguida, usamos o predicado auxiliarc
para fazer uma recursão estrutural sobre a árvore de análise, convertendo em notação postfix de maneira profunda.fonte
Javascript (ES6), 244 bytes
Exemplo:
Chamada:
f('0-1+(2-3)*4-5*(6-(7+8)/9+10)')
Saída:
0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -
(com um espaço à direita)Explicação:
fonte
R, 142 bytes
R é capaz de analisar a si próprio; portanto, em vez de reinventar a roda, apenas colocamos o analisador em funcionamento, o que gera notação de prefixo e usamos uma função recursiva para alternar para a notação de postfix.
O
p
argumento é controlar o uso de avaliação não-padrão (a desgraça dos programadores R em todos os lugares), e existem algunsif
s extras para controlar a saída de colchetes (que queremos evitar).Entrada:
(0-1+(2-3)*4-5*(6-(7+8)/9+10))
Saída:
0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -
Entrada:
(((((1))) + (2)))
Saída:
1 2 +
Como bônus, funciona com símbolos arbitrários e quaisquer funções predefinidas com até dois argumentos:
Identidade de Euler
Entrada:
e^(i*pi)-1
Saída:
e i pi * ^ 1 -
Dividendos de 13 entre 1 e 100
Entrada:
which(1:100 %% 13 == 0)
Saída:
1 100 : 13 %% 0 == which
Regressão linear do peso do frango bebê em função do tempo
Entrada:
summary(lm(weight~Time, data=ChickWeight))
Saída:
weight Time ~ ChickWeight lm summary
O último exemplo talvez esteja um pouco fora do escopo do OP, mas ele usa a notação postfix, então ...
fonte