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...
PS.: Leider gehen die Einrückungen teilweise flöten. Ich hoffe, es ist trotzdem lesbar.
- 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: