Cuando vi el título de esta pregunta cerrada , pensé que parecía un interesante desafío de código de golf. Así que permítanme presentarlo como tal:
Reto:
Escriba un programa, expresión o subrutina que, dada una expresión aritmética en notación infija , como 1 + 2
, genera la misma expresión en notación postfix , es decir 1 2 +
.
(Nota: se publicó un desafío similar a principios de enero. Sin embargo, creo que las dos tareas son lo suficientemente diferentes en detalle para justificar este desafío por separado. Además, solo noté el otro hilo después de escribir todo lo siguiente, y prefiero no solo tirarlo todo)
Entrada:
La entrada consiste en una expresión aritmética infija válido que consta de números (números enteros no negativos representan como secuencias de uno o más dígitos decimales), equilibrado de paréntesis para indicar una subexpresión agrupados, y los cuatro infija binarios operadores +
, -
, *
y /
. Cualquiera de estos puede estar separado (y toda la expresión rodeada) por un número arbitrario de caracteres de espacio, que deben ignorarse. 1
Para aquellos a quienes les gustan las gramáticas formales, aquí hay una gramática simple similar a BNF que define entradas válidas. Por brevedad y claridad, la gramática no incluye los espacios opcionales, que pueden ocurrir entre dos tokens (que no sean dígitos dentro de un 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 El único caso donde la presencia de espacios puede afectar el análisis es cuando separan dos números consecutivos; sin embargo, dado que dos números no separados por un operador no pueden aparecer en una expresión infija válida, este caso nunca puede ocurrir en una entrada válida.
Salida:
La salida debe ser una expresión postfix equivalente a la entrada. La expresión de salida debe consistir solamente de números y los operadores, con un solo carácter de espacio entre cada par de fichas adyacentes, como en el siguiente gramática (que no incluye los espacios) 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 Nuevamente, por simplicidad, la number
producción en esta gramática admite números con ceros a la izquierda, a pesar de que están prohibidos en la salida por las siguientes reglas.
Precedencia del operador:
En ausencia de paréntesis, se aplican las siguientes reglas de precedencia:
- Los operadores
*
y/
tienen mayor prioridad que+
y-
. - Los operadores
*
y/
tienen la misma precedencia entre sí. - Los operadores
+
y-
tienen la misma precedencia entre sí. - Todos los operadores son asociativos a la izquierda.
Por ejemplo, las siguientes dos expresiones son equivalentes:
1 + 2 / 3 * 4 - 5 + 6 * 7
((1 + ((2 / 3) * 4)) - 5) + (6 * 7)
y ambos deberían producir el siguiente resultado:
1 2 3 / 4 * + 5 - 6 7 * +
(Estas son las mismas reglas de precedencia que en el lenguaje C y en la mayoría de los lenguajes derivados de él. Probablemente se parecen a las reglas que le enseñaron en la escuela primaria, excepto posiblemente por la precedencia relativa de *
y /
.)
Reglas misceláneas:
Si la solución dada es una expresión o una subrutina, la entrada debe proporcionarse y la salida debe devolverse como una sola cadena. Si la solución es un programa completo, debería leer una línea que contenga la expresión infija de la entrada estándar e imprimir una línea que contenga la versión postfix en la salida estándar.
Los números en la entrada pueden incluir ceros a la izquierda. Los números en la salida no deben tener ceros a la izquierda (excepto el número 0, que se emitirá como
0
).No se espera que evalúe u optimice la expresión de ninguna manera. En particular, no debe suponer que los operadores necesariamente satisfacen cualquier identidad asociativa, conmutativa u otra identidad algebraica. Es decir, no debe suponer que, por ejemplo,
1 + 2
es igual2 + 1
o que1 + (2 + 3)
es igual(1 + 2) + 3
.Puede suponer que los números en la entrada no exceden 2 31 - 1 = 2147483647.
El objetivo de estas reglas es garantizar que la entrada defina de forma exclusiva la salida correcta.
Ejemplos:
Aquí hay algunas expresiones de entrada válidas y las salidas correspondientes, presentadas en la forma "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 + * -"
(Al menos, espero que todo esto sea correcto; hice la conversión a mano, por lo que los errores podrían haber aparecido).
Para ser claros, las siguientes entradas no son válidas; sí no importa lo que su solución no si les da (aunque, por supuesto, por ejemplo, devuelve un mensaje de error es más agradable que, por ejemplo, el consumo de una cantidad infinita de memoria):
""
"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 + *
?Respuestas:
Utilidades de Shell: 60 caracteres
Se corrigieron los diversos problemas, pero se hizo mucho más largo :(
fuente
sed -re's/[:@iKWr]+/ /g'
arregla a un costo de 1 personaje.C,
250245236193185 caracteresAquí hay una versión legible de la fuente no protegida, que aún refleja la lógica básica. En realidad es un programa bastante sencillo. El único trabajo real que tiene que hacer es empujar un operador de baja asociatividad a una pila cuando se encuentra un operador de alta asociatividad, y luego volverlo a abrir al "final" de esa subexpresión.
fuente
if
. Ej.if(!*p||*p==41)return p;s[++t]=*p;}
->return*p&&*p-41?s[++t]=*p:p;
*f(p,s)char*p,s;{
if
prueba falla. 2. Lo sé, pero la función K&R declina es donde dibujo la línea. Simplemente no puedo volver a ellos.}}
yfor
. Pero aquí hay una mejora:printf(" %ld"+!a,...
p
global (la llamada recursiva solo asigna la llamadap
a la persona que llama). Entonces hazlof(p=gets(b))
.Bash con Haskell con
Preprocesador Csed, 180195198275Por fin, ya no es más que la solución C. La parte crucial de Haskell es casi tan floja como la solución bc ...
Toma datos como parámetros de línea de comandos.
w
Si no le gusta este cambio, se creará un archivo con algunos mensajes de advertencia de ghcrunghc 2>/dev/null
.fuente
Python 2,
290272268250243238 bytes¡Ahora finalmente más corto que la respuesta de JS!
Este es un programa completo que utiliza una implementación básica del algoritmo de patio de maniobras . La entrada se proporciona como una cadena entre comillas y el resultado se imprime en
STDOUT
.Pruébalo en línea!
Explicación:
Lo primero que debemos hacer es convertir la entrada en tokens. Hacemos esto mediante la búsqueda de todas las coincidencias de la expresión regular
\d+|\S
, traducidas aproximadamente a "cualquier grupo de dígitos y cualquier carácter no espacial". Esto elimina el espacio en blanco, analiza los dígitos adyacentes como tokens individuales y analiza los operadores por separado.Para el algoritmo de patio de maniobras, hay 4 tipos de tokens distintos que debemos manejar:
(
- Paréntesis izquierdo)
- Paréntesis derecho+-*/
- Operadores9876543210
- Literales numéricosAfortunadamente, los códigos ASCII de estos están agrupados en el orden mostrado, por lo que podemos usar la expresión
(t>'/')+(t>')')+(t>'(')
para calcular el tipo de token. Esto da como resultado 3 para dígitos, 2 para operadores, 1 para paréntesis derecho y 0 para paréntesis izquierdo.Usando estos valores, indexamos en la tupla grande luego
exec
de obtener el fragmento correspondiente para ejecutar, según el tipo de token. Esto es diferente para cada ficha y es la columna vertebral del algoritmo de patio de maniobras. Se utilizan dos listas (como pilas):O
(pila de operaciones) yB
(búfer de salida). Una vez que se han ejecutado todos los tokens, los operadores restantes en laO
pila se concatenan con el búfer de salida y se imprime el resultado.fuente
Prólogo (SWI-Prolog) , 113 bytes
Pruébalo en línea!
SWI Prolog tiene un conjunto mucho mejor de componentes integrados para esto que GNU Prolog, pero aún se ve frenado por la verbosidad de la sintaxis de Prolog.
Explicación
term_to_atom
Si se ejecuta hacia atrás, analizará una expresión de notación infija (almacenada como un átomo) en un árbol de análisis (obedeciendo las reglas de precedencia habituales y eliminando los ceros a la izquierda y los espacios en blanco). Luego usamos el predicado auxiliarc
para hacer una recursión estructural sobre el árbol de análisis, convirtiéndolo en notación postfix de una manera profunda primero.fuente
Javascript (ES6), 244 bytes
Ejemplo:
Llamada:
f('0-1+(2-3)*4-5*(6-(7+8)/9+10)')
Salida:
0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -
(con un espacio final)Explicación:
fuente
R, 142 bytes
R es capaz de analizarse a sí mismo, por lo que, en lugar de reinventar la rueda, simplemente ponemos a trabajar el analizador, que genera la notación de prefijo, y usamos una función recursiva para cambiarlo a notación de postfix.
El
p
argumento es controlar el uso de la evaluación no estándar (la pesadumbre de los programadores de R en todas partes), y hay algunosif
s adicionales para controlar la salida de paréntesis (que queremos evitar).Entrada:
(0-1+(2-3)*4-5*(6-(7+8)/9+10))
Salida:
0 1 - 2 3 - 4 * + 5 6 7 8 + 9 / - 10 + * -
Entrada:
(((((1))) + (2)))
Salida:
1 2 +
Como beneficio adicional, funciona con símbolos arbitrarios y cualquier función predefinida con hasta dos argumentos:
Identidad de Euler
Entrada:
e^(i*pi)-1
Salida:
e i pi * ^ 1 -
Dividendos de 13 entre 1 y 100
Entrada:
which(1:100 %% 13 == 0)
Salida:
1 100 : 13 %% 0 == which
Regresión lineal del peso del pollo bebé en función del tiempo.
Entrada:
summary(lm(weight~Time, data=ChickWeight))
Salida:
weight Time ~ ChickWeight lm summary
El último ejemplo está quizás un poco fuera del alcance del OP, pero usa notación postfix, así que ...
fuente