我需要使用有效的python语法转换字符串,例如:
'1+2**(x+y)'
并获得等效的LaTeX:
$1+2^{x+y}$
我尝试过sympy的乳胶功能,但它会处理实际的表达,而不是它的字符串形式:
>>> latex(1+2**(x+y))
'$1 + 2^{x + y}$'
>>> latex('1+2**(x+y)')
'$1+2**(x+y)$'
但是要做到这一点,它需要将x和y声明为“符号”类型。
我想要更直接的东西,最好是使用编译器模块中的解析器。
>>> compiler.parse('1+2**(x+y)')
Module(None, Stmt([Discard(Add((Const(1), Power((Const(2), Add((Name('x'), Name('y'))))))))]))
最后但同样重要的是,为什么:我需要生成那些乳胶snipptes,以便我可以在带有mathjax的网页中显示它们。
答案 0 :(得分:16)
这是一个相当长但仍然不完整的方法,不涉及任何方式的同情。这足以涵盖(-b-sqrt(b**2-4*a*c))/(2*a)
的示例,该示例将转换为\frac{- b - \sqrt{b^{2} - 4 \; a \; c}}{2 \; a}
并呈现为
它基本上创建了AST并且它产生了与AST节点对应的乳胶数学。什么是应该给出足够的想法如何在它缺乏的地方扩展它。
import ast
class LatexVisitor(ast.NodeVisitor):
def prec(self, n):
return getattr(self, 'prec_'+n.__class__.__name__, getattr(self, 'generic_prec'))(n)
def visit_Call(self, n):
func = self.visit(n.func)
args = ', '.join(map(self.visit, n.args))
if func == 'sqrt':
return '\sqrt{%s}' % args
else:
return r'\operatorname{%s}\left(%s\right)' % (func, args)
def prec_Call(self, n):
return 1000
def visit_Name(self, n):
return n.id
def prec_Name(self, n):
return 1000
def visit_UnaryOp(self, n):
if self.prec(n.op) > self.prec(n.operand):
return r'%s \left(%s\right)' % (self.visit(n.op), self.visit(n.operand))
else:
return r'%s %s' % (self.visit(n.op), self.visit(n.operand))
def prec_UnaryOp(self, n):
return self.prec(n.op)
def visit_BinOp(self, n):
if self.prec(n.op) > self.prec(n.left):
left = r'\left(%s\right)' % self.visit(n.left)
else:
left = self.visit(n.left)
if self.prec(n.op) > self.prec(n.right):
right = r'\left(%s\right)' % self.visit(n.right)
else:
right = self.visit(n.right)
if isinstance(n.op, ast.Div):
return r'\frac{%s}{%s}' % (self.visit(n.left), self.visit(n.right))
elif isinstance(n.op, ast.FloorDiv):
return r'\left\lfloor\frac{%s}{%s}\right\rfloor' % (self.visit(n.left), self.visit(n.right))
elif isinstance(n.op, ast.Pow):
return r'%s^{%s}' % (left, self.visit(n.right))
else:
return r'%s %s %s' % (left, self.visit(n.op), right)
def prec_BinOp(self, n):
return self.prec(n.op)
def visit_Sub(self, n):
return '-'
def prec_Sub(self, n):
return 300
def visit_Add(self, n):
return '+'
def prec_Add(self, n):
return 300
def visit_Mult(self, n):
return '\\;'
def prec_Mult(self, n):
return 400
def visit_Mod(self, n):
return '\\bmod'
def prec_Mod(self, n):
return 500
def prec_Pow(self, n):
return 700
def prec_Div(self, n):
return 400
def prec_FloorDiv(self, n):
return 400
def visit_LShift(self, n):
return '\\operatorname{shiftLeft}'
def visit_RShift(self, n):
return '\\operatorname{shiftRight}'
def visit_BitOr(self, n):
return '\\operatorname{or}'
def visit_BitXor(self, n):
return '\\operatorname{xor}'
def visit_BitAnd(self, n):
return '\\operatorname{and}'
def visit_Invert(self, n):
return '\\operatorname{invert}'
def prec_Invert(self, n):
return 800
def visit_Not(self, n):
return '\\neg'
def prec_Not(self, n):
return 800
def visit_UAdd(self, n):
return '+'
def prec_UAdd(self, n):
return 800
def visit_USub(self, n):
return '-'
def prec_USub(self, n):
return 800
def visit_Num(self, n):
return str(n.n)
def prec_Num(self, n):
return 1000
def generic_visit(self, n):
if isinstance(n, ast.AST):
return r'' % (n.__class__.__name__, ', '.join(map(self.visit, [getattr(n, f) for f in n._fields])))
else:
return str(n)
def generic_prec(self, n):
return 0
def py2tex(expr):
pt = ast.parse(expr)
return LatexVisitor().visit(pt.body[0].value)
答案 1 :(得分:15)
您可以将sympy.latex
与eval
:
s = "1+2**(x+y)"
sympy.latex(eval(s)) # prints '$1 + {2}^{x + y}$'
你仍然需要将变量声明为符号,但是如果这确实是一个问题,那么编写解析器来解决所有问题比从头开始生成乳胶要容易得多。
答案 2 :(得分:10)
您可以使用SymPy。只需将字符串首先传递给sympify()
函数,然后将其转换为有效的SymPy表达式(即为您创建符号等)。所以你可以做到
>>> latex(sympify('1+2**(x+y)'))
1 + 2^{x + y}
S()
也是sympify()
的快捷方式,即latex(S('1+2**(x+y)'))
也有效。
答案 3 :(得分:4)
对Geoff Reedy的一个很好的解决方法:
class GenerateSymbols(defaultdict):
def __missing__(self, key):
self[key] = sympy.Symbol(key)
return self[key]
之前它不会将新项目添加到词典中。现在,您可以将其与表达式一起使用:
d= GenerateSymbols()
eq = '(-b-sqrt(b**2-4*a*c))/(2*a)'
您可以在将其转换为LaTeX之前进一步简化它:
sympy.latex(sympy.simplify(eval(eq,d)))
你得到了这个:
'$- \\frac{b + \\operatorname{sqrt}\\left(- 4 a c + b^{2}\\right)}{2 a}$'
答案 4 :(得分:3)
要建立在tom10的答案上,您可以定义一个生成符号的字典,并在调用eval时使用它:
from collections import defaultdict
class GenerateSymbols(defaultdict):
def __missing__(self, key):
return sympy.Symbol(key)
然后,如果你使用
sympy.latex(eval('1+2**(x+y)',GenerateSymbols()))
您不必担心为变量预先创建符号。