Visualizador de ecuaciones artísticas Ascii

10

Tratar las ecuaciones en ausencia de un buen editor de ecuaciones es desordenado y desagradable. Por ejemplo, si quisiera expresar una integral y su solución, podría verse así:

Integral [x ^ 3 e ^ (- mx ^ 2 b / 2), dx] = - ((2 + b m x ^ 2) / (b ^ 2 * e ^ ((b m x ^ 2) / 2) * m ^ 2))

En integrals.wolfram.com , esto se llama "formulario de entrada". A nadie le gusta ver una ecuación en "forma de entrada". La forma ideal de visualizar esta ecuación sería:

ingrese la descripción de la imagen aquí

(Wolfram llama a esto "forma tradicional")

Para este codegolf, escriba un programa que tome alguna ecuación en "forma de entrada" como entrada y visualice esa ecuación en una representación ascii de "forma tradicional". Entonces, para este ejemplo, podríamos obtener algo como esto:

       /\      3
       |      x
       | ------------  dx = 
       |       2
      \/   (m x  b)/2
          e

              2
     2 + b m x
-(-----------------)
            2
   2  (b m x )/2  2
  b  e           m

Requisitos:

  1. No baraje, simplifique ni reorganice la entrada de ninguna manera. Preséntelo exactamente de la misma forma en que lo describió la entrada.
  2. Admite las cuatro operaciones matemáticas básicas (+, -, *, /). Cuando no se multiplican dos números adyacentes, el símbolo * está implícito y debe omitirse.
  3. No se requiere soporte para la integración (como se muestra en el ejemplo anterior) . Poder admitir entradas con funciones como Integrar [...] o Sqrt [...] es una ventaja.
  4. Potencias de soporte como se muestra en el ejemplo anterior (la raíz enésima se puede modelar elevando a la potencia enésima).
  5. Los paréntesis redundantes (como los que rodean al denomentador y el numerador de la fracción grande en el ejemplo anterior) deben omitirse.
  6. La expresión en el denominador y numerador de una fracción debe estar centrada encima y debajo de la línea de división horizontal.
  7. Puede elegir iniciar o no una nueva línea después de un signo igual. En el ejemplo anterior, se inicia una nueva línea.
  8. El orden de las operaciones debe ser exactamente el mismo en la salida que en la entrada.

Algunos ejemplos de entrada y salida asociada para probar su solución:

Entrada:

1/2 + 1/3 + 1/4

Salida:

1   1   1
- + - + -
2   3   4

Entrada:

3x^2 / 2 + x^3^3

Salida:

   2     3
3 x     3
---- + x   
 2

Entrada:

(2 / x) / (5 / 4^2)

Salida:

2
-
x
--
5
--
 2
4

Entrada:

(3x^2)^(1/2)

Salida:

    2 1/2
(3 x )
Ami
fuente
Su pregunta generalmente debe tener una etiqueta que exprese qué tipo de concurso es. Me tomé la libertad de agregar uno porque dijiste "codegolf" en el texto.
dmckee --- ex-gatito moderador
3
Este problema es demasiado nebuloso para ser código golf. No dice qué construcciones deben ser compatibles, o cómo deben verse. ¿Sería suficiente solo con +, -, * y /? ¿Sigma tiene que ser apoyado? ¿Qué pasa con las letras griegas? Las posibles soluciones a la pregunta, tal como la ha planteado, podrían tener una funcionalidad demasiado variada como para compararla con la longitud del código.
MtnViewMark
@MtnViewMark, agregué algunos "Requisitos" ... avíseme si el golf es mejor ahora.
Ami
@Ami, sí, mucho.
MtnViewMark
Estoy de acuerdo con MtnViewMark, esto parece muy abierto y vago. Quizás sería mejor limitar la entrada y la salida a un conjunto bien definido de casos de prueba para jugar golf. ¿Has hecho una implementación de referencia?
gnibbler

Respuestas:

10

Python 2, 1666 caracteres

El diseño en realidad es bastante fácil: el análisis de la entrada es un verdadero problema. Todavía no estoy seguro de que sea completamente correcto.

import re,shlex
s=' '
R=range

# Tokenize.  The regex is because shlex doesn't treat 3x and x3 as two separate tokens.  The regex jams a space in between.                                                 
r=r'\1 \2'
f=re.sub
x=shlex.shlex(f('([^\d])(\d)',r,f('(\d)([^\d])',r,raw_input())))
T=[s]
while T[-1]:T+=[x.get_token()]
T[-1]=s

# convert implicit * to explicit *                                                                                                                                          
i=1
while T[i:]:
 if(T[i-1].isalnum()or T[i-1]in')]')and(T[i].isalnum()or T[i]in'('):T=T[:i]+['*']+T[i:]
 i+=1

# detect unary -, replace with !                                                                                                                                            
for i in R(len(T)):
 if T[i]=='-'and T[i-1]in'=+-*/^![( ':T[i]='!'
print T

# parse expression: returns tuple of op and args (if any)                                                                                                                   
B={'=':1,',':2,'+':3,'-':3,'*':4,'/':4,'^':5}
def P(t):
 d,m=0,9
 for i in R(len(t)):
  c=t[i];d+=c in'([';d-=c in')]'
  if d==0and c in B and(B[c]<m or m==B[c]and'^'!=c):m=B[c];r=(c,P(t[:i]),P(t[i+1:]))
 if m<9:return r
 if'!'==t[0]:return('!',P(t[1:]))
 if'('==t[0]:return P(t[1:-1])
 if'I'==t[0][0]:return('I',P(t[2:-1]))
 return(t[0],)

# parenthesize a layout                                                                                                                                                     
def S(x):
 A,a,b,c=x
 if b>1:A=['/'+A[0]+'\\']+['|'+A[i]+'|'for i in R(1,b-1)]+['\\'+A[-1]+'/']
 else:A=['('+A[0]+')']
 return[A,a+2,b,c]

# layout a parsed expression.  Returns array of strings (one for each line), width, height, centerline                                                                      
def L(z):
 p,g=z[0],map(L,z[1:])
 if p=='*':
  if z[1][0]in'+-':g[0]=S(g[0])
  if z[2][0]in'+-':g[1]=S(g[1])
 if p=='^'and z[1][0]in'+-*/^!':g[0]=S(g[0])
 if g:(A,a,b,c)=g[0]
 if g[1:]:(D,d,e,f)=g[1]
 if p in'-+*=,':
  C=max(c,f);E=max(b-c,e-f);F=C+E;U=[s+s+s]*F;U[C]=s+p+s;V=3
  if p in'*,':U=[s]*F;V=1
  return([x+u+y for x,u,y in zip((C-c)*[s*a]+A+(E-b+c)*[s*a],U,(C-f)*[s*d]+D+(E-e+f)*[s*d])],a+d+V,F,C)
 if'^'==p:return([s*a+x for x in D]+[x+s*d for x in A],a+d,b+e,c+e)
 if'/'==p:w=max(a,d);return([(w-a+1)/2*s+x+(w-a)/2*s for x in A]+['-'*w]+[(w-d+1)/2*s+x+(w-d)/2*s for x in D],w,b+e+1,b)
 if'!'==p:return([' -  '[i==c::2]+A[i]for i in R(b)],a+2,b,c)
 if'I'==p:h=max(3,b);A=(h-b)/2*[s*a]+A+(h-b+1)/2*[s*a];return(['  \\/|/\\  '[(i>0)+(i==h-1)::3]+A[i]for i in R(h)],a+3,h,h/2)
 return([p],len(p),1,0)

print'\n'.join(L(P(T[1:-1]))[0])

Para la gran entrada en la pregunta, obtengo:

 /\         2                     2 
 |     - m x  b          2 + b m x  
 |     --------    = - -------------
 |  3      2                    2   
\/ x  e         dx         b m x    
                           ------   
                        2     2    2
                       b  e       m 

Aquí hay algunos casos de prueba más difíciles:

I:(2^3)^4
O:    4
  / 3\ 
  \2 / 

I:(2(3+4)5)^6
O:             6
  (2 (3 + 4) 5) 

I:x Integral[x^2,dx] y
O:   /\ 2     
  x  | x  dx y
    \/        

I:(-x)^y
O:     y
  (- x) 

I:-x^y
O:     y
  (- x)

Ese último está mal, algún error de precedencia en el analizador.

Keith Randall
fuente
¿No debería la línea de base del argumento integral estar centrada en sentido vertical después de la integral? Actualmente se parece más a un subíndice de la integral.
Joey
No es difícil de cambiar, pero desperdiciaría algo de espacio. Actualmente hago que el signo integral sea lo suficientemente grande como para abarcar su argumento (con un mínimo de 3).
Keith Randall
Golf leve: use pestañas en lugar de espacios dobles.
CalculatorFeline