2012-08-28 18 views
7

Mam pytanie dotyczące oceny wyrażenia matematycznego w ciągu znaków. Na przykład mój ciąg jest następujący:Python - oblicz wyrażenie matematyczne w ciągu znaków

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

Zastanawiam się, jak ocenić ten ciąg i uzyskać następujący wynik:

'I have 30 apples' 

Czy jakikolwiek sposób to zrobić?

Z góry dziękuję.

P.S. funkcja Pythona eval nie pomaga w tym przypadku. Wystąpił błąd podczas próby oceny za pomocą funkcji eval.

+3

Czy to zadanie domowe? –

+3

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

+1

@jeffery_the_wind nie tak naprawdę w punkcie, ponieważ ten (w przeciwieństwie do tego) wymaga odrzucenia nie matematycznych części ciągu. –

Odpowiedz

0

Jest to bardzo trudny problem, który prawdopodobnie jest prawie niemożliwy do rozwiązania w ogóle. Jednak tutaj jest prosty sposób na zaatakowanie problemu, który działa z przykładowym wejściem.

* krok 1 - odkaż dane wejściowe. To jest najtrudniejsza rzecz do zrobienia w ogóle. Zasadniczo potrzebujesz sposobu na wyciągnięcie pojedynczego wyrażenia matematycznego z łańcucha bez jego zniekształcania. Oto prosty regex będzie działać:

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

* Krok 2 - ocenia za pomocą eval:

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

* Krok 3 - substytut powrotem

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

Cóż, to nie jest tak, że nie da się go rozwiązać w ogóle. Jest to źle zdefiniowany problem, jaki jest (co stanowi wyrażenie do oceny, a co nie?), Ale gdy już to naprawimy, łatwo zostanie to rozwiązane, jeśli faktycznie będziesz musiał próbować parsować rzeczy zamiast nadużywać 'eval'. – delnan

+1

@delnan - jeśli jest to nadużycie "eval", to co nie jest nadużyciem "eval" (w którym momencie, czy nie powinno być ono całkowicie usunięte z języka?). Myślę, że jest to całkiem dobre miejsce do użycia 'eval' biorąc pod uwagę, że problem jest wystarczająco ograniczony, aby parsować wyrażenie, które ma być ocenione poza ciągiem wejściowym. – mgilson

+0

Ja na przykład uważam, że 'eval' nie powinien być umieszczony w widocznym miejscu (tj. W globalnej przestrzeni nazw). Znam przypadki użycia dla 'compile' i' exec' (i są one znacząco różne od tego, ponieważ kontrolują ciąg wejściowy w 100% i wiadomo, co zrobi). Mam jeszcze do czynienia z dobrym przypadkiem użycia 'eval' - kiedy chcesz ocenić wyrażenie matematyczne, napisz ewaluatora stoczni manewrowej lub coś podobnego. Jeśli chcesz uruchomić kod Pythona, użyj 'exec', ponieważ jest on mniej ograniczony. Nie unikam 'eval' dla poprawności, ale żeby nie mieszać kodu z danymi. (Śrubuj facetów, którzy mówią, że kod to dane.) – delnan

2

Oto moja próba:

>>> 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' 

Notatki:

To bardzo proste, nie zajmie się złożonymi równaniami - jak te z pierwiastkiem kwadratowym, pi itd., Ale wierzę w to, o co chodzi w pytaniu. Aby uzyskać wiarygodną odpowiedź , zobacz question posted by jeffery_the_wind; ale uważam, że może to być przesada w tej uproszczonej sprawie.

0

Dla rozwiązania bez użycia eval, oto co bym zrobił. Zacznij od znalezienia wszystkich wyrażeń matematycznych w ciągu, których będę zdefiniować jako ciąg znaków, który zawiera spacje, nawiasy, cyfry i operacje, a następnie rozebrać się mecze, które są wszystkie spacje:

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

Następnie ocenić wyrażenia wykorzystujące klasę NumericStringParser z this question, który korzysta pyparsing:

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

Następnie, aby zastąpić wyniki z powrotem do wyrażenia, odwrócenie sortowania wyrażeń przez ich indeksu początkowego i umieścić je z powrotem do oryginalnego napisu:

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

Czasami lepiej jest uprościć pytanie niż skomplikowanych rozwiązań.Można uprościć ten problem poprzez swój kod być zapewnione jak to

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

ten sposób można analizować je za pomocą prostego wyrażenia regularnego i eval co jest w środku. W przeciwnym razie masz dużo złożoności.

0

Mój opcja:

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

Dzięki wszystkim za pomoc. W rzeczywistości mój podany przykład jest bardzo prosty w porównaniu z tym, co mam w prawdziwym zadaniu. Czytam te ciąg z pliku, a czasami to może mieć widok takiego:

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

równanie Math są proste, ale może występuje wiele czasu w jednym ciągu, i powinny być oceniane oddzielnie.

więc napisać przykładowy kod, który jest w stanie obsłużyć to przypadki: Może to nie jest tak dobra, ale rozwiązania problemu:

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

[wiem, że to jest stare pytanie, ale warto podkreślić, nowych użytecznych rozwiązań, ponieważ pop-up]

Od python3.6, ta funkcja jest obecnie wbudowane w języku, ukuty „F-strings”.

Patrz: PEP 498 -- Literal String Interpolation

Na przykład (uwaga f prefiks):

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' 
Powiązane problemy