Buch Cover Buch Cover Buch Cover Buch Cover

Web-Code: - Webcode Help

Ohm'sche Schaltung / Compilerbau (Rekursion)

Um eine Programmiersprache zu schreiben, wird für algebraische Ausdrücke das selbe Verfahren verwendet wie in dieser Aufgabe. Wir benutzen hier lediglich zwei Operatoren sowie Zahlen. Das Verfahren kann aber einfach auf kompliziertere Ausdrücke wie weitere Operatoren, Funktionsaufrufe und Variablennamen ausgedehnt werden.

Ausgangslage: Um einen elektrischen Widerstand (Maßeinheit Ohm [?]) in einem Schaltkreis zu berechnen, gelten die folgenden zwei Regeln:

  1. Serienschaltung: Sind die Widerstände hintereinander geschaltet (in Serie) so zählt man diese einfach zusammen (100 Ohm + 200 Ohm= 300 Ohm)
  2. Parallelschaltung: Sind die Widerstände nebeneinander (parallel) geschaltet, so ergibt sich der Gesamtwiderstand aus dem Reziprokwert der Summe der einzelnen Reziprokwerte: 100 Ohm parallel zu 200 Ohm (Im Symbol: 100 | 200) ergibt {1\over{{1\over{100}} + {1\over{200}}}} = 66.67\Omega.

Die Parallelschaltung soll in unserer "Programmiersprache" stärker binden als die Serienschaltung (wie die letzten beiden Beispiele der folgenden Tabelle zeigen).

Code
Schaltkreis
100 + 200
Serieschaltung
100 | 200
100 Ohm parallel zu 200 Ohm 
100 | 200 + 300 | 400
Serieschaltungssymbol bindet stärker als Parallelschaltung
100 | (200 + 300) | 400
Serieschaltung innerhalb einer Parallelschaltung benötigt Klammersetzung

Um einen Parser (= Syntaxanalysierer) zu schreiben, bedient man sich zunächst einiger einfacher Zustandsdiagramme (hier als grafische Notation und als EBNFEBNF=Erweiterte Backus-Naur-Form).

Parser Zustandsdiagramm und EBNF

Schaltung (Bestehend aus mehreren in Serie geschalteten Summanden)

 Summand Parser Zustand
  Schaltung ::= Summand {"+" Summand}
Summand (bestehend aus mehreren parallelen Widerständen)
 Summand bestehend aus mehreren Widerständen
  Summand ::= Widerstand {"|" Widerstand}
Widerstand (entweder eine Zahl oder ein Klammerausdruck)
 Zahl oder Klammerausdruck
  Widerstand ::= Zahl | "(" Schaltung ")"

Als Eingabe ins Programm dient nun eine Zeichenkette, die eine Schaltung repräsentiert. Beispiel:

( 3 | 4 + 5 ) + 4 + 5 | 6 + 7 | ( 8 + 9 ) + 4 | 30

Ein Scanner soll in der Lage sein, nach und nach jedes Symbol (Klammer, Operator oder Zahl) zurückgeben. Eine Funktion des Scanners soll das nächste Symbol anzeigen [zeigeNaechstesSymbol()], eine zweite Funktion [symbolWurdeBenutzt()] soll das Symbol als gelesen bzw. verbraucht kennzeichnen. Das gibt uns die Möglichkeit, Zeichen nur anzusehen, ohne sie zu benutzen. Das ist immer dann nützlich, wenn das Ende eines Zustands erreicht ist. Kommt nämlich im Beispiel des Summanden kein "|"-Symbol mehr, so sind wir fertig mit der Behandlung des Summanden und dürfen das nächst gelesene Zeichen nicht "konsumieren", d. h. wir dürfen das Zeichen erst in der übergeordneten Struktur wieder verwenden.

Vorgegeben sind musterhaft die Programmcodes der Schaltung und des Widerstandes:

public double parseSchaltung()
{
  value: double
  value := parseSummand()
  // Pluszeichen?
  while("+" = scanner.zeigeNaechstesSymbol())
  {
    scanner.symbolWurdeBenutzt()
    value := value + parseSummand()
  }
  return value
}
private double parseWiderstand()
{
  sc: String
  sc := scanner.zeigeNaechstesSymbol()
  if("(" = sc)
  {
    scanner.symbolWurdeBenutzt()
    value: double
    value :=  parseSchaltung()
    sc := scanner.zeigeNaechstesSymbol()
    if(")" = sc)
    {
      scanner.symbolWurdeBenutzt()
      return value
    }
    else
    {
      System.out.println("Error in parsing ')' expected.")
      exit()
    }
  }
  // Muss eine Zahl sein, denn der Fall des Klammerausdrucks wurde
  // bereits behandelt:
  if(istGanzzahl(sc))
  {
    scanner.symbolWurdeBenutzt()
    return sc  
  }
  else
  {
    System.out.println("Error in parsing: Number expected.")
    exit()
  }
  return 0
} //unreachable

Schreiben Sie die fehlende Funktion des Summanden (parseSummand()). Dazu sind natürlich die Funktionen des Scanners unumgänglich.

Zusatzaufgabe: Schreiben Sie ein Programm, das algebraische Ausdrücke der Form

4 + (-3 + 2 / 6) * 5

für ganze Zahlen und die Operatoren: +, -, *, / (inkl. Vorzeichen) korrekt auswerten kann.

0 Kommentare

Bitte melde dich an um einen Kommentar abzugeben

2 Lösung(en)

import java.util.Scanner;

public class PushBackScanner {
  Scanner sc;
  
  String betrachte;
  
  public PushBackScanner(String scannable) {
    sc = new Scanner(scannable); }
  
  /*
   *  Betrachte das nächste Symbol.
   *  Zeige es, oder zeige "null", falls kein Symbol mehr vorhanden ist.
   */
  public String zeigeNaechstesSymbol() {
    if(betrachte != null) {
      return betrachte;      }
    int nextInt;
    try {
      nextInt = sc.nextInt();
      betrachte = "" + nextInt;
      return betrachte;      } 
    catch (Exception ex) { } // no int
    try {
      String c = sc.next(); // "(", ")", "+" oder "|"
      betrachte = "" + c;
      return betrachte; }
    catch (Exception ex) {
      return null;  } // end of file?
  }
  
  /**
   * Vernichte das aktuelle Symbol in der "Warteschlange"
   */
  public void symbolWurdeBenutzt() {
      betrachte = null; }

} // end of class Scanner

/***********************************/

public class Widerstand {

  PushBackScanner scanner;
   
  public Widerstand() {
    this("( 3 | 4 + 5 ) + 4 + 5 | 6 + 7 | ( 8 + 9 ) + 4 | 30");
  }
  public Widerstand(String schaltung) {
    this.scanner = new PushBackScanner(schaltung);
    System.out.println(schaltung + " ergibt " 
                         + parseSchaltung() + " Ohm.");
  }
  
  public double parseSchaltung() {
    double value = parseSummand();
    // Pluszeichen?
    while("+".equals(scanner.zeigeNaechstesSymbol())) {
      scanner.symbolWurdeBenutzt();
      value = value + parseSummand(); }
      return value;  }
   
  public double parseSummand() {
    double invValue = 1.0 / parseWiderstand();
    // Senkrechten Strich (= Parallelschaltung)
    while("|".equals(scanner.zeigeNaechstesSymbol())) {
      scanner.symbolWurdeBenutzt();
      invValue = invValue +
                 1.0 / parseWiderstand(); }
    return 1.0 / invValue;  }
    
  private double parseWiderstand() {
    String sc = scanner.zeigeNaechstesSymbol();
    if("(" .equals(sc)) {
      scanner.symbolWurdeBenutzt();
      double value =  parseSchaltung();
      sc = scanner.zeigeNaechstesSymbol();
      if(")".equals(sc)) {
        scanner.symbolWurdeBenutzt();
        return value; } 
      else {
        System.out.println("Error in parsing ')' expected.");
        System.exit(0); } // Throw exception
    }
    // Muss eine Zahl sein, denn der Fall des Klammerausdrucks wurde
    // bereits behandelt:
    if(istGanzzahl(sc)) {
      scanner.symbolWurdeBenutzt();
      int w = Integer.parseInt(sc);
      return w; }
    else {
      System.out.println("Error in parsing: Number expected.");
      System.exit(0); }
    return 0; } //unreachable 

  private boolean istGanzzahl(String sc) {
    try {
      Integer.parseInt(sc); }
    catch (NumberFormatException nfe) {
      return false;  }
    return true;  }
  
  public static void main(String[] args) {
    new Widerstand();  }
}

                
# Komplett neuer Parser in Ruby
#   Kombi aus rekursiver und iterativer Auflösung des eingegebenen Netzwerks
#   -> nutzt den Eingabestring via "Suchen und Ersetzen" als eine Art Stack-Ersatz.

def get_operand_boundaries(operator, str)
    # Zeichengrenzen der Operanden berechnen
    # a -> Start erste Zahl
    # b -> Stelle des Operators = Ende erste Zahl, Anfang zweite Zahl
    # c -> Ende zweite Zahl

    if str.include?(operator)

        b = str.index(operator)
        
        before_operator = str[0, b]
        after_operator = str[b+1, str.length]
        
        a1 = before_operator.rindex("|")
        if not a1
            a1 = 0
        end
        
        a2 = before_operator.rindex("+")
        if not a2
            a2 = 0
        end
        
        a = [a1, a2].max()
        
        c1 = after_operator.index("|")
        if not c1
            c1 = after_operator.length
        end
        
        c2 = after_operator.index("+")
        if not c2
            c2 = after_operator.length
        end        
        
        c = [c1, c2].min()
        c+=b
    
        return [a, b, c]
    
    else
    
        return nil
        
    end

end

def parser(input)

    while input.include?("(") # Löse zuerst _alle_ Klammern und deren Inhalt im akutellen Ausdruck (rekursiv)

        a = input.index("(")
        b = input[a, input.length].index(")")

        result = parser(input[a + 1, b - 1])
        input[a, b + 1] = result
        
    end
    
    while (input.include?("|")) # Löse danach von links nach rechts den |-Operator im aktuellen Ausruck auf (iterativ)

        boundaries = get_operand_boundaries("|", input)
        
        if boundaries

            x = input[boundaries[0] + 1, boundaries[1]].chomp.to_f()
            y = input[boundaries[1] + 1, boundaries[2]].chomp.to_f()
            
            result = 1.0 * x * y / (x + y)
            input[boundaries[0] + 1, boundaries[2] - boundaries[0] - 1] = result.to_s
            
        end
        
    end
        
    while (input.include?("+")) # Löse zum Schluss v.l.n.r. den +-Operator im aktuellen Ausdruck auf (iterativ)

        boundaries = get_operand_boundaries("+", input)
        
        if boundaries
        
            x = input[boundaries[0] + 1, boundaries[1]].chomp.to_f()
            y = input[boundaries[1] + 1, boundaries[2]].chomp.to_f()
            
            result = x + y
            input[boundaries[0] + 1, boundaries[2] - boundaries[0] - 1] = result.to_s
            
        end
        
    end
    
    return input
end



puts "Gib ein Widerstandsnetzwerk ein:"
input = gets.chomp
puts "Wert des Netzwerkes:"
print parser(input), " Ohm"

                

Lösung von: Ich Bins (tubs)

Verifikation/Checksumme:

( 3 | 4 + 5 ) + 4 + 5 | 6 + 7 | ( 8 + 9 ) + 4 | 30 ergibt: 21.93 Ohm

Aktionen

Bewertung

Durchschnittliche Bewertung:

Eigene Bewertung:
Bitte zuerst anmelden

Meta

Zeit: 4.8
Schwierigkeit: k.A.
Webcode: reye-jj52
Autor: Philipp G. Freimann (BBW (Berufsbildungsschule Winterthur) https://www.bbw.ch)

Zu Aufgabenblatt hinzufügen