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

kuddeldaddeldu

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

Mark

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

kuddeldaddeldu

Erfahrenes Mitglied
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... ;)
 

Neue Beiträge