Python实现问题

时间:2009-09-13 17:11:40

标签: python data-structures implementation formula

嘿。我有一个问题,我正在尝试用Python解决,我想不出一个聪明的实现。输入是一串字母。其中一些代表变量,另一些代表运算符,我想迭代大量的变量值(不同的配置)。

我认为一个等价的问题是你得到一个方程式,其中许多变量存储为字符串,并且当你用x,y,z等代替许多不同的值或者满足布尔公式时,你想看到解决方案。

我想不出任何可以实现这一点的聪明的数据结构 - 每次“设置”公式,替换变量,花费的时间比实际评估它要多。

我知道这有点抽象 - 但是任何人都有任何想法或者有过与之相似的经历吗?

有人用过我想要实现一个评估者。这是真的,但除此之外。为了问题,假设eval(字符串)函数已经实现。什么是有效的数据结构,它将保存值并允许每次干净有效地更改它们?肯定不是一个字符串!它必须是可修改的列表。如果它有变量x的一些实例,它应该能够快速地将它们全部考虑在内并在评估之前改变它们的值等等。

4 个答案:

答案 0 :(得分:1)

因为Ira说你需要一个像Lex / Yacc这样的表达式评估员来完成这项工作,你可以在这里找到很多:

http://wiki.python.org/moin/LanguageParsing

我一直在使用pyparsing并为这类事做点精彩

答案 1 :(得分:0)

你想要的显然是表达评估者。

其他你使用通常称为“eval”的语言的内置工具(我不知道Python是否提供此功能)或者你构建了一个表达式解析器/评估器。

要了解如何构建这样的表达式求值程序的一些想法, 你可以看看下面的SO高尔夫练习: Code Golf: Mathematical expression evaluator (that respects PEMDAS)

要做你想做的事,你必须将基本的想法(它们几乎都是相同的)翻译成Pythonese,并添加一个工具来解析变量名并查找它们的值。 这应该是一个非常直接的延伸。

答案 2 :(得分:0)

将该字符串解析为树(或等效的可解释数据结构),只需一次,然后重复使用函数为每个感兴趣的变量赋值集“解释树”。 (您甚至可以生成Python字节码作为“可解释的数据结构”,因此您可以使用eval作为“解释树” - 这使得生成缓慢,但这只需要一次,并且快速解释)。

正如你所说的那样有点抽象,所以让我们给出一个具体的,如果过于简单化的例子。例如,变量是字母x,y,z,t,并且运算符是用于加法的运算符和用于减法的运算符 - 相邻字母的字符串隐含地表示高优先级乘法,如通常的数学约定;没有括号,严格的从左到右执行(即没有运算符优先级,超出乘法)。必须忽略除这6个以外的每个字符。那么,这是一个非常特殊的解析器和Python字节码生成器:

class BadString(Exception): pass

def makeBytecode(thestring):
  theoperator = dict(a='+', s='-')
  python = []
  state = 'start'
  for i, letter in enumerate(thestring):
    if letter in 'xyzt':
      if state == 'start':
        python.append(letter)
        state = 'variable'
      elif state == 'variable':
        python.append('*')
        python.append(letter)
    elif letter in 'as':
      if state == 'start':
        raise BadString(
            'Unexpected operator %r at column %d' % (letter, i))
      python.append(theoperator[letter])
      state = 'start'

  if state != 'variable':
    raise BadString(
      'Unexpected operator %r at end of string' % letter)
  python = ''.join(python)
  # sanity check
  # print 'Python for %r is %r' % (thestring, python)
  return compile(python, thestring, 'eval')

现在,您可以简单地调用eval,其结果为第一个参数,dict将值与x,y,z和t相关联作为第二个参数。例如(已将上述模块导入为par并取消注释完整性检查):

>>> c=par.makeBytecode('xyax')
Python for 'xyax' is 'x*y+x'
>>> for x in range(4):
...   for y in range(5):
...     print 'x=%s, y=%s: result=%s' % (x,y,eval(c,dict(x=x,y=y)))
... 
x=0, y=0: result=0
x=0, y=1: result=0
x=0, y=2: result=0
x=0, y=3: result=0
x=0, y=4: result=0
x=1, y=0: result=1
x=1, y=1: result=2
x=1, y=2: result=3
x=1, y=3: result=4
x=1, y=4: result=5
x=2, y=0: result=2
x=2, y=1: result=4
x=2, y=2: result=6
x=2, y=3: result=8
x=2, y=4: result=10
x=3, y=0: result=3
x=3, y=1: result=6
x=3, y=2: result=9
x=3, y=3: result=12
x=3, y=4: result=15
>>> 

对于更复杂但仍然简单的解析字符串&构建快速可解释的数据结构,例如参见pyparsing

答案 3 :(得分:0)

我看到已经提到了eval这个想法,如果您有运营商替代,这将无济于事,但我过去曾使用过以下内容:

def evaluate_expression(expr, context):
    try:
        return eval(expr, {'__builtins__': None}, context)
    except (TypeError, ZeroDivisionError, SyntaxError):
        # TypeError is when combining items in the wrong way (ie, None + 4)
        # ZeroDivisionError is when the denominator happens to be zero (5/0)
        # SyntaxError is when the expression is invalid
        return None

然后您可以执行以下操作:

values = {
    'a': 1.0,
    'b': 5.0,
    'c': 1.0,
}
evaluate_expression('a + b - (1/c)', **values)

将评估为1.0 + 5.0 - 1 / 1.0 == 5.0

同样,这不会让你在运算符中替换,也就是说,你不能让'd'求值为+,但是它为你提供了一种在Python中使用eval函数的安全方法(你例如,不能运行“while True”。

有关安全使用eval的详情,请参阅this example