Hackerboard Wiki HaboBlog
Hackerboard bei Facebook Hackerboard bei Google+ Hackerboard bei Twitter

[HaBo]

 
Programmieraufgaben Hier wird regelmäßig eine neue Programmieraufgabe gestellt, die dann gelöst werden soll und in Zusammenarbeit mit den Moderatoren auch besprochen werden kann.

"Aus klein mach groß": Teil3 - Rechnen mit Zahlen

Diskussion: "Aus klein mach groß": Teil3 - Rechnen mit Zahlen im Forum Programmieraufgaben, in der Kategorie Code Kitchen; Anzeige dies ist die Fortsetzung der Aufgabenreihe "Aus klein mach groß": "Aus klein mach groß": Teil2 - Zahlen und Satzzeichen ...

Antwort
Alt 14.05.07, 16:47   #1 (permalink)
CDW
Moderator
 
Benutzerbild von CDW
 
Registriert seit: 20.07.05
CDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: Opteron
Likes: 202
Standard "Aus klein mach groß": Teil3 - Rechnen mit Zahlen

Anzeige

dies ist die Fortsetzung der Aufgabenreihe "Aus klein mach groß":
"Aus klein mach groß": Teil2 - Zahlen und Satzzeichen
"Aus klein mach groß": Teil1 - Stack
Und ist wie die ganze Reihe an Anfänger gerichtet.

Diesesmal nutzen wir die zwei vorherigen Aufgaben, um einen einfachen Rechner zu bauen, der die Eingaben im Postfix Format aktzeptiert.
Einige von uns kennen noch die alten Taschenrechner, die Eingaben in diesem Format erwartetet .
Dabei werden zuerst die Operanden und dann die Operatoren angegeben.
Bsp:
7 8 +
entspricht: 7+8=15
7 8 + 2 *
entspricht: (7+8 )*2
In dieser Notation braucht man also keine Klammern und auch keine "Gewichtung" der Operatoren.

Nutze also die Funktionen/Klassen aus der Teilaufgabe2 (ggf. anpassen/erweitern ), um ein UPN Rechner-Modul zu bauen.

Dabei bekommt das Rechner-Modul eine Eingabe als String
"1 2+" und gibt ein Ergebnis (oder eine Fehlermeldung) zurück.

Es sollten 4 Grundrechenarten akzeptiert werden: + - * /
Trennzeichen zwischen Operanden (also verschiedenen Zahlen): " " (Leerzeichen).
Trennzeichen zwischen Operanden und Operatoren/Operatoren: entweder Leerzeichen oder kein Trennzeichen.

Bsp: 1 2 + oder 1 2+

Implementiere zusätzlich zum Rechner-Modul ein Ausgabemodul, um
Zwischenergebnisse (optional: auch die durchgeführte Operation/Operatoren) anzuzeigen:
7 8+2 *
Ausgabe:
(7+8= )15 (in Klammern: optionale Ausgabe)
(15*2= )30
Ergebnis=30

Das Ausgabemodul sollte nur zur Fehlersuche/Überprüfung dienen und so integriert sein, dass es sich leicht (im Quellcode) "ausschalten" lässt (also die Anweisungen nicht "untrennbar" mit dem Rechner-Modul verbunden sind - somit bitte keine GUI/CUI anweisungen hineinquetschen, sondern einen Aufruf des Ausgabemoduls).

Gehe davon aus, dass die Eingabe "korrekt" ist, zumindest was das Zahlenformat angeht. Man muss also keine 123.123.123 Formen berücksitigen.

Allgemeine Tipps:
in der letzten Teilaufgabe haben wird aus einem Satz Zahlen und Satzzeichen ausgelesen. Diese Rotine lässt sich nun etwas anpassen (es waren nicht umsonst 4 Satzzeichen ), so dass */+- erkannt werden. Zahlenerkennung kann so belassen werden (es sei denn, man hat RegExpressions verwendet *g*).

Man könnte nun die Eingabe durchgehen und Zahlen einlesen/im Stack ablegen. Kommt ein Operator(*/+-) so werden die letzen beiden Zahlen aus dem Stack geholt und miteinander verrechnet - das Ergebnis kommt wieder auf den Stack.
Wenn nun der String abgearbeitet wurde, liegt im Stack das Endergebnis.

Gibt es bei einer Operation nicht mehr genügend Operanden (z.B wenn noch + abgearbeitet werden muss, im Zahlenstack aber nur eine einzige Zahl steht) oder liegen auf dem Stack nach der Abarbeitung mehrere Zahlen , so war die Eingabe fehlerhaft

__________________
Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf!
Selig, wer nichts zu sagen hat und trotzdem schweigt.
CDW ist offline   Mit Zitat antworten
Alt 15.05.07, 20:12   #2 (permalink)
 
Registriert seit: 03.12.04
Boar Leistung: Facit NTK
Likes: 0
Standard

So, hier meine Lösung (Java).

UPNModul.java   
Code:
public class UPNModul {

	// Der Zahlen-Stack
	private static Stack digits = new Stack();

	//Die Methode löst die eigebene Gleichung und gibt das Ergebnis als String zurück
	public static String solve(String line) throws RuntimeException {

		String number = ""; //Zwischenspeicher, um eine komplette Zahl zusammenzusetzn
		boolean floatFound = false; //Prüft, ob die Zahl schon einen Punkt hat
		while(!digits.isEmpty())
			digits.pop();
		
		for(int i=0; i<line.length(); i++) {
			
			char c = line.charAt(i); //Das aktuelle Zeichen
			
			//Ziffern oder der erste Punkt (Alle weiteren Punkte gehören nicht zur Zahl)
			if(Character.isDigit(c) || (c == '.' && !floatFound)) {
				number+=c; //Aktuelle Ziffer zum Zwischenspeicher hinzufügen
				if(c=='.') floatFound = true;
			}
			//Operatoren oder Leerzeichen
			else if(c == ' ' || c == '+' || c == '-' || c=='*' || c=='/') {
				//Die Zahl im Zwischenspeicher auf den Stack legen (wenn im Zwischenspeicher etwas steht)
				if(!number.equals("")) {
					numberInStack(number, floatFound);
					floatFound = false;
					number = "";
				}
				if(c == ' ') continue; //Wenn Leerzeichen: Prüfe das nächste Zeichen

				//Die 2 Zahlen aus dem Stack
				float digit1;
				float digit2;
				try {
					digit2 = (Float) digits.pop();
					digit1 = (Float) digits.pop();
				} catch (RuntimeException e) {
					//Wenn keine 2 Zahlen im Stack liegen (RuntimeException vom Stack) ist die Gleichung fehlerhaft
					throw new RuntimeException("Die Gleichung entspricht nicht der Postfixnotation");
				}
						
				//Je nach Operator wird das Ergebnis berechnet und wieder auf den Stack gelegt
				switch(c) {
				case '+': 
					digits.push(digit1 + digit2);
					break;
				case '-':
					digits.push(digit1 - digit2);
					break;
				case '*':
					digits.push(digit1 * digit2);
					break;
				case '/':
					digits.push(digit1 / digit2);
					break;
				}
			// Wenn es keine Ziffer, kein Leerzeichen und kein Operator war: Fehler
			} else {
				throw new RuntimeException("Falsche Eingabe: "+c);
			}
		}
		//Das Endergebnis aus dem Stack holen und als String zurück geben
		String result = ""+digits.pop();
		if(!digits.isEmpty() || !number.equals(""))
			throw new RuntimeException("Die Gleichung entspricht nicht der Postfixnotation");
		if(result.endsWith(".0"))
			result = result.substring(0,result.length()-2);
		return result;
	} //solve
	
	//Schreibt eine Zahl als Float oder Integer in den Stack
	private static void numberInStack(String number, boolean floatFound) {
		//Wenn die Zahl nur aus einem Punkt besteht wird dieser ignoriert
		if(number.startsWith(".") && number.length()==1) {
			number = "";
			return;
		}
		//Wenn die Zahl mit einem Punkt aufhört wird der Punkt abgeschnitten
		if(number.endsWith(".")) {
			number = number.substring(0,number.length()-1);
			floatFound = false;
		}
		digits.push(Float.parseFloat(number));
	} //numberInStack
}

UPNFrame.java   
Code:
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class UPNFrame extends JFrame implements ActionListener{

	private static final long serialVersionUID = -7501896257995843922L;
	
	private JPanel inputPanel, outputPanel;
	private JTextField input, output;
	private JLabel inputLabel, outputLabel;
	private JButton ok;
	
	public UPNFrame() {
		super("UPN-Rechner");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		input = new JTextField(20);
		output = new JTextField(10);
		inputLabel = new JLabel("Eingabe: ");
		outputLabel = new JLabel("Ergebnis: ");
		ok = new JButton("Enter");
		
		inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,10,5));
		inputPanel.add(inputLabel);
		inputPanel.add(input);
		inputPanel.add(ok);
		
		outputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT,10,5));
		outputPanel.add(outputLabel,BorderLayout.NORTH);
		outputPanel.add(output,BorderLayout.SOUTH);
		
		ok.addActionListener(this);
		input.addActionListener(this);
		
		this.setLayout(new BorderLayout());
		this.add(inputPanel,BorderLayout.NORTH);
		this.add(outputPanel, BorderLayout.SOUTH);
		
		this.pack();
		this.setLocationRelativeTo(null);
		this.setVisible(true);
	} //ExtractFrame
	
	public void actionPerformed(ActionEvent e) {
		if(!input.getText().equals("")) {
			try {
				String result = UPNModul.solve(input.getText());
				output.setText(result);
			} catch(RuntimeException exc) {
				JOptionPane.showMessageDialog(this, exc.getMessage(), "Fehler", JOptionPane.ERROR_MESSAGE);
			}
			
		}	
	} //actionPerformed
	
	public static void main(String[] args) {
		new UPNFrame();
	} //main
}


Diesmal hab ich GUI und Programmlogik getrennt und mit Exceptions gearbeitet.
Im Anhang wieder die .jar-Datei zum testen.

Gruß,
Boar
Angehängte Dateien
Dateityp: zip UPNModul.zip (3,9 KB, 50x aufgerufen)
Boar ist offline   Mit Zitat antworten
   
HaBOT
 
- Anzeige -

Werbung ist gerade online    
Alt 16.05.07, 15:49   #3 (permalink)
 
Benutzerbild von Eydeet
 
Registriert seit: 14.04.06
Eydeet Leistung: Facit NTK
Likes: 4
Standard

Meine Lösung entspricht zwar nicht ganz der geforderten Aufgabenstellung (Debug-Modus ist fest integriert), aber ich hoffe, das ist nicht so schlimm

Um den Debug-Modus zu aktivieren, muss man die Option --debug anhängen (eigentlich ist egal, was man anhängt, es wird nur die Anzahl an Argumenten überprüft).
Angehängte Dateien
Dateityp: zip teil3.zip (8,1 KB, 36x aufgerufen)
Eydeet ist offline   Mit Zitat antworten
Alt 16.05.07, 17:17   #4 (permalink)
CDW
Moderator
Themenstarter
 
Benutzerbild von CDW
 
Registriert seit: 20.07.05
CDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: OpteronCDW Leistung: Opteron
Likes: 202
Standard

@Eydeet: Du hast da ein paar Fehler drin
probier mal (im Debugmodus): 1 2+ 3 4+*
oder 5 6+ 7 8+*
(ohne das Leerzeichen zwischen +*)
oder 5 6+7 8+*

Es scheint irgendwie nicht möglich zu sein,
(5+6)*(7+8 ) zu berechnen
Wenn ich den Code richtig interpretiert habe: Du legst die Operatoren auf den Stack und rechnest nur, wenn ein NON-Operand/Operator kommt - das geht nicht gut
__________________
Noch mal, für alle Pseudo-Geeks: 1+1=0. -> 10 wäre Überlauf!
Selig, wer nichts zu sagen hat und trotzdem schweigt.
CDW ist offline   Mit Zitat antworten
Alt 17.05.07, 09:49   #5 (permalink)
 
Benutzerbild von Eydeet
 
Registriert seit: 14.04.06
Eydeet Leistung: Facit NTK
Likes: 4
Standard

Ups Ich hab beim Testen immer normale Rechnungen wie 1 * 2 + 4 - 3 o.ä. eingegeben, weil mir die andere Form noch nie begegnet ist.

Jetzt also mit Postfix-Notation:

EDIT:
Ich hab noch mal eine verbesserte (?) Version angehängt (calc-1.1.zip).

In dieser Version sind Teile der Main-Funktion in eine weitere Klasse ausgelagert, und man kann direkt mehrere Rechnungen hintereinander eingeben, die sich aufeinander beziehen, da der alte Stack weiterverwendet wird.

Das macht es einem möglich, mit dem alten Ergebnis weiterzurechnen.
Angehängte Dateien
Dateityp: zip teil3.zip (8,2 KB, 38x aufgerufen)
Dateityp: zip calc-1.1.zip (10,2 KB, 53x aufgerufen)
Eydeet ist offline   Mit Zitat antworten
Alt 22.03.08, 13:25   #6 (permalink)
 
Registriert seit: 22.03.08
MontyPerl Leistung: Facit NTK
Likes: 0
Standard

Hm, ich habs mal in perl gemacht, nimmt postfix sowie infix an. Bei postfix muss der Term komplett mit Leerzeichen getrennt sein, bei infix können die Klammern die Zahlen "berühren", aber Operatoren sollen immernoch "frei stehen". Und da immernur ein Argument pro "switch" erlaubt ist, muss man den Term auch quoten. (Auch um shell-Interpretation von Metazeichen entgegenzuwirken.)
Für Benutzungshilfe einfach den "-h" switch angeben.
*perl*   
Code:
#!/usr/bin/env perl
use warnings;
use strict;
use Getopt::Std;

# A little script for parsing mathematical expressions.

my $Operator = qr{[-+/%*x]};
my $Number = qr{[-+]?(:?(:?[1-9][0-9]*)?\.)?[1-9][0-9]*};
my $Bracket = qr{[)(]};


sub calcPostfix {
    my @numStack;
    foreach my $atom (@_) {
        if ($atom =~ m{^$Number$}) {push(@numStack, $atom)}
        elsif ($atom =~ m{^$Operator$}) {
            my ($b, $a) = (pop(@numStack), pop(@numStack));
            unless ($a) {$a = 0}
            unless ($b) {$b = 0}
            my $result;
            if    ($atom eq '*' or $atom eq 'x'){$result = $a * $b}
            elsif ($atom eq '/' or $atom eq '%'){$result = $a / $b}
            elsif ($atom eq '+')                {$result = $a + $b}
            elsif ($atom eq '-')                {$result = $a - $b}
            push(@numStack, $result)
        }
    }
    return pop(@numStack)
}

sub infix2postfix {
    my (@postfixTerm, @opStack);
    # A precendence-hash mapping operators to integers representing their precendence.
    my %precHash = ('*'   =>   2,
                    'x'   =>   2,
                    '%'   =>   2,
                    '/'   =>   2,
                    '+'   =>   1,
                    '-'   =>   1,
                    ')'   =>   0,
                    '('   =>   0,
                    'EOT' =>   0);

    push(@opStack, "EOT");
    PARSE: foreach my $atom (@_, "EOT") {       # EOT == "End of term" mark
        if    ($atom =~ m{^$Number$}) {push(@postfixTerm, $atom)}
        elsif ($atom =~ m{^$Operator$}) {
            if ($precHash{$atom} > $precHash{$opStack[$#opStack]}) {push(@opStack, $atom)}
            else {
                push(@postfixTerm, pop(@opStack));
                redo PARSE
            }
        }
        elsif ($atom =~ m{^$Bracket$}) {
            if ($atom eq '(') {push(@opStack, $atom)}
            else {
                while($opStack[$#opStack] ne '(') {push(@postfixTerm, pop(@opStack))}
                pop(@opStack)
            }
        }
        elsif ($atom eq "EOT") {
            push(@postfixTerm, pop(@opStack)) until ($opStack[$#opStack] eq "EOT")
        }
    }
    return @postfixTerm
}

sub Help {
    print "Usage: $0 [switch] [argument]\n\n";
    print "Available switches:\n";
    print "-h\t\tDisplay this help text and exit.\n";
    print "-i\t\tThis switch takes a term in infix notation as an argument.\n";
    print "-p\t\tThis switch takes a term in postfix notation as an argument.\n\n";
    print "Note: The postfix-notation has to be seperated by whitespaces.\n";
    print "This here for example:           $0 -p \'1 2 + 3 *\'\n";
    print "will work, while this here:      $0 -p \'1 2+ 3*\'\n";
    print "won\'t. Same goes for the infix notation, though the braces may\n";
    print "\"touch\" the digits like this:  $0 -i \'(1 + 2) * 3\'\n";
    exit(0)
}


unless (@ARGV) {Help()}

my %opts;
getopts('hi:p:', \%opts);

if (exists($opts{'h'})) {Help()}
print "<--Postfix Calculator---|\n";
print "                 \t|---------------------------------------------------------->\n";
if (exists($opts{'p'})) {
    print "Postfix notation:\t|\t$opts{'p'}\n";
    print "Result:          \t|\t".calcPostfix(split(/\s+/, $opts{'p'}))."\n";
    print "                 \t|---------------------------------------------------------->\n"
}
if (exists($opts{'i'})) {
    my @ifNotation = grep(/[^\s]+/, split(m{\s+|(?<=[\)-+/%*x0-9])(?=[)(])|(?<=[)(])(?=[\(-+/%*x1-9])}, $opts{'i'}));
    my @pfNotation = infix2postfix(@ifNotation);
    print "Infix notation:  \t|\t@ifNotation\n";
    print "Postfix notation:\t|\t@pfNotation\n";
    print "Result:          \t|\t".calcPostfix(@pfNotation)."\n";
    print "                 \t|---------------------------------------------------------->\n"
}

(Außerdem gehts von einem Unixoiden System aus (siehe shebang), also notfalls (der Fall in dem Windows installiert ist) einfach mit "perl skriptname" starten..)
MontyPerl ist offline   Mit Zitat antworten
Antwort
   
- Anzeige -

Werbung ist gerade online    

[HaBo] » Software Home » Code Kitchen » Programmieraufgaben » "Aus klein mach groß": Teil3 - Rechnen mit Zahlen
Themen-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks sind aus
Pingbacks sind aus
Refbacks sind aus


Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
rechnen mit php und sql Catarrer (Web-) Design und webbasierte Sprachen 6 06.09.08 12:09
"Aus klein mach groß": Teil2 - Zahlen und Satzzeichen CDW Programmieraufgaben 8 24.02.08 01:58
"Aus klein mach groß": Teil4- Zahlen anordnen CDW Programmieraufgaben 2 03.06.07 21:56
"Aus klein mach groß": Teil1 - Stack CDW Programmieraufgaben 2 08.05.07 17:07
Rechnen mit Formulartdaten ? Franzl (Web-) Design und webbasierte Sprachen 2 23.01.06 07:05


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61