2012-08-28 29 views
7

Tôi có một câu hỏi liên quan đến việc đánh giá biểu thức toán học trong một chuỗi. Ví dụ chuỗi của tôi là như sau:Python - Đánh giá biểu thức toán học trong chuỗi

my_str='I have 6 * (2 + 3) apples' 

Tôi đang tự hỏi làm thế nào để đánh giá chuỗi này và nhận được kết quả sau:

'I have 30 apples' 

có phải là cách nào để làm điều này?

Xin cảm ơn trước.

P.S. python's eval chức năng không giúp đỡ trong trường hợp này. Nó đưa ra một lỗi, khi cố gắng đánh giá với hàm eval.

+3

là bài tập về nhà này? –

+3

http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string –

+1

@jeffery_the_wind không thực sự ở điểm, vì điều này (không giống như vậy) yêu cầu loại bỏ các phần phi toán học của chuỗi. –

Trả lời

0

Đây là một vấn đề rất phức tạp mà có lẽ gần như không thể giải quyết nói chung. Tuy nhiên, đây là một cách đơn giản để tấn công vấn đề làm việc với đầu vào ví dụ.

* bước 1 - khử trùng đầu vào. Đây là phần khó nhất để làm nói chung. Về cơ bản, bạn cần một cách để kéo một biểu thức toán học ra khỏi chuỗi mà không mangling nó. Dưới đây là một regex đơn giản sẽ làm việc:

sanitized = re.sub(r'[a-zA-Z]','',my_str).strip() 

* Bước 2 - Đánh giá sử dụng eval:

value = eval(sanitized, {'__builtins__':None}) 

* Bước 3 - trở lại thay thế

new_string = my_str.replace(sanitized, str(value)) 
+0

Vâng, nó không phải là nó không thể được giải quyết nói chung. Đó là một vấn đề không được định nghĩa như là (những gì cấu thành một biểu thức được đánh giá và cái gì không?) Nhưng một khi chúng ta cắt nó xuống, nó có thể dễ dàng giải quyết, nếu bạn thực sự bận tâm phân tích nội dung thay vì lạm dụng 'eval'. – delnan

+1

@delnan - nếu đây là lạm dụng 'eval', thì điều gì không phải là' eval' lạm dụng (tại thời điểm đó, không nên xóa hoàn toàn ngôn ngữ đó?). Tôi nghĩ rằng đây là một nơi hoàn toàn tốt để sử dụng 'eval' cho rằng vấn đề là đủ hạn chế để phân tích các biểu thức được đánh giá ra khỏi chuỗi đầu vào. – mgilson

+0

Tôi cho một người nghĩ rằng 'eval' không nên được đặt ở vị trí nổi bật (tức là trong không gian tên chung). Tôi biết các ca sử dụng cho 'compile' và' exec' (và chúng khác biệt đáng kể so với điều này, ở chỗ chúng kiểm soát chuỗi đầu vào 100% và nó biết nó sẽ làm gì). Tôi chưa gặp phải một trường hợp sử dụng tốt cho 'eval' - khi bạn muốn đánh giá một biểu thức toán học, viết một bộ đánh giá sân shunting hoặc một cái gì đó. Nếu bạn muốn chạy mã Python, sử dụng 'exec' vì nó ít bị hạn chế hơn. Tôi không tránh 'eval' cho tính chính xác, nhưng không trộn lẫn mã với dữ liệu. (Vít những người lisp người nói mã là dữ liệu.) – delnan

2

Đây là nỗ lực của tôi:

>>> import string 
>>> s = 'I have 6 * (2+3) apples' 
>>> symbols = '^*()/+-' 
>>> formula = [(x,s.index(x)) for x in s if x in string.digits+symbols] 
>>> result = eval(''.join(x[0] for x in formula), {'__builtins__':None}) 
>>> s = s[:formula[0][1]] + str(result) + s[formula[-1][1]+1:] 
>>> s 
'I have 30 apples' 

Ghi chú:Điều này rất đơn giản, nó sẽ không đối phó với các phương trình phức tạp - giống như những phương trình có căn bậc hai, pi, v.v ... nhưng tôi tin vào tinh thần của câu hỏi sau là gì. Để có một số thực sự câu trả lời mạnh mẽ, hãy xem question posted by jeffery_the_wind; nhưng tôi tin rằng nó có thể là quá mức cần thiết cho trường hợp đơn giản này.

0

Đối với giải pháp không sử dụng eval, dưới đây là những gì tôi sẽ làm. Bắt đầu bằng cách tìm tất cả các biểu thức toán học trong chuỗi, mà tôi sẽ xác định như là một chuỗi có chứa khoảng trắng, dấu ngoặc đơn, số, và các hoạt động, sau đó loại bỏ các trận đấu mà tất cả các khoảng trắng:

>>> import re 
>>> my_str = 'I have 6 * (2 + 3) apples' 
>>> exprs = list(re.finditer(r"[\d\.\s\*\+\-\/\(\)]+", my_str)) 
>>> exprs = [e for e in exprs if len(my_str[e.start():e.end()].strip()) > 0] 

Tiếp theo, đánh giá các biểu thức bằng cách sử dụng lớp NumericStringParser từ this question, trong đó sử dụng pyparsing:

>>> nsp = NumericStringParser() 
>>> results = [nsp.eval(my_str[e.start():e.end()]) for e in exprs] 
>>> results 
[30.0] 

sau đó, để thay thế các kết quả trở lại vào biểu hiện, ngược lại loại những biểu hiện bởi chỉ số bắt đầu của họ và đặt chúng trở lại vào chuỗi gốc:

>>> new_str = my_str 
>>> for expr, res in sorted(zip(exprs, results), key=lambda t: t[0].start(), reverse=True): 
...  new_str = new_str[:expr.start()] + (" %d " % res) + new_str[expr.end():] 
... 
>>> new_str 
'I have 30 apples' 
2

Đôi khi tốt hơn là đơn giản hóa câu hỏi thay vì đưa ra các giải pháp phức tạp.Bạn có thể muốn đơn giản hóa vấn đề bằng cách mã của bạn được cung cấp như thế này

my_str='I have {6 * (2 + 3)} apples' 

Bằng cách này bạn có thể phân tích nó sử dụng một regex đơn giản và eval gì bên trong. Nếu không, bạn đang ở trong rất nhiều phức tạp.

0

lựa chọn của tôi:

>>> import re 
>>> def calc(s): 
...  val = s.group() 
...  if not val.strip(): return val 
...  return " %s " % eval(val.strip(), {'__builtins__': None}) 
>>> re.sub(r"([0-9\ \.\+\*\-\/(\)]+)", calc, "I have 6 * (2 + 3) apples") 
'I have 30 apples' 
1

Nhờ tất cả sự giúp đỡ của bạn. Thực tế, ví dụ được cung cấp của tôi rất đơn giản so với những gì tôi có trong thực tế. Tôi đọc những chuỗi từ tập tin và đôi khi là có thể có cái nhìn như thế này:

my_str='ENC M6_finger_VNCAPa (AA SYZE BY (0.14*2)) < (0.12 + 0.07) OPPOSITE REGION' 

phương trình Math đều đơn giản nhưng có thể xảy ra nhiều lần trong một chuỗi, và nên được đánh giá riêng rẽ.

Vì vậy, tôi viết một mẫu mã, đó là khả năng xử lý các trường hợp này: Có lẽ nó không phải là như vậy tốt, nhưng giải quyết vấn đề:

def eval_math_expressions(filelist): 
     for line in filelist: 
       if re.match('.*[\-|\+|\*|\/].*',line): 
         lindex=int(filelist.index(line)) 
         line_list=line.split() 
         exp=[] 
         for word in line_list: 
           if re.match('^\(+\d+',word) or re.match('^[\)+|\d+|\-|\+|\*|\/]',word): 
             exp.append(word) 
           else: 
             ready=' '.join(exp) 
             if ready: 
               eval_ready=str(eval(ready)) 
               line_new=line.replace(ready,eval_ready) 
               line=line_new 
               filelist[lindex]=line 
             exp=[] 
     return filelist 
0

[Tôi biết đây là một câu hỏi cũ, nhưng nó là giá trị chỉ ra các giải pháp hữu ích mới khi họ bật lên]

Kể từ python3.6, khả năng này ngay bây giờ là được xây dựng vào ngôn ngữ, đặt ra "f-strings".

Xem: PEP 498 -- Literal String Interpolation

Ví dụ (chú ý f prefix):

f'I have {6 * (2 + 3)} apples' 
=> 'I have 30 apples' 
color = 'green' 
f'I have {6 * (2 + 3)} {color} apples' 
=> 'I have 30 green apples'