apohllo.pl
więcej niż strona domowa...
 

ONP

Notacja infiksowa, prefiksowa i sufiksowa

Zwykła notacja algebraiczna (zwana notacja infiksową) posiada pewne wady, jeśli weźmiemy pod uwagę łatwość jej interpretacji przez komputery:
  • operatory posiadają priorytety (tzn. mnożenie wykonywane jest przed dodawaniem)
  • zmiana priorytetów operatorów wymaga użycia nawiasów

Alternatywnymi wobec niej notacjami są notacja prefiksowa (przedrostkowa) oraz sufiksowa (przyrostkowa, odwrotna notacja polska). W pierwszej z nich operator (symbol reprezentujący działanie) stoi przed operandami (argumentami działania), a w drugiej – za nimi. Przykładowo:

2 + 3 notacja konwencjonalna
+ 2 3 notacja prefiksowa
2 3 + notacja sufiksowa

Notacja prefiksowa oraz sufiksowa nie posiadają wad notacji tradycyjnej, tzn. kolejność operacji zawsze wyznaczana jest przez położenie argumentów, a nawiasy nie są stosowane. Z tego względu zapis w tych notacjach może być w znacznie prostszy sposób interpretowany przez komputer. Z drugiej strony, brak nawiasów sprawa, że dla człowieka zapis tego rodzaju jest trudniejszy w interpretacji.

Odwrotna notacja polska

Obliczanie wartości

ONP pozwala na konstrukcję bardzo prostego algorytmu obliczania wartości wyrażenia. Polega on na tym, że jeśli w w wyrażeniu czytanym od lewej pojawia się operand (liczba), to wkładany jest on na stos, natomiast jeśli pojawia się operator, to ze stosu zdejmowane są jego argumenty, a na stos trafia wynik działania. Ostateczny wynik jest odczytywany ze stosu.

Przykładowo dla wyrażenia ‘2 3 4 + *’, zmiany zawartości stosu wyglądają następująco:

2 <= 2
2 3 <= 3
2 3 4 <= 4
2 7 <= + (3 + 4)
14 <= * (2 * 7)

Wynik: 14

Poniżej przedstawiony jest kod w języku Ruby, który pozwala na ewaluację wyrażenia w ONP przekazanego jako argument programu (uwaga – zakłada się, że wszystkie elementy oddzielone są pojedynczymi spacjami):

expr = ARGV[0]
stack = []
expr.split(/\s+/).each do |token|
  case token
  when /[+*\/^-]/
    op2 = stack.pop
    op1 = stack.pop
    case token
    when "+" 
      stack.push(op1.to_f + op2.to_f)
    when "-" 
      stack.push(op1.to_f - op2.to_f)
    when "*" 
      stack.push(op1.to_f * op2.to_f)
    when "/" 
      stack.push(op1.to_f / op2.to_f)
    when "^" 
      stack.push(op1.to_f ** op2.to_f)
    end 
  else 
    stack.push token
  end
end
puts stack.pop

Konwersja z notacji konwencjonalnej do ONP

Istnieje dosyć prosty algorytm pozwalający na dokonanie konwersji pomiędzy notacją konwencjonalną, a ONP. Na wstępie należy ustalić priorytety operatorów w systemie konwencjonalnym:

operator(y) priorytet
^ 4
* / 3
+ – 2
= 1
( 0
Następnie analizując zawartość wyrażenia należy wykonywać następujące akcje:
  • jeśli element jest liczbą, to trafia on bezpośrednio do reprezentacji wyjściowej wyrażenia
  • jeśli element jest operatorem, to trafia na stos, wpierw jednak, ze stosu zdejmowane są kolejno wszystkie operatory, których priorytet jest większy bądź równy priorytetowi operatora, który ma być włożony na stos i trafiają one do reprezentacji wyjściowej
  • jeśli element jest nawiasem otwierający, to trafia on na stos
  • jeśli element jest nawiasem zamykającym, to ze stosu zdejmowane są wszystkie elementy, aż do pierwszego nawiasu otwierającego. Elementy te trafiają do reprezentacji wyjściowej. Nawiasy – otwierający i zamykający – są pomijane.
  • na koniec wszystkie elementy pozostałe na stosie, dopisywane są do reprezentacji wyjściowej

Przykładowo:

(2 + 3) * 4
2 3 + 4 *
Zawartość stosu:
( <= (
( + <= +
<= ) zdjęcie ze stosu + i (
 * <= *

Poniższy kod w Ruby realizuję konwersję z systemu konwencjonalnego do ONP:

expr = ARGV[0]
result = []
stack = []
PRIOR = Hash.new(0).merge({
  "^" => 4,
  "*" => 3, "/" => 3,
  "+" => 2, "-" => 2,
  "=" => 1,
  "(" => 0
})
expr.scan(/[*\/+^-]|\d+|\(|\)/) do |token|
  case token
  when /\A[*\/+^-]\Z/
    while PRIOR[(token1 = stack.last)] >= PRIOR[token]
      result << stack.pop
    end
    stack.push token
  when "(" 
    stack.push token
  when ")" 
    while (token1 = stack.pop) != "(" 
      result << token1
    end
  else
    result << token
  end
end
while (token = stack.pop)
  result << token 
end
puts result.join(" ")
onp | Opublikowano 11:05 03-02-2011. Ostatnia modyfikacja 04:37 04-02-2011 |
comments powered by Disqus