Technik, Gothic und Anderes

Technik ist Spiel, Gothic ist ernst und Zeit hat man zuviel

  • Kategorien

  • Tags

  • Archiv

  • Links

    zu Bee5

    blog.oncode.info läuft bei Cyon und ich bin sehr glücklich damit.

Archiv

Eine eigene Programmiersprache erschaffen? Lexer und Parser in PHP!

Geschrieben von skaldrom am 25. October 2007

Ich will Gott sein!

Paper SnippetsWer träumt nicht davon: Eine eigene Programmiersprache, denn der Syntax und das Wort sind Ausdruck! Einen Schritt weiter gehen und vom Programmiersprachenanwender zum Programmiersprachenerschaffer aufsteigen! Auch dies ist nun möglich in PHP, doch leider nur mit minimaler Dokumentation.

Ich gehe davon aus, dass die Leserin/Der Leser Grundlagen im Compilerbau besitzt. Ansonsten müsste man sich diese noch aneignen. Compilerbau ist rekursiv und somit göttlich! *schwärm*.

Das Beispiel

Ich möchte einen Interpreter bauen für normale, arithmetische Ausdrücke wie 9*(8+6)-2/(-4). Klar könnte man diesen einfach durch eval hauen, aber erstens macht das nicht so viel Spass und zweitens ist dieses Beispiel sowas wie das Hello World des Compilerbaus.

Als BNF habe ich folgendes vor:

<Expression> ::= '+' <Expression> // Unäres + (Vorzeichen)
<Expression> ::= '-' <Expression> // Unäres - (Vorzeichen)
<Expression> ::= <Term>
<Expression> ::= <Expression> '+' <Term>
<Expression> ::= <Expression> '-' <Term>
<Term> ::=  <Factor>
<Term> ::= <Term> '*' <Factor>
<Term> ::= <Term> '/' <Factor>
<Factor> ::=  NUMBER // Ok, hier hab ich abgekürzt ;) Soll der Lexer erledigen...
<Factor> ::= '(' <Expression> ')'

Ein Problem stellen die unären Plus- und Minusoperatoren dar. Sie besitzen das gleiche Symbol, nicht aber die gleiche Priorität wie ihre binären Kollegen. Lemon erlaubt keine änderung der Priorität nach Regeln und so funzen jetzt halt auch Konstrukte wie —+++—+1…

Die Werkzeuge

Ich verwende PHP_LexerGenerator, der kompatibel zum re2c Format ist und den PHP_ParserGenerator der in grossen Teilen dem LEMON Parser Generator entspricht. Heruntergeladen werden können sie mittels folgenden Zeilen, die allerdings unter Umständen noch eine Anpassungen der Versionsnummer brauchen:

pear install channel://pear.php.net/PHP_ParserGenerator-0.1.5
pear install channel://pear.php.net/PHP_LexerGenerator-0.3.4

Als einziges, weiteres Beispiel für diese Libs ausserhalb der Doku habe ich nur dieses Drupalmodul gefunden.

Die Realisierung

Der Lexer

Für den Lexer muss eine plex Datei erstellt werden. Sie beinhaltet den Rumpf des Lexerobjekts und die lexikalischen Regeln:

TermLexer.plex

<?php
class TermLexer
{
    private $data;
    public $counter;
    public $token;
    public $value;
    public $node;
    public $line;
    private $state = 1;

    function __construct($data)
    {
        $this->data = $data;
        $this->counter = 0;
        $this->line = 1;
    }

/*!lex2php
%input $this->data
%counter $this->counter
%token $this->token
%value $this->value
%line $this->line
number = /[0-9]+(\.[0-9]+)?/
multiplication = /\*/

division = @/@
plus = /\+/
minus = /\-/
openP = /\(/
closeP = /\)/
other = /./
*/
/*!lex2php
%statename START
multiplication {
  $this->token = TermParser::TP_MULTIPLICATION;
  //echo "multiplication: ".$this->value."\n";
}
division {
  $this->token = TermParser::TP_DIVISION;
  //echo "division: ".$this->value."\n";
}
plus{
  $this->token = TermParser::TP_PLUS;
  //echo "plus: ".$this->value."\n";
}
minus{
  $this->token = TermParser::TP_MINUS;
  //echo "plus: ".$this->value."\n";
}
openP {
  $this->token = TermParser::TP_OPENP;
  //echo "openP: ".$this->value."\n";
}
closeP {
  $this->token = TermParser::TP_CLOSEP;
  //echo "closeP: ".$this->value."\n";
}
number {
  $this->token = TermParser::TP_NUMBER;
  //echo "number: ".$this->value."\n";
}
other {
  return false;
}
*/

}

So richtig laufen tuts aber noch nicht, da er Konstanten des Parsers benötigt. Die stehen erst zur Verfügung wenn wir den Parser auch gebaut haben.

Der Parser

Der Parser wird vom Lexer mit Tokens gefüttert. Die Notation im Lemon-Style unterscheidet sich von Lex und Yacc, ist aber durchaus logisch. Hier der ganze Code, mal hingeknallt:

TermParser.y

/* This is an example for a Parser in PHP */
%name TP_
%declare_class {class TermParser}
%include_class
{
    // states whether the parse was successful or not
    public $successful = true;
    public $retvalue = 0;
    private $lex;
    private $internalError = false;

    function __construct($lex) {
        $this->lex = $lex;
    }
}

%token_prefix TP_

%parse_accept
{
    $this->successful = !$this->internalError;
    $this->internalError = false;
    $this->retvalue = $this->_retvalue;
    echo "WORKED!!\n\n";
}

%syntax_error
{
    $this->internalError = true;
    echo "Syntax Error on line " . $this->lex->line . ": token '" .
        $this->lex->value . "' count ".$this->lex->counter." while parsing rule: ";
    foreach ($this->yystack as $entry) {
        echo $this->tokenName($entry->major) . '->';
    }
    foreach ($this->yy_get_expected_tokens($yymajor) as $token) {
        $expect[] = self::$yyTokenName[$token];
    }
    echo "\n"; 
    throw new Exception('Unexpected ' . $this->tokenName($yymajor) . '(' . $TOKEN. '), expected one of: ' . implode(',', $expect));
}

%left PLUS MINUS.
%left MULTIPLICATION DIVISION.

start(res)       ::= expression(expr). { res = expr; }

/* Unary minus or plus */
expression(res)  ::= PLUS expression(e). { res = +e; }
expression(res)  ::= MINUS expression(e). { res = -e; }

/* The common stuff */
expression(res)  ::= term(t). { res = t; }
expression(res)  ::= expression(e1) PLUS term(t2). { res = e1+t2; }
expression(res)  ::= expression(e1) MINUS term(t2). { res = e1-t2; }

term(res)        ::= factor(f). { res = f; }
term(res)        ::= term(t1) MULTIPLICATION factor(f2). { res = t1*f2; }
term(res)        ::= term(t1) DIVISION factor(f2). { res = t1/f2; }

factor(res)      ::= NUMBER(n). { res = n; }
factor(res)      ::= OPENP expression(e) CLOSEP. { res = e; }

Generieren der eigentlichen Parser und Lexer

Damit Parser und Lexer generiert werden und Terme gefüttert werden können, sei hier ein kleines Beispiel gegeben:

<?php
///////
// Parser and lexer have to be created only once! They create two files you can use later
//////
// Create Parser
passthru('php /usr/share/php/PHP/ParserGenerator/cli.php TermParser.y');

// Create Lexer
require_once 'PHP/LexerGenerator.php';
$lex = new PHP_LexerGenerator('TermLexer.plex');

# Test
// These are the created files
include_once("TermParser.php");
include_once("TermLexer.php");

$teststr='9*(8+6)-2/(-4)';

$lex = new TermLexer($teststr);
// $lex = new TermLexer(file_get_contents($lexerfile));

echo "-------------------------------\n";
echo " Parsing $teststr\n";
echo "-------------------------------\n";
$parser = new TermParser($lex);
while ($lex->yylex())  {
    echo "Parsing  {$lex->token} Token {$lex->value} \n";
    $parser->doParse($lex->token, $lex->value);
}
$parser->doParse(0, 0);

print "Returnvalue: ".$parser->retvalue."\n";
?>

Und nu?

Nun viel Spass… Ich bin auf der Suche nach der EBNF für RegExps, wenn die jemand hat :) …. Ihr könnt den ganzen Code hier auch downloaden…

Nachtrag September 2008

Um es klarer zu beschreiben: PHP_LexerGenerator und PHP_ParserGenerator werden nur zum erzeugen des Lexers und des Parsers gebraucht. Es werden zwei php-Dateien generiert, die ganz normal verwendet werden können.

Eingeordnet in Theorie und Schnipsel | 19 Komentare »

Moodle Block Resource-Download

Geschrieben von skaldrom am 22. October 2007

Der Moodle Resource Download Block

Moodle Resource Download Block Auf vielfachen Schülerwunsch hin habe ich einen Block für unser Learning Management System Moodle codiert: Den Resource Download Block. Er erlaubt den Download aller Kursdateien und Verzeichnisse in einem ZIP-Archiv. Dieses Zip-Archiv wird wie im Beitrag Zipdateien on-the-fly erstellen mit PHP dynamisch erstellt, da (in der Theorie) jeder Lernende eine andere Kursansicht haben kann.

Einen Block erstellen ist relativ einfach, wenn man dem Block Howto folgt, aber der Teufel liegt wie immer im Detail.

Configwerte

Jeder Block kann verschiedene Konfigurationswerte erfragen und erhalten. Diese unterscheiden sich aber, ob der Block global (pinned, sticky) ist, in welchem er nur von einem Ort aus konfiguruiert wird oder ob er den individuellen Kursen hinzugefügt wurde, wobei dann jede Instanz ihre eigene Konfiguration hat. Ich habe mich für den zweiten Weg entschieden.

Um die Konfigurationsvariablen mit einem Defaultwert zu versehen, muss man sich in der instance_config_print() Methode darum kümmern:

if (!isset ($this->config)) {
    // ... teacher has not yet configured the block, let's put some default values here to explain things
    $this->config->exclusionregexp= block_downloader_default_exclusionregexp();
    $this->config->compression= block_downloader_default_compression();
    $this->config->maxsize= block_downloader_default_maxsize();
}

Die Datei, die das Zip zusammenstellt ist mehr oder weniger ausserhalb von Moodle, da sie nicht “als Block” erscheinen kann. Um da an die Konfigurationsdaten zu kommen, muss man etwas mehr Aufwand treiben. Sie befinden sich base64 codiert in den Tabellen block_instance respektive blocks_pinned für globale Blocks. courseid und instanceid werden dabei vom Link im Block übergeben.

<?php

/**
 * Create the zip on the fly and push it to the browser.
 */


require_once ('../../config.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/archive.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/downloadlib.php');
require_once ($CFG->dirroot . '/lib/filelib.php');
require_once ($CFG->dirroot . '/lib/moodlelib.php');
require_once ($CFG->dirroot . '/blocks/downloader/lib/downloadlib.php');

$courseid= required_param('courseid', PARAM_INT); // Course identification
$instanceid= required_param('instanceid', PARAM_INT); // Instance of the block

// Securitycheck
[SNIP]

// Get Config Data
$where= "pagetype = 'course-view' AND visible = 1 AND id=" . addslashes($instanceid);

// Instance?
$blockarr= get_records_select('block_instance', $where . " AND pageid=" . addslashes($courseid));

// Maybe pinned? In this case we have no courseid
if (!$blockarr) {
    $blockarr= get_records_select('block_pinned', $where);
}

if (!$blockarr) {
    error("cannot find block with: " . $where . " AND pageid=" . addslashes($courseid));
    exit ();
}

// Take the first result
$block= array_pop($blockarr);

$configdata= unserialize(base64_decode($block->configdata));

print_r($configdata);

[...]

Dateien pro Kurs abholen

Das Zusammenstellen aller Pfade für Dateien, die ein Benutzer sieht und auf die er Zugriff hat ist echt mühsam. Ich wünsche mir hier ein Bisschen den Servicegedanken: Beispielsweise würde man viel Zeit sparen, wenn der Entwickler mit dem entsprechenden Wissen Funktionen wie can_read($userid, $resourceid) implementieren würde. Zusätzlich wäre man unabhängig vom verwendeten Absicherungsschema. Ich werde das zukünftig in meinen Programmen berücksichtigen.

Reaktionen

Die Community hat – wie meistens bei Moodle – sehr nett auf die Veröffentlichung reagiert. Zwei Tage nach Version 1.0.0 habe ich schon eine Slovakische Übersetzung und diverse Hinweise auf Bugs erhalten.

Eingeordnet in Theorie und Schnipsel, Web | Keine Kommentare »

Zipdateien on-the-fly erstellen mit PHP

Geschrieben von skaldrom am 17. October 2007

Das Problem

On-The-Fly An unserer Schule wird Moodle als LMS (Learning Management System) verwendet. Es eignet sich sehr gut um Unterrichtsunterlagen wie Skripts, Aufträge und Präsentationen zu publizieren und den Lernenden auf eine angenehme Art und Weise zur Verfügung zu stellen. In einigen Kursen gibt es sehr viele dieser Ressourcen (Dateien und Verzeichnisse) zum Download und alleine schon das Anklicken und Speichern nimmt einen grossen Teil des zur Verfügung stehenden Zeitbudgets ein.

Einzelne Ressourcen können von der Lehrperson unsichtbar gemacht werden und nicht alle Dateien eines Kurses stehen zum Download bereit. Die Ansicht ist also sehr individuell und kann sich schnell ändern. Die Dateien werden über eine Versionsverwaltung in das System eingespiesen und können sich so ausserhalb des Systems verändern. Leider verunmöglicht dies die offensichtliche Lösung, einfach ein statisches Zip-Archiv zum Download zur Verfügung zu stellen.

Screenshot Moodle

Die Lösung

Das Zip muss also dynamisch (im Speicher) generiert werden bei einem Aufruf. Fündig wurde ich bei der Klasse archive bei phpclasses.org. Neben Zip-Archiven kann sie auch tar’s, gzip’s und bzip2′s erzeugen. Ich habe einige, kleine Erweiterungen vorgenommen, die noch beschrieben werden.

Die Handhabung ist sehr einfach:

// Archivklasse einbinden:
require_once ('lib/archive.php');

// Objekt erzeugen. Das Argument bezeichnet den Dateinamen
$zipfile= new zip_file('Meine Zipdatei.zip');

// Die Optionen
$zipfile->set_options(array (
        'basedir' => "/home/me/toZip/", // Das Basisverzeichnis. Sonst wird der ganze Pfad von / an im Zip gespeichert.
        'followlinks' => 1, // Symlinks sollen berücksichtigt werden
        'inmemory' => 1, // Die Datei nur im Speicher erstellen
        'level' => 6, // Level 1 = schnell, Level 9 = gut
        'recurse' => 1, // In Unterverzeichnisse wechseln
        // Wenn zu grosse dateien verarbeitet werden, kannes zu einem php memory error kommen
        // Man sollte nicht über das halbe memory_limit (php.ini) hinausgehen
        'maxsize' => 12*1024*1024 // Nur Dateien die <= 12 MB gross sind zippen
));

// Alle Dateien im Verzeichnis /home/me/toZip/Stuff hinzufügen
// Alle ".doc" Dateien und alle Ordner im Verzeichnis /home/me/toZip/Letters hinzufügen
$zipfile->add_files(array("Stuff", "Letters/*.doc"));

// Alle ".tmp" dateien in Stuff ausschliessen
$zipfile->exclude_files("htdocs/*.tmp");

// Alle Dateien in ".svn" und "CVS" Verzeichnissen ausschliessen (Regular Expressions)
$zipfile->exclude_regexp_files('.*/CVS|.*/CVS/.*|.*/\.svn|.*/\.svn.*');

// Archiv erstellen
$zipfile->create_archive();

// Archiv zum Download anbieten
$zipfile->download_file();

Änderungen

Ich habe folgende Dinge gegenüber der original archive.php geändert:

  • Mittels exclude_regexp_files() können Dateien auf Grund von Regular Expressions ausgeschlossen werden.
  • Die maxsize Option erlaubt das Ausschliessen von Dateien die grösser sind als die angegebene Zahl in Byte.
  • Verzeichnisse unter Linux werden als Verzeichnisse behandelt (Bug?)
  • exclude_file() prüft, ob eine Datei ausgeschlossen würde

Meine Version kann natürlich auch frei heruntergeladen werden.

Eingeordnet in Theorie und Schnipsel | 11 Komentare »

Schere, Stein, Papier: Bewertungen im Kreis

Geschrieben von skaldrom am 27. September 2007

Round n RoundDie Regeln von Schere, Stein, Papier sind weitherum bekannt. Zwei Mitspieler zeigen gleichzeitig ein Symbol, danach wird ausgewertet: Schere wird von Stein geschlagen → Stein wird von Papier geschlagen → Papier wird von Schere geschlagen. Die Gewinne sind im Kreis herum (zirkulär wie der geneigte Klugscheisser sagen würde). Diese Kreisbewertung gibt es oft, auch wenn mir jetzt überhaupt kein Beispiel dazu einfällt :-D .

Nehmen wir an, wir müssten die Auswertung programmieren und bestimmen, wer gewonnen hat. Die Symbole seien Konstanten und auf Ziffern gemappt:

define("SCHERE", 0);
define("STEIN", 1);
define("PAPIER", 2);

Insgesamt gibt es 9 Fälle (ich kann 3 Symbole zeigen, mein Mitspieler kann auf jedes mit 3 anderen Symbolen reagieren: 3 mal 3 macht 9). Der Programmierer, der nach SLOCs bezahlt wird würde also 9 ifs schreiben.

Mein Ziel ist es aber, nur 3 ifs zu benötigen für die 3 Resultate: Spieler 0 gewinnt, Spieler 1 gewinnt und unentschieden.

Die erste Reduktion ist trivial: Wenn beide dasselbe Symbol zeigen, dann ist unentschieden. Damit sind wir auf 7 ifs.

Weiter hilft eine Faustregel des alten Informatikers: Wenn etwas im Kreis herum geht, ist fast immer ein Modulo beteiligt.

Die Grundüberlegung: Wenn der Spieler 0 um 1 darunter liegt (beispielsweise STEIN=1 unter PAPIER=2) dann hat Spieler 1 gewonnen. Und nun muss das ganze halt noch im Kreis herum: PAPIER liegt 1 unter SCHERE.

Wenn nicht unentschieden ist, und auch Spieler 1 nicht gewonnen hat, ja dann, hmm, hat wohl Spieler 0 gewonnen!

Code!

define("SCHERE", 0);
define("STEIN", 1);
define("PAPIER", 2);

[...]
    if($symbolFromPlayer0==$symbolFromPlayer1) {
        // Unentschieden:
    } elseif( ($symbolFromPlayer0+1)%3 == $symbolFromPlayer1) {
        // Player1 hat gewonnen
    } else {
        // Player0 hat gewonnen
    }
[...]

Jou, juhu, hat geklappt! Wir haben unsere 3 ifs…

Eingeordnet in Theorie und Schnipsel | 2 Komentare »

Observer Pattern in PHP mit der SPL

Geschrieben von skaldrom am 12. September 2007

Das Observer Pattern

Observed!Normalerweise läuft es mir kalt den Rücken runter wenn ich das Wort Pattern höre. Ich will keine Fassaden, Dekoratöre und Factories in meinen Applikationen (ausser für Anwendungen in der entsprechenden Problemdomäne). An Patterns lastet der Geruch von CRC-Karten, Pair-Programming, millionenzeiliger, selbstgeschriebener Frameworks die Dinge vereinfachen sollen die man nicht begriffen hat und anderen Dingen an, die ich nicht so mag :-) .
Eine Ausnahme ist das Observer-Pattern (was hat es wohl angestellt, dass es auch Pattern geschimpft wird?). Grundsätzlich geht es darum, dass eine Klasse für Daten oder Ereignisse zuständig ist (das Subject). Andere Klassen können sich als Beobachter (Observer) anmelden und werden bei Statusänderungen informiert. MVC (Model-View-Controller) macht rege davon Gebrauch, indem das Model die Daten verwaltet (Subject) und Views (Observer) die Daten darstellen. Eine View könnte beispielsweise für eine Baumansicht verantwortlich sein, während sich eine andere View um eine listenartige Ansicht kümmert. Ändern sich nun die Daten, so werden beide benachrichtigt und können ihre Ausgaben anpassen. So ist es einfach, neue Klassen hinzuzufügen die auf Statusänderungen reagieren können müssen.

Das Observerpattern mit SPL

SPL ist die Standard PHP Library die ab PHP 5.0 dabei ist, stetig weiterentwickelt wird und ohne zusätzlichen Aufwand/Code verwendet werden kann. Sie erlaubt ein paar ganz nette Gags, aber das soll hier nicht Thema sein.

Ohne SplObjectStorage

Leider sind Beispiele für die PHP Implementierung etwas knapp gesät. Das PHP-Wiki ist offline und auch sonst gibt es neben PHP Avancado nur wenig Code. Die offizielle SPL-Seite hat zwar viel Doku aber leider auch keine Beispielimplementierung.

Darum hier etwas Code. Die Klasse Ticker generiert jede Sekunde einen Tick. Einige Observer beobachten sie und reagieren darauf.

Die erste Version arbeitet ohne SplObjectStorage, damit das Prinzip klar wird. Man muss sich somit selber um die Speicherung der Observer kümmern.

<?php
/**
 * Observer pattern without SplObjectStorage
 *
 * Little example because there are not much of them on the net.
 * It implements a Model-View relationship.
 * Taken from <a href="http://blog.oncode.info">Technik, Gothic und Anderes</a>.
 * Needs at least PHP 5.1
 *  
 * @see http://www.php.net/~helly/php/ext/spl/interfaceSplObserver.html
 * @see http://phpavancado.net/comment/reply/410
 * @see http://wiki.cc/php/?title=SplObserver
 * @author Skaldrom Y. Sarg
 */


/**
 * The Class we are observing
 *
 * This class generates a tick every second.
 */

class Ticker implements SplSubject {
    protected $numTicks= 0; // Stores the ticks
    protected $observers= array (); // Stores all observers

    /**
     * Needed to register observers
     *
     * Note the typehinting here.
     */

    public function attach(SplObserver $observer) {
        $this->observers[]= $observer;
    }

    /**
     * Needed to unregister observers
     *
     * Note the typehinting here.
     */

    public function detach(SplObserver $observer) {
        // Not implemented here
    }

    /**
     * Notify all Observers that we have changed
     */

    public function notify() {
        foreach ($this->observers as $obj) {
            $obj->update($this);
        }
    }

    /**
      * Get the number of ticks
      */

    public function getNumTicks() {
        return $this->numTicks;
    }

    /**
     * The ticker itslef
     */

    public function doTicks() {
        while ($this->numTicks < 10) {
            sleep(1); // Sleep for one second
            $this->numTicks++;
            echo "\nTicker: Added a Tick (" . $this->numTicks . ")\n";
            $this->notify(); // Tell all observers we have changed         
        }
    }
}

/**
 * Observer that counts the ticks
 */

class Observer1 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 1: Wow, a Tick!!! We are at tick number " . $subject->getNumTicks() . "\n";
    }
}

/**
 * Observer that calculates the number of ticks until 10
 */

class Observer2 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 2: TickTick, there are " . (10 - $subject->getNumTicks()) . " ticks left until 10\n";
    }
}

$ticker= new Ticker(); // The subject to be observed

$observer1_1= new Observer1(); // First observer of type 1
$ticker->attach($observer1_1);

$observer1_2= new Observer1(); // Another observer of type 1
$ticker->attach($observer1_2);

$observer2= new Observer2(); // Observer of type 2
$ticker->attach($observer2);

// Start the run
$ticker->doTicks();
?>

Und die Ausgabe (im Sekundentakt):

Ticker: Added a Tick (1)
Observer 1: Wow, a Tick!!! We are at tick number 1
Observer 1: Wow, a Tick!!! We are at tick number 1
Observer 2: TickTick, there are 9 ticks left until 10

Ticker: Added a Tick (2)
Observer 1: Wow, a Tick!!! We are at tick number 2
Observer 1: Wow, a Tick!!! We are at tick number 2
Observer 2: TickTick, there are 8 ticks left until 10
[...]

SplObjectStorage

SplObjectStorage nimmt uns einiges ab. Er implementiert aber das Interface SplSubject nicht. SplObjectStorage hat eine Implementierung von attach() und detach() und SplSubject auch, aber eine andere :-( . Nun muss man halt zusammenleimen, sonst wärs noch kürzer…

<?php
/**
 * Observer pattern with SplObjectStorage
 *
 * Little example because there are not much of them on the net.
 * It implements a Model-View relationship with the help of the SPL object storage class.
 * Taken from <a href="http://blog.oncode.info">Technik, Gothic und Anderes</a>.
 * Needs at least PHP 5.1
 *  
 * @see http://www.php.net/~helly/php/ext/spl/classSplObjectStorage.html
 * @see http://wiki.cc/php/?title=SplObjectStorage
 * @author Skaldrom Y. Sarg
 */


/**
 * The Class we are observing
 *
 * This class generates a tick every second.
 */

class Ticker extends SplObjectStorage implements SplSubject {
    protected $numTicks= 0; // Stores the ticks

    /**
     * Needed to register observers
     *  
     * This method glues plObjectStorage::attach() and SplSubject::attach(). Otherwise you will get something like:
     *  "Fatal error: Declaration of SplObjectStorage::attach() must be compatible with that of SplSubject::attach()"
     */

    public function attach(SplObserver $observer) {
        // Duplicate-Checking is done in SplObjectStorage::attach()
        parent :: attach($observer);
    }

    /**
     * Needed to unregister observers
     *  
     * This method glues plObjectStorage::detach() and SplSubject::detach(). Otherwise you will get something like:
     *  "Fatal error: Declaration of SplObjectStorage::detach() must be compatible with that of SplSubject::dettach()"
     */

    public function detach(SplObserver $observer) {
        // Duplicate-Checking is done in SplObjectStorage::attach()
        parent :: detach($observer);
    }

    /**
     * Notify all our observers
     *
     * The observers are stored in the SplObjectStorage and this class implements an iterator.
     */

    public function notify() {
        foreach ($this as $observers) {
            $observers->update($this);
        }
    }

    /**
      * Get the number of ticks
      */

    public function getNumTicks() {
        return $this->numTicks;
    }

    /**
     * The ticker itslef
     */

    public function doTicks() {
        while ($this->numTicks < 10) {
            sleep(1); // Sleep for one second
            $this->numTicks++;
            echo "\nTicker: Added a Tick (" . $this->numTicks . ")\n";
            $this->notify(); // Tell all observers we have changed         
        }
    }
}

/**
 * Observer that counts the ticks
 */

class Observer1 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 1: Wow, a Tick!!! We are at tick number " . $subject->getNumTicks() . "\n";
    }
}

/**
 * Observer that calculates the number of ticks until 10
 */

class Observer2 implements SplObserver {
    public function update(SplSubject $subject) {
        echo "Observer 2: TickTick, there are " . (10 - $subject->getNumTicks()) . " ticks left until 10\n";
    }
}

$ticker= new Ticker(); // The subject to be observed

$observer1_1= new Observer1(); // First observer of type 1
$ticker->attach($observer1_1);

$observer1_2= new Observer1(); // Another observer of type 1
$ticker->attach($observer1_2);

$observer2= new Observer2(); // Observer of type 2
$ticker->attach($observer2);

// Start the run
$ticker->doTicks();
?>

Fazit

Man beachte: Hier könnten noch viele beliebige Observer hinzugefügt werden, ohne dass das Subject (Ticker) verändert werden muss.

Eingeordnet in Theorie und Schnipsel | 4 Komentare »

Verschiedenes und Links

Geschrieben von skaldrom am 10. August 2007

Webseitenwertberechnungsunterstützer

Dieses kleine Tool kreiert nach Eingabe der URL Links auf verschiedene Sites mit Kenndaten zum “Wert” einer Site.

URL (mit http://):

Links zu Freunden, Bekannten und Mitstreiterinnen

4t2 Sinn des IT Lebens
Blog eines weiteren, leicht IT-Verrückten…
AD&D Campaign
Beschreibungen der Abenteuer meines alter Egos und dessen Kumpanen (danke Frigidor).
Adrenalinum
Man trifft sich immer mal wieder ;)
Toxic-Noise
Dunkles aus Vergangenheit, Gegenwart und Zukunft.

Eingeordnet in | 5 Komentare »

5 Wege für ProgrammiererInnen die Zeit intelligent zu verdödeln

Geschrieben von skaldrom am 8. August 2007

1. Corewars

Die Corewars ArenaOldie but Goldie! Corewars ist ein Programmierspiel in welchem sich Programme in einem zyklischen Speicher bekämpfen. Diese kleinen Kämpfer sind in einer Assemblerartigen Programmiersprache geschrieben und beherrschen sogar eine Art Multithreading. Bei jedem der Kampfprogramme wird abwechslungsweise ein Befehl ausgeführt. Das Programm das am Schluss noch ausführbar ist hat gewonnen.

Einfachstes Beispiel: der Imp. Er gewinnt zwar nie, aber verliert auch nicht. Alles was er tut ist den Code aus Speicherstelle 0, also sich selber (in Corewars sind alle Speicherangaben relativ zur eigenen Position) eine Speicherstelle weiterkopieren. Als nächstes wird diese kopierte Version ausgeführt und er rast geschwind durch den ganzen Speicher und überschreibt viele, sehr ausgeklügelte aber träge Giganten.

; The imp: Er kopiert sich selbst immer einen Schritt weiter
mov 0, 1

Etwas vielfältiger ist der Dwarf. Er schiesst Steine (unausführbare DAT-Anweisungen) durch den Speicher:

add #4, 3 ; Addiere 4 zur DAT-Anweisung
mov 2, @2 ; Kopiere die DAT-Anweisung an die Speicherstelle auf die sie zeigt
jmp -2    ; Von vorne
dat #0

Grundsätzlich gibt es drei Strategien die sich grundsätzlich zyklisch gegenseitig schlagen:

Papers:
Papers sind Replikatoren. Sie kopieren sich selbst über das Spielfeld und führen ihre Kopien dann aus.
Stone:
Stones schmeissen unausführbare dat Anweisungen über das Spielfeld.
Scissors:
Scannen das Spielfeld und versuchen andere Warriors zu finden und mit sinnlosem Code zu beschäftigen.

Ganz wilde Naturen wenden genetische Algorithmen an um sich Krieger zu züchten! Ganz verrückte Sache!

Die Idee ist schon über 10 Jahre alt und die “Arenen” sind relativ ausgereift, inklusive Debuggingfunktionen. Es gibt sie online als Applet und einige Implementationen wie ARES, Corewin oder den Klassiker pMars (inklusive Quellcode) den es auch für Linux gibt (wenn man nicht selber kompilieren will). Die Sprache (redcode) ist genormt und es gibt Turniere (sogenannte Hills) auf denen ständig Kampfprogramme gegeneinander antreten. Sogar ein Benchmark gibt es.

Eine gute Deutsche Einführung gibt W. Zimmer. Auf Englisch gibt es ganz viele verschiedene (einfach mal googeln oder wikipedien).

2. NetLogo

Die NetLogo Oberfläche

Kennt Ihr noch Logo? Die Sprache mit der Schildkröte (turtle) die man hemmungslos herumkommandieren konnte? NetLogo ist Logo auf Speed, denn mit ihm kann man fast beliebig viele Schildkröten kommandieren. Das ermöglicht sehr interessante Simulationen für verschiedene Fänomene wie Gerüchteausbreitung, Jäger/Beuteverhältnis, Seggregation, Partikelverhalten und besoffene Seemänner :-) . Über NetHub ist NetLogo Client/Server fähig. Eine spassige und äusserst fesselnde Sache.

3. Robocode

Robocode Arena

Bei Robocode geht es darum in Java einen Roboter zu entwickeln. Dabei leitet man von einer Grundklasse ab und lässt die Roboter in einer grafisch schön gestalteten Arena gegeneinander antreten. Ursprünglich wurde das Spiel von IBM entwickelt, baer heute macht eine deutsche und eine englische Community weiter. Robocode ist eine geniale Möglichkeit, Java und objektorientiertes Design zu lernen.

package msc;
import robocode.*;
//import java.awt.Color;

/**
 * StupidZonk - a robot by (your name here)
 */

public class StupidZonk extends Robot
{
        /**
         * run: StupidZonk's default behavior
         */

        public void run() {
                // After trying out your robot, try uncommenting the import at the top,
                // and the next line:
                //setColors(Color.red,Color.blue,Color.green);
                while(true) {
                        // Replace the next 4 lines with any behavior you would like
                        ahead(100);
                        turnGunRight(360);
                        back(100);
                        turnGunRight(360);
                }
        }

        /**
         * onScannedRobot: What to do when you see another robot
         */

        public void onScannedRobot(ScannedRobotEvent e) {
                fire(1);
        }

        /**
         * onHitByBullet: What to do when you're hit by a bullet
         */

        public void onHitByBullet(HitByBulletEvent e) {
                turnLeft(90 - e.getBearing());
        }

}

4. CodeRuler

Das Coderuler-Land

CodeRuler ist ein weiteres IBM Glanzstück: Sie, beziehungsweise Ihr Programm, ist eine Königin oder König und Herrscht über Land, Burgen, Ritter und Untertanen. Sie kämpfen gegen eine oder mehrere böse Königinnen und Könige in feindlichen Schlössern. Ritter können Bauern metzeln und Schlösser einnehmen, Bauern bestellen das Land und bringen Punkte um neue Bauern oder Ritter zu machen und im Schloss werden diese dann produziert.

Technisch bedeutet dies, dass man wieder eine Javaklasse ableitet und bestimmte Methoden überschreibt. Es macht unglaublich Spass und ganz nebenbei lernt man viel über Java.

Hier ein Beispiel eines etwas dementen Rulers:

import com.ibm.ruler.*;
import java.util.Random;

public class MyRuler extends Ruler {
    public String getRulerName() {
        return "Simple Ruler";
    }

    public String getSchoolName() {
        return "IBM developerWorks";
    }
    public void initialize() {
    }
    protected Random rand = new Random();
    public void orderSubjects(int lastMoveTime) {

    IPeasant[] peasants = getPeasants();
    for (int i = 0; i < peasants.length; i++) {
                move(peasants[i], rand.nextInt(8) + 1);
        }
    }  
}

5. Kara

Kara der Käfer

Kara wurde von Swisseduc entwickelt und sieht schnufflig aus. Der “jööö”-Effekt soll aber nicht täuschen, denn beim Marienkäfer handelt es sich um eine ausgewachsene Zustandsmaschine. Mit dieser lassen sich einfachere und Komplexere Aufgaben lösen die im Programm vorgegeben sind. Das GUI ist sehr anwenderfreundlich und ermöglicht schnelle trial-error-Zyklen. Bis zum Schluss kann man die Grenzen von Zustandsmaschinen erfassen und bekommt ein gutes “Gefühl” für die Dinge.

Kara hat viele weitere Projekte: Multikara ermöglicht mehrere Zustandsmaschinen kooperativ zu betreiben, mit LegoKara kann ein richtiger Roboter Kara übernehmen und des Weiteren sind Anbindungen an viele Programmiersprachen vorhanden. Diese ermöglichen dann die Verbindung von der Zustandsmaschine zum Coden.

Zu Kara gibt es auch gute Unterrichtsmaterialien für eine Einführung in die Programmierung.


Buchempfehlung

Ein sehr beeinflussendes und eindrückliches Werk sind: A.K. Dewdney. Computer-Kurzweil und Computer-Kurzweil 2. Diese Bücher sprühen vor Ideen..! Leider gibt es keine Neuauflage und sie sind nur noch gebraucht zu erwerben…

Eingeordnet in Coding, Desktop | 6 Komentare »

Patchwork…

Geschrieben von skaldrom am 9. July 2007

Situation

Man hat ein geniales OSS Produkt installiert und 3, 4, 123 kleinste Änderungen eingefügt um es an die Situation anzupassen oder Fehler zu korrigieren (die man natürlich zurückgemeldet hat). Nun werden ein paar kleine aber hässliche Sicherheitslücken gefunden und eine neue Version publiziert. Die Motivation geht natürlich in den Keller mit der Aussicht alles wieder und wieder reproduzieren zu müssen.

Eine mögliche Lösung: Patches. Patches sind kleinere Codeflicken die sich überraschend intelligent in neuen Code einzufügen wissen.

Erstellen eines Patches mit CVS/SVN

Wenn man alles im SVN/CVS hat ist man fein raus. Neue Version einer Datei nicht einchecken und folgendes tun:

cvs diff -up FILENAME > filename.patch

bzw.

svn diff -up FILENAME > filename.patch

Will man Änderungen in mehreren Dateien rekursiv verarbeiten, so geht das mit SVN/CVS so:

cvs diff -Rup DIRECTORY > filename.patch

bzw.

svn diff -Rup DIRECTORY > filename.patch

Erstellen eines Patches ohne CVS/SVN

Ohne SVN und CVS muss man die Originaldatei zuerst wegkopieren und folgendes tun:

diff -up ORIGINAL NEW > filename.patch

Bei ganzen Verzeichnissen muss man ebenfalls die unveränderte Version zuerst wegkopieren. Nach der Änderung in das Grundverzeichnis der Arbeitsversion wechseln und dann folgendes eingeben:

diff -urpN ORGINAL_DIRECTORY NEUES_DIRECTORY > filename.patch

-N fügt auch neu erstellte Dateien hinzu.

In Praktisch bedeutet dies zum Beispiel:

diff -urpN ../webapplication/. . > webapplicationbooster.patch

Patchen der Quellen

Das ist einfach. Im richtigen Verzeichnis:

patch -p0 < filename.patch

Hinweise

  • Die Option -B beim Erzeugen des Patches ignoriert leere Zeilen
  • -u8 fügt mehr Kontext hinzu: Für eher paranoid gestimmte Naturen…

Eingeordnet in Theorie und Schnipsel | Keine Kommentare »

Emailadressen auf Webseiten codieren

Geschrieben von skaldrom am 5. April 2007

Spammer brauchen Emailadressen. Neue Emailadressen bekommen sie, indem sie (wie Google) mit sog. Robots von Website zu Website springen, den Inhalt herunterladen und die Emailadressen herauspfriemeln. Das heisst, man sollte seine Emailadressen im WWW verstecken, so gut dass es geht. Eine Möglichkeit ist die Benutzung von JavaScript (weil das die Addressharvester anscheinend noch nicht so beherrschen), eine Andere ist die Verwendung von Bildern (es gibt natürlich noch mehr).

Ich habe dazu ein kleines PHP Skriptchen geschrieben. Es kann eine Emailadresse alleine oder viele Emailaddressen in Text eingebettet verstecken. Im Zip hat es 3 Dateien:

  • hidemail.php: Das Hauptscript, das die Arbeit erledigt.
  • imagemke.php: Ein Script, das aus den Mailadressen Bilder erzeugt wenn das Javascript ausgeschaltet ist.
  • hmexample.php: Ein Beispiel, das die Anwendung zeigt. Es gibt auch eine online Demo.

Ich würde mich über Feedback freuen :) …. Download: hidemail PHP Skript um Mails zu hiden. Zum Gebrauch müssen einige Einstellungen (beispielsweise die URL zu imagemke.php in hidemail.php gemacht werden.

Ideen sind von:


Nachtrag Juli 2008:
Einen Interessanten Beitrag dazu gibt es beim Tillate Techblog!

Eingeordnet in Webapplikationen | 3 Komentare »

Eclipse und die Umlaute

Geschrieben von skaldrom am 3. April 2007

Ganz einfach, schlicht und ergreifend, und kann einem Stunden sparen: Eclipse unter Linux importiert keine Projekte die einen Umlaut im Pfadnamen haben. GNARRRGH!

Eingeordnet in Coding, Linux | Keine Kommentare »