[QUIZ#2] kuddeldaddeldu (PHP)

kuddeldaddeldu

Erfahrenes Mitglied
Hi,

- Term wird nochmal in Infixnotation ausgegeben (und zwar mit Spendierhosen bzgl. der Klammerung :-( )
- das Setzen von Variablen ist möglich
- es können zusätzliche Funktionen definiert werden

Benutzung:

- Variable definieren: a := <term>
- Beenden: :quit
- Variablen ausgeben: :vars
- Umgebung zurücksetzen: :clear
- Ergebnis der letzten Berechnung: _

Als Erweiterung hatte ich mir das Speichern von Formeln gedacht, aber da das doch alles ziemlich fummelig war, hab' ich das nicht mehr geschafft...

PHP:
#/usr/bin/php5
<?php
/*************************************************************
* Parserklasse zum Einlesen des PN-Terms in eine Baumstruktur
* das Input-Array wird per Referenz übergeben, um die
* Rekursion zu ermöglichen
**************************************************************/
class TermParser {
   
   // Parst die Eingabe in einen Objektbaum (rekursiv)
   public function __construct(&$input) {
      $this->item = $this->getToken($input);
      // Token ist arithmetischer Operator => zwei Subterme parsen 
      if($this->is_op($this->item)) {
	 $this->operands[] = new TermParser($input);
	 $this->operands[] = new TermParser($input);
      } else {
	 if(!is_numeric($this->item)) {
	    // Token ist benutzerdefinierte Funktion f(1,..,n) => n Subterme parsen
	    if(method_exists('F', $this->item)) {
	       for($i = 0; $i < F::getArity($this->item); $i++) {
		  $this->operands[] = new TermParser($input);
	       }
	    } else {
	       // Token ist Variablenname => Inhalt aus Environment holen
	       $env = Environment::getInstance();
	       $this->item = $env->getVar($this->item);
	    }
	 }
      }
   }
   
   // Auswertung der Terme (rekursiv)
   public function calculate() {
      // Item ist arithmetischer Operator => Infixnotation evaluieren
      if($this->is_op($this->item)) {
	 eval('$result = ' . $this->operands[0]->calculate() . $this->item . $this->operands[1]->calculate() . ';');
	 // Item ist benutzerdefinierte Funktion:
	 // Argumente berechnen und Funktionsausdruck evaluieren
      } else if(method_exists('F', $this->item)) {
	 foreach($this->operands as $term) {
	    $args[] = $term->calculate();
	 }
	 eval('$result = F::' . $this->item . '(' . implode(',', $args) . ');'); 
      } else {
	 // Item ist Operand
	 $result = $this->item;
      }
      return $result;
   }
   
   // Ausgabe der Infixnotation (rekursiv)
   // nicht ganz, wie gewünscht. Additionen und Subtraktionen
   // werden gnadenlos in Klammern gepackt, ob nötig oder nicht ^^
   public function output() {
      if($this->is_op($this->item)) {
	 $str = $this->operands[0]->output() . $this->item . $this->operands[1]->output();
	 if($this->is_sum($this->item)) {
	    $str = '(' . $str . ')';
	 }
      } else if(method_exists('F', $this->item)) {
	 $str = $this->item . '(' . implode(',', array_map(create_function('$item', 'return $item->output();'), $this->operands)) . ')';
      } else {
	 $str = $this->item;
      }
      return $str;
   }
   
   private function getToken(&$array) {
      return trim(array_shift($array));
   }
         
   private function is_op($string) {
      return in_array($string, array('+','-','*','/'));
   }
   
   private function is_sum($string) {
      return in_array($string, array('+','-'));
   }
}

/******************************************************************
* Klasse für den Shelldialog mit Umgebungsvariablen
* Implementiert als Singleton
*******************************************************************/
class Environment {
   
   static private $instance = NULL;
   
   private $variables; //Container für benutzerdefinierte Variablen
   private $formulas; //Container für benutzerdefinierte Formeln (ToDo)
   
   static public function getInstance() {
      if (self::$instance === NULL) {
	 self::$instance = new self;
      }
      return self::$instance;
   }
   
   private function __construct() {}
   
   // benutzerdefinierte Variable auslesen
   public function getVar($name) {
      return isset($this->variables[$name]) ? $this->variables[$name] : '';
   }
   
   // benutzerdefinierte Variable setzen
   // der Wert kann ein beliebiger gültiger Term sein
   public function setVar($name, $term) {
      $parser = new TermParser($term);
      $this->variables[$name] = $parser->calculate();
   }
   
   // Alle Variablen ausgeben (Eingabe ":vars")
   private function printVars() {
      foreach($this->variables as $name => $value) {
	 echo $name . ' = ' . $value . "\n";
      }
   }
   
   // Umgebung "resetten" (Eingabe ":clear")
   private function init() {
      $this->variables = array('_' => '');
   }
   
   // die "Shell" starten
   public function start() {
      $this->init();
      while(true) {
	 echo 'calcoid:> ';
	 $line = trim(fgets(STDIN));
	 $args = explode(' ', $line);
	 switch($args[0]) {
	    case ':quit':
	       exit;
	    case ':vars':
	       $this->printVars();
	       break;
	    case '_':
	       echo $this->getVar('_') . "\n";
	       break;
	    case ':clear':
	       $this->init();
	       echo "OK\n";
	       break;
	    case $args[1] == ':=':
	       $name = array_shift($args);
	       $this->setVar($name, array_slice($args, 1));
	       echo $this->getVar($name) . "\n";
	       break;
	    default:
	       $parser = new TermParser($args);
	       $output = $parser->output();
	       $this->variables['_'] = $parser->calculate();
	       if(!is_numeric($output)) {
		  echo $output . ' = ' . $this->getVar('_') . "\n";
	       } else {
		  echo $output;
	       }
	 }
      }
   }
}

// Umgebung instanziieren und starten
$calculator = Environment::getInstance();
$calculator->start();

// benutzerdefinierte Funktionen
// ziemlich "quick&dirty", zu jeder Funktion muss 
// zusätzlich die Stelligkeit notiert werden, da das die
// Klassen- und Objektfunktionen leider nicht hergeben.
class F {

   static public function getArity($name) {
      $arity = array('potenz' => 2,
		     'wurzel' => 1
		     );
      return $arity[$name];
   }
   static public function potenz($x, $y) {
      return pow($x, $y);
   }
   static public function wurzel($x) {
      return sqrt($x);
   }
}
?>

PS.: Leider gehen die Einrückungen teilweise flöten. Ich hoffe, es ist trotzdem lesbar.
 
Zuletzt bearbeitet:
Hi,

das ist eine CLI-Version. Einfach kopieren, die Shebang-Zeile evtl. anpassen, dann kannst Du das in der Shell aufrufen. ;)
Du musst halt PHP installiert haben, aber das ist bei Perl, Python,... ja auch nicht anders.

LG
 
Hi kuddeldaddeldu!

Unglaublich, daß das die gleiche Sprache sein soll, die ich benutzt habe :eek: ;)

Sieht sehr spannend aus; wenn ich mal Zeit habe, muß ich mich dringen in Klassen (also Objektorientiertes Programmieren?) mit Php reinarbeiten und dann werde ich auf dieses / Dein Beispiel zurückkommen ;) :)
Vielen Dank für diesen Einblick! :)

Liebe Grüße,
Mark.
 
Hi Mark,

Unglaublich, daß das die gleiche Sprache sein soll, die ich benutzt habe :eek: ;)

Na ja, prozedural und objektorientiert ist halt immer so ein Unterschied, egal in welcher Sprache. Aber ob das jetzt gerade das Musterbeispiel an OOP war, ich weiß nicht... :-( :D

Sieht sehr spannend aus; wenn ich mal Zeit habe, muß ich mich dringen in Klassen (also Objektorientiertes Programmieren?) mit Php reinarbeiten und dann werde ich auf dieses / Dein Beispiel zurückkommen ;) :)
Vielen Dank für diesen Einblick! :)

Wenn Du Fragen hast, immer raus damit. :)

LG

PS.: Bei mir wird übrigens der Ausdruck auch nicht auf Gültigkeit geprüft... ;)
 
Zurück