Visualiseur d'équations artistiques Ascii

10

Traiter des équations en l'absence d'un bon éditeur d'équations est désordonné et désagréable. Par exemple, si je voulais exprimer une intégrale et sa solution, cela pourrait ressembler à ceci:

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

Sur integrals.wolfram.com , cela s'appelle "formulaire de saisie". Personne n'aime voir une équation sous "forme d'entrée". La façon idéale de visualiser cette équation serait:

entrez la description de l'image ici

(Wolfram appelle cette "forme traditionnelle")

Pour ce codegolf, écrivez un programme qui prendra une équation en "forme d'entrée" en entrée et visualisez cette équation dans une représentation ascii de "forme traditionnelle". Donc, pour cet exemple, nous pourrions obtenir quelque chose comme ceci:

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

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

Exigences:

  1. Ne mélangez pas, ne simplifiez pas ou ne réorganisez pas l'entrée de quelque façon que ce soit. Rendez-le exactement sous la même forme que celle décrite par l'entrée.
  2. Prend en charge les quatre opérations mathématiques de base (+, -, *, /). Lorsqu'il ne multiplie pas deux nombres adjacents, le symbole * est implicite et doit être omis.
  3. La prise en charge de l'intégration (comme illustré dans l'exemple ci-dessus) n'est pas requise. Être en mesure de prendre en charge la saisie avec des fonctions comme [...] Integrate ou Sqrt est un bonus.
  4. Prend en charge les pouvoirs comme indiqué dans l'exemple ci-dessus (la nième racine peut être modélisée en augmentant à la 1 / nième puissance).
  5. Les parenthèses redondantes (comme celles autour du dénomentateur et du numérateur de la grande fraction dans l'exemple ci-dessus) doivent être omises.
  6. L'expression au dénominateur et au numérateur d'une fraction doit être centrée au-dessus et au-dessous de la ligne de division horizontale.
  7. Vous pouvez choisir de commencer ou non une nouvelle ligne après un signe égal. Dans l'exemple ci-dessus, une nouvelle ligne est démarrée.
  8. L'ordre des opérations doit être exactement le même dans la sortie que dans l'entrée.

Quelques exemples d'entrées et de sorties associées pour tester votre solution:

Contribution:

1/2 + 1/3 + 1/4

Production:

1   1   1
- + - + -
2   3   4

Contribution:

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

Production:

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

Contribution:

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

Production:

2
-
x
--
5
--
 2
4

Contribution:

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

Production:

    2 1/2
(3 x )
Suis-je
la source
Votre question devrait généralement avoir une étiquette indiquant le type de concours. J'ai pris la liberté d'en ajouter un parce que vous avez dit "codegolf" dans le texte.
dmckee --- chaton ex-modérateur
3
Ce problème est trop nébuleux pour être du code-golf. Vous ne dites pas quelles constructions doivent être prises en charge, ni à quoi elles doivent ressembler. Serait-ce juste de supporter +, -, * et / serait suffisant? Sigma doit-il être pris en charge? Et les lettres grecques? Les solutions possibles à la question que vous avez posée pourraient être trop variées en termes de fonctionnalité pour être comparées pour la longueur du code.
MtnViewMark
@MtnViewMark, j'ai ajouté quelques "exigences" ... faites-moi savoir si le golf est meilleur maintenant.
Ami
@Ami - oui, beaucoup.
MtnViewMark
Je suis d'accord avec MtnViewMark, cela semble très ouvert et vague. Il serait peut-être préférable de limiter l'entrée et la sortie à un ensemble bien défini de tests pour le golf. Avez-vous fait une implémentation de référence?
gnibbler

Réponses:

10

Python 2, 1666 caractères

La mise en page est en fait assez facile - c'est l'analyse de l'entrée qui est une douleur royale. Je ne suis toujours pas sûr que ce soit complètement correct.

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])

Pour la grande contribution à la question, j'obtiens:

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

Voici quelques cas de test plus délicats:

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)

Ce dernier est faux, une erreur de priorité dans l'analyseur.

Keith Randall
la source
la ligne de base de l'argument intégral ne devrait-elle pas être centrée de manière véridique après l'intégrale? Actuellement, il ressemble plus à un indice à l'intégrale.
Joey
Pas difficile à changer, mais cela ferait perdre de l'espace. Je fais actuellement le signe intégral juste assez grand pour couvrir son argument (avec un minimum de 3 élevé).
Keith Randall
Golf léger: utilisez des tabulations au lieu des espaces doubles.
CalculatorFeline