Angezapft und ausgeblutet

vom 4. November 2008

Ich war heute das erste mal Blut spenden. Einen halben Liter habe ich mir mit Gewalt aus dem Körper gepumpt, hätte nicht gedacht, dass das so schnell geht. Bea hatte auch ihr Blut fließen lassen.

War eigentlich kein großes Ding, hab mir das irgend wie spektakulärer vorgestellt. Lob an das DRK, die Ärzte und Helfer waren alle sehr zuvorkommend, kompetent und Nett.

7 Kommentare, delicious bookmark del.icio.us


Kompetenzzentrale

vom 31. October 2008

Da mich kb genötigt hat (ok, eigentlich hab ichs ihm ja versprochen), ein paar Bilder von meiner "Kompetenzzentrale" zu zeigen: Hier also mein Arbeitszimmer:

Kompetenzzentrale 0

Kompetenzzentrale 1

Kompetenzzentrale 2

Kompetenzzentrale 3

3 Kommentare, delicious bookmark del.icio.us


Analyse des Programmierwettbewerbs

vom 22. September 2008

Zuerst einmal möchte ich mich bei allen bedanken die mitgeamcht haben! Insgesammt habe ich sieben Einsendungen in den unterschiedlichsten Programmiersprachen erhalten, das hat mich sehr gefreut!

Mitgemacht haben (sortiert nach Abgabedatum):
Aaron Müller (Ruby, download)
Klaus Breyer (PHP, download)
Thomas Monninger (Bash, download)
Florian Eitel (Ruby, download)
Wolfgang Kuhl (Java, download)
Stefan Nitz (PHP, download)
Marcel Steinle (C#, download)
Robert Giczewski (Java, download)

(Alle Programme herunterladen)

Die Aufgabe

Die gestellte Aufgabe haben alle erfolgreich gemeistert. Ziel war es, einen kleinen Transformator zu schreiben, der Text in eine strukturierte Form bringt. Dabei gab es verschiedene Ansätze, die meisten verwendeten reguläre Ausdrücke um den Text zu analysieren, aber es gab auch andere interessante Lösungen. (Bei den Codeausschnitten habe ich mich aufs Wesentliche beschränkt und Unnötiges wie das Einlesen der Datei oder die Ausgabe oftmals weggelassen. Wer den kompletten Code sehen will, klickt auf den angegebenen Link)

Auswertung

Klaus

  • Sprache: PHP
  • LOC: 10

Schon nach ein paar Stunden erhielt ich seine Lösung. Er verwendete die preg_split()-Funktion um mit Hilfe eines regulären Ausdrucks die Aufzählungszeichen als Trenner zu identifizieren. So zerlegte er den kompletten Eingabetext in einem Rutsch. In einer Schleife entfernte er dann die Zeilenumbrüche durch Leerzeichen. Zum Testen stellte er gleich noch ein Eingabeformular bereit.

$text = $_POST['text'];
$array = preg_split("/((\-\s)|(\>\s)|(\d\.\s)|(\so\s))+/", $text, -1, PREG_SPLIT_NO_EMPTY);
foreach($array as $key => $value) {
    $array[$key] = trim(str_replace("\n", " ", $value));
}

echo "<p>";
foreach($array as $key => $value)   {
    echo " * " . $value . "<br />";
}
echo "</p>";

Kleine Erklärug zum RegExp-Ausdruck: die \s fangen alles auf, was im Entferntesten nach Leerzeichen aussieht, das \d steht für eine Ziffer. Die runden Klammern markieren Anker um später darauf zugreifen zu können (was hier aber nicht verwendet wurde) oder gruppieren Ausdrücke.

Thomas

  • Sprache: Bash
  • LOC: 23

Auch sehr schnell war Thomas mit seiner Bash-Lösung. Auch hier wurde mit regulären Ausdrücken gearbeitet, um zuerst die Leerzeichen und leere Aufzähungspunkte wegzuschneiden (\d), dann nach den Aufzählungspunkten zu suchen und durch Sternchen zu ersetzen (\g) und zum Schluss noch doppelte Sternchen und Ziffern zu entfernen. Wie es unter Linux üblich ist wurde hier sed verwendet. Als temporärer Zwischenspeicher mussten Dateien herhalten.

if [ $# -eq 1 ]
then
 sed -e '/^[ \t]*$/d' -e 's/\(^[ \x9]*\([->o]\|\([0-9]\+[\.]*\)\)\)/'*'/g' -e 's/^\*\([ ]*\)/'*' /g' -e 's/^[ \x9]\+/''/g' $1 > tmp.tmp
 while read zeile; do
 if [ "${zeile:0:1}" = "*" ]
 then
  if [ -n "$tmp1" ]
  then
   echo "$tmp1" > tmp2.tmp
   tmp1="$zeile"
  else
   tmp1="$zeile"
  fi
 else
   tmp1="$tmp1"" ""$zeile"
 fi
 done < tmp.tmp
 cat tmp2.tmp > ergebnis 
 rm tmp.tmp
 rm tmp2.tmp
else
 echo "Usage: thomas.sh <input-file>"
fi

Wer die Syntax von Bash nicht kennt: Um Teile aus einem String herauszufiltern, kann man "${zeile:0:1}" verwenden (ähnlich wie substring() in anderen Sprachen). Das -n beim if prüft ob der String NICHT leer ist (NonZero).

Florian

  • Sprache: Ruby
  • LOC: 2

Sehr kompakt formuliert und ohne jeglichen extra Ballast: Der Eingabetext wird von der Standardeingabe gelesen, drei reguläre Ausdrücke darauf angewendet und wieder auf der Standardausgabe ausgegeben. Der Ablauf ist ähnlich wie bei Thomas: Im ersten Schritt werden die Aufzählungspunkte durch Sternchen ersetzt und die Leerzeichen entfernt, im zweiten Schritt werden die Zeilenumbrüche behandelt. Das letzte gsub() behandelt den Spezialfall, falls eine Ziffer alleine in einer Zeile steht. Dadurch, dass die Aufzählungspunkte extra definiert sind, lässt sich der Transformator sehr schnell erweitern!

bullets = %w( [[:digit:]]+\.  -  >  o )
puts STDIN.read.gsub(/(^\s*(#{ bullets.join("|") })\s+)/, "* ").gsub(/\n(?!\*)\s*/, " ").gsub(/^\* \s+/, "* ")

Für die, die noch nie mit Ruby gearbeitet haben: Das %w erzeugt ein Array, der Separator hier ist das Leerzeichen. gsub() ist äquivalent zum preg_replace() von PHP.

Wolfgang

  • Sprache: Java
  • LOC: 26

Einen ganz anderen Weg hat Wolfgang mit seiner Java-Lösung gewählt. Ganz ohne reguläre Ausdrücke hat er das Problem mit Zeichenvergleiche gelöst. Von jeder Zeile werden zuerst die Leerzeichen am Anfang und am Ende entfernt. Diese Strings werden dann dahingehend überprüft ob das zweite Zeichen ein Punkt oder ein Leerzeichen ist, also ob es sich um ein Aufzählungspunkt handelt. Wenn das der Fall ist, wird der Rest zusammen mit dem Sternchen an die Ausgabe angehängt. So ist der Code auch für alle anderen Typen von Aufzählungszeichen vorbereitet, doch wenn das Aufzählungszeichen aus zwei Zeichen besteht (z.B. "14." oder "#>") oder ein einzelner Buchstabe am Anfang einer neuen Zeile steht, funktioniert das Programm nicht mehr richtig.

public String parseToWikiString(File file) throws IOException{
  try {
    br = new BufferedReader(new FileReader(file));  
    zeile = br.readLine();
    while (zeile != null) {
      if(!zeile.equals("")){  
        zeile = zeile.trim();
        if(zeile.charAt(1) == '.' || zeile.charAt(1) == ' '){
          output = output + "\r\n * ";
          zeile = zeile.substring(2).trim();
          output = output + zeile;  
        }else
          output = output + zeile ;    
      }
      zeile = br.readLine();
    }
    br.close();
    return output;
  }catch (FileNotFoundException e){
    e.printStackTrace();}
  return "Fehler beim Parsen";
}

Stefan (PHP)

  • Sprache: PHP
  • LOC: 11

Stefan hat es sich mit seiner Lösung einfach gemacht. Er nahm wiederum die preg_split()-Funktion zur Hilfe um die Aufzählungspunkte zu finden und an den Stellen zu trennen. Da der Browser (wenn man denn das PHP-Skript auf einem Webserver ausführt) nur ein Leerzeichen zulässt und Zeilenumbrüche ebenfalls ignoriert, kommt seine Lösung ganz ohne Entfernung dieser aus. Klever gemacht :-)

$keywords = preg_split("/- |[0-9]\.|>| o /", $txt);
for ($x = 1; $x < sizeof($keywords); $x++) {
    echo "* ".$keywords[$x]."
"; }

Marcel

  • Sprache: C#
  • LOC: 28

Auch eine C#-Variante wurde von Marcel eingereicht! Schön dokumentiert und übersichtlich. Der Eingabetext wird zuerst an den bekannten Kommentarzeichen gesplittet, von den einzelnen Ergebniszeilen die Leerzeichen entfernt und mit einem Stern ausgegeben. Leider wurde nicht beachtet, dass die Aufzählungspunkte immer am Anfang stehen müssen. So gibt es schnell Fehler, wenn beispielsweise ein HTML-Tag im Text steht oder der 70. Geburtstag der Großeltern gefeiert wird.

StreamReader sr = new StreamReader(args[0]);
StreamWriter sw = new StreamWriter(args[1]);

Regex split = new Regex("-|  o |> |[1-99].", RegexOptions.Multiline | RegexOptions.Compiled);
Regex whitespace = new Regex(@"\s+|\s+\n|\n|\r\n");
while (!sr.EndOfStream)
    input += Convert.ToString((char)sr.Read());

intermediate = split.Split(input);
for (int i = 1; i < intermediate.Length; i++)
{
    sw.WriteLine("* " + whitespace.Replace(intermediate[i], " ").Trim());
}
sw.Close();

Robert

  • Sprache: Java
  • LOC: 108

Auch diese Java-Lösung kommt ganz ohne reguläre Ausdrücke aus. Hier wird in der Methode identifyBulletPoints() in einer Schleife auf die verschiedenen Vorkommen von Aufzählungszeichen überprüft und diese dann durch Sternchen ersetzt. Warum eine neue myTrim()-Methode implementiert wurde, war mir nicht ganz klar. Die rekursive Methode prepareList() bringt dann die noch unstrukturierte Liste in die richtige Form.

private void identifyBulletPoints(ArrayList<String> tmp){
  ArrayList<String> dummy= new ArrayList<String>();
  for(int i=0; i<tmp.size();i++){
     dummy.add(myTrim(tmp.get(i)));
     next = false;
     for(int n=0;n<pattern.length&!next;n++){
        if(dummy.get(i).indexOf(pattern[n])==0){
           if(pattern[n] == "o ")
              final_version.add(dummy.get(i).replaceFirst(pattern[n], "* "));
           else
              final_version.add(dummy.get(i).replaceFirst(pattern[n], "*"));
           next = true;
        }
     }
     if(!next)
        final_version.add(dummy.get(i));
  } 
}

private ArrayList<String> prepareList(ArrayList<String> a){
  for(int i=0; i<a.size();i++){
     if(final_version.get(i).indexOf("*")!=0){
        if(i>>0){
           final_version.set(i-1, final_version.get(i-1).concat(" "+final_version.get(i)));
           final_version.remove(i);
           prepareList(final_version);
        }
     }
  }
  return final_version;
}

Aaron

  • Sprache: Ruby
  • LOC: 6

Zu guter Letzt, meine Version. Ich habe ebenfalls wie einige andere auch, die Aufzählungspunkte als Trenner für die split()-Funktion verwendet, anschließend die Leerzeichen entfernt und alle Zeilen die nicht mit einem Stern anfangen zur vorherigen hinzugefügt.

input = ""
while line = gets do input += line end
input.split(/^[\s]*[-|\*|#|>|+|o]|[\d]+[.|:]?[\s]*/).each do |line|
    task = line.gsub(/  /, "").gsub(/\n[\s]*/, " ").strip
    puts "* #{task}" unless task == ""
end

Analyse

Ich habe mir überlegt, wie man diese Programme anständig miteinander vergleichen könnte. Da alle Programme das gleiche tun, habe ich mich für einen (zugegeben sehr primitiven) Benchmark entschlossen. Das Ergebnis war beeindruckend!

(Als Testumgebung musste mein AMD Duron 1.6 mit 1GB Ram und Ubuntu Linux 8.04 herhalten)

LOC (Die komplette Laufzeittabelle)

Florians Lösung (Ruby) scheint bereits auf Performance getrimmt zu sein, denn auch bei sehr großen Eingaben läuft das Programm innerhalb einer 10tel Sekunde durch. An der Programmiersprache kann es nicht liegen, da meine Ruby-Version ganze 21 Sekunden für 30 Tausend Zeilen braucht. Auch sehr schnell waren die PHP-Versionen von Stefan und Klaus. Hier glaube ich aber, dass die PCRE-Engine von PHP sehr viel an Zeit gespart hat, da gerade diese Funktionen die Kernpunkte der Sprache darstellen und dementsprechend optimiert wurden. Die Bash-Lösung von Thomas arbeitet sehr stabil, die Ausführungszeit steigt linear zum Input.

Erstaunt hat mich die Laufzeit der Java-Versionen von Robert und Wolfgang, hier gibt es sehr starke Unterschiede. Bei kleineren Inputs sind beide noch gleich auf, doch bei größeren Inputs bricht Wolfgangs Lösung sehr stark ein und braucht bei 30.000 Zeilen fast 17 Minuten. Grund dafür könnte die Bearbeitung auf Character-Ebene sein. Vielleicht weiß hier jemand eine treffendere Erklärung?

Die C#-Version von Marcel braucht auch bei kleineren Inputs schon recht lange. Einen Grund dafür könnte das zeichenweise Einlesen des Inputs sein, oder aber Mono ist schuld daran. (Ich habe die C#-Version mit dem Mono JIT-Compiler 1.2.6 übersetzt)

Wer den Benchmark selbst noch einmal auf seinem Rechner ausführen will, kann das gerne tun. Die komplette "Testumgebung" einfach Entpacken und das Ruby-Skript starten.

LOC

Auch interessant ist die größe des Programmcodes. Florian hat es in zwei Zeilen geschafft, wärend Robert ganze 108 Zeilen benötogt hat. Über Übersichtlichkeit und Design lässt sich hier natürlich streiten, doch mir gefällt der Spruch "Keep simple things simple" sehr gut :-) Natürlich liegt es hauptsächlich an der gewählten Programmiersprache und deren Features, wie viele Zeilen Code man braucht und welche Herangehensweise man wählt.

Bewertung

Hier seit ihr gefordert. Verwendet bitte die Kommentarfunktion am linken Seitenrand, um eure Meinung zum Code abzugeben.

Vielen Dank an alle fürs Mitmachen!

Nachtrag: Klaus und Florian haben sich die Mühe gemacht und den Benchmark bei sich auszuprobieren. Danke dafür! Erstaunlich ist, dass die Ergebnisse auf einem Windows-System etwas anders ausfallen, aber seht selbst:

Klaus

  • CPU: AMD Phenom X4 9950 (4 x 2.6Ghz)
  • RAM: 4GB DDR2 800Mhz
  • OS: Ubuntu 64 Bit Hardy Heron

Testergebnisse, Systemauslastung

Florian

  • CPU: Intel Core 2 Quad CPU - 2.40GHz
  • RAM: 2GB
  • OS: WinXP SP3

Testergebnisse

6 Kommentare, delicious bookmark del.icio.us


Programmierwettbewerb

vom 16. September 2008

Da ja zur Zeit sonst nicht viel auf meinem Blog passiert möchte ich einen kleinen Programmier-Wettbewerb starten. Die Aufgabe ist nicht schwer und fordert ca. eine Stunde Spaß.

Problem

Änderungswünsche vom Kunden erhalte ich meistens als Liste per E-Mail oder Word-Dokument (ich weiß, es ist grausam, aber was will man machen?). Damit ich bei all den Mails und Dateien nicht den Überblick verliere, trage ich jeden Änderungswunsch in mein Wiki ein und hake ihn bei Beendigung ab und schreibe evtl. noch einen Kommentar darunter.

Leider hat mein Wiki eine vordefinierte Syntax für Aufzählungslisten, so dass ich zuerst die Punkte ins Wiki kopiere und dann Punkt für Punkt mühsam in Wiki-Syntax umformatieren muss. Das ist mir aber nun zu blöde und ich will diesen Vorgang automatisieren.

Aufgabe

Die Aufgabe ist nun, einen Transformator zu schreiben, der Aufzählungslisten aller Art entgegennimmt, und daraus Wiki-konforme Aufzählungslisten erzeugt. Zum Testen habe ich eine Beispieldatei erstellt, die transformiert werden soll. (zum Vergleich hier der Output) Der Transformator kann in einer beliebigen Sprache geschrieben werden, die auf den gängisten Betriebssystemen Windows, Linux und OSX interpretiert oder compilliert werden kann. Als Input (Dateiname, InputStream, Pipe, ...) erhält der Transformator die chaotische Auflistung, als Output (auf der Console, im Browser oder in einer GUI) wird die transformierte Wiki-Syntax ausgegeben.

Wer mir bis zum Sonntag (21.09.2008) seine Lösung per E-Mail an mail@aaron-mueller.de schickt, nimmt beim Wettbewerb teil. Alle Einsendungen werde ich hier im Blog präsentieren, die Jury seit ihr selbst. Jeder der mitmacht erhält unzählige Lobpreisungen, Ruhm und Ehre, mehr kann ich euch leider im Moment nicht bieten. :)

Warum soll ich da mitmachen?

Weil es eine gute Übung ist. Ich hatte das Problem anfangs mit ca. 2 Seiten Code gelöst. Nach etwas Überlegen waren es nur noch 4 Zeilen. Zudem ist es spannend zu sehen, wie andere an das Problem herangehen und welche Features der gewählten Programmiersprache beonders nützlich dabei sind.

Bei reger Teilnahme (was mich sehr freuen würde) werde ich öfters solche Aufgaben stellen. Ich wünsche euch viel Erfolg bei der Lösung. Happy hacking!

Update: 7 Einreichungen hab ich schon erhalten. Wer noch mitmachen will, hat bis Sonntag 12:00 Uhr Zeit. Ich freue mich über jede Einsendung!

6 Kommentare, delicious bookmark del.icio.us


AJAX ohne AJAX

vom 4. September 2008

Vorgestern bin ich auf ein Problem gestoßen, an das ich noch nicht gedacht hatte: Es ist nicht möglich, eine Datei mit einem XMLHTTPRequest zu versenden. Grund dafür ist JavaScript, welches in der Browser-Sandbox läuft und nicht auf lokale Dateien zugreifen kann.

Doch wie hat das GMail gelöst? Hier werden Dateianhänge nach dem Auswählen ohne ein Neuladen der Seite hochgeladen. Nach ein paar Experimenten und etwas Hilfe von ajaxpatterns.com fand ich ein paar interessante Browsereigenschaften, mit denen man GET- und POST-Requests auch ohne das XMLHTTPRequest-Interface absetzen kann.

Der iFrame Tag

Die Idee ist Folgende: Wenn per JavaScript ein GET-Request abgesetzt werden soll, wird dynamisch ein iFrame erzeugt und dessen src-Attribut auf eine URL gesetzt, die dann aufgerufen wird. Die GET-Parameter können dann wie gewohnt in Form von ?p1=val1...&pn=valn an die Adresse angehängt werden. Um die Server-Response abzufangen, bekommt das iFrame eine onLoad-Funktion verpasst, die den Inhalt des iFrames extrahiert, weiterverarbeitet und das iFrame wieder löscht. Das iFrame dient hier als eine Art Kontainer zum Senden und Empfangen.

<script type="text/javascript">
function $(id) { return document.getElementById(id); }
function call() {
    var d = document.createElement('div');
    d.innerHTML =
        '<iframe id="iFrame" style="display: none;" ' + 
        'onLoad="callback()"></iframe>';
    document.body.appendChild(d);

    $("iFrame").src = "server.php?val=" + $("var").value;
}
function callback() {
    $("result").innerHTML =
        $("iFrame").contentDocument.body.innerHTML; 
    $('iFrame').parentNode.removeChild($('iFrame'));
}
</script>

<p id="result"></p>
<form onSubmit="call(); return false;">
    <input type="text" id="var" />
    <input type="submit" />
</form>

Mit dieser Technik hat man einen fast vollwertigen Ersatz für das XMLHTTPRequest-Interface. Manche Frameworks nutzen dieses Feature, um auch ältere Browser zu unterstützen.

Doch was tun, wenn der Browser keine iFrames unterstützt? Hier kann man sich einer ähnlichen Technik bedienen: Sobald man einem img-Tag das src-Attribut verpasst, versucht der Browser das Bild zu laden. Hier lässt sich wie im iFrame-Beispiel auch eine URL angeben die zu einem Script führt und Parameter anhängen.

var img = document.createElement('img');
img.setAttribute('src', 'server.php?p1=val1');

Dies ist natürlich ein one-way-ticket. Nach dem GET-Request kann der Server nur noch mit einem Bild antworten.

Und was ist mit POST?

Hier gibt es einen weiteren Trick, POST-Requests an den Server zu senden. Beim dynamischen Erstellen des iFrames fügt man diesem ein Formular hinzu, welches die Parameter als input-Tags beinhaltet. Mit JavaScript sendet man dann das Formular ab und extrahiert wiederum die Server-Response aus dem iFrame.

Einfacher geht es mit dem target-Attribut des form-Tags. Hier kann man ein iFrame als Target angeben. Das iFrame dient hier wiederum als Kontainer für das Abfangen der Antwort vom Server. Im folgenden wird wird die ausgewählte Datei auf den Server geladen:

<script type="text/javascript">
function $(id) { return document.getElementById(id); }
function call(form) {
    var d = document.createElement('div');
    d.innerHTML =
        '<iframe id="iFrame" name="iFrame" ' + 
        'style="display: none;" ' + 
        'onload="callback()"></iframe>';
    d.setAttribute('id', 'repository');
    document.body.appendChild(d);
    return false;
}
function callback() {
    $('result').innerHTML =
        $('iFrame').contentDocument.body.innerHTML;
    $('repository').parentNode.removeChild($('repository'));
}


<p id="result"></p>

<form method="post" enctype="multipart/form-data" 
    action="server.php" target="iFrame"
    onSubmit="call(this);">
    
    <input type="file" name="file" />
    <input type="submit" />
</form>

Auf Serverseite wird die Datei nur noch entgegengenommen und in das richtige Verzeichnis geschoben.

foreach ($_FILES as $f)
   move_uploaded_file($f['tmp_name'], "upload/".$f['name']);

Warum der Umstand?

Eigentlich erstaunlich, dass sich Web-Entwickler immer wieder solche dreckige Hacks einfallen lassen müssen, um ans Ziel zu kommen. Der Browser ist mittlerweile zum "Plattformübergreifenden Betriebssystem" mutiert, doch die Programmierung ist alles andere als komfortabel.

Mit dem neuen Google Browser Chrome kommt hoffentlich wieder etwas frischen Wind in den Browser-Markt. Ich warte derweil geduldig auf eine Linux-Version von Chrome.

4 Kommentare, delicious bookmark del.icio.us


Mein GTD

vom 30. July 2008

Seit knapp 2 Jahren geht der GTD (Getting Things Done) Hype um. Ich laß natürlich die Klassiker wie "The 7 Habits of Highly Effective People", "Getting Things Done" von David Allen und "Getting Real" von 37Signals. Auch hab ich mir die RSS-Feeds von Blogs zu diesem Thema wie zenhabits.net, lifehacker.com, diyplanner.com und dumblittleman.com abboniert. Doch schon am Anfang viel mir auf, dass ich gewisse Dinge schon seit Jahren mache, auch wenn mir dies nie so wirklich bewusst waren. Aber ich habe auch einiges dazugelernt und ein paar Ideen in meinem eigenen "GTD-System" adaptiert.

Da ich des Öfteren gefragt werde, was ich denn so alles in mein Notizbuch schreibe, warum ich einen eeePC oder ein Whiteboard brauche oder wo ich meine Links speichere, will ich hier mal mein privates "StuffToDone"-System vorstellen. Vielleicht findet der ein oder andere ein paar Ideen oder weiß noch ein Verbesserungsvorschlag. Ich würde mich freuen.

Das System

Wichtig war und ist mir, dass ich all die Informationen, die täglich auf mich herabprasseln, irgend wo sammeln kann. Am Besten an zentralen Orten, auf die ich von überall Zugriff habe, um diese Informationen zu verarbeiten. Zudem ist es mir wichtig, die verarbeiteten Informationen ebenfalls von überall abzurufen, wenn ich sie brauche.

Stuff Das Diagramm in groß

Ich habe grundlegend 6 "Container", in denen ich Informationen sammle. Diese Container "leere" ich in regelmäßigen Abständen.

Das sind zum einen das Firefox-Plugin "ReadItLater". Damit sammle ich alle möglichen URLs, die ich in irgend einer Form verarbeiten will. Diese URLs können von den unterschiedlichen Stellen kommen, sei es von einer Newsseite, von einem Freund über IRC oder aus einem RSS-Feed. Längere Texte, die ich im Moment nicht lesen kann landen auch hier. Diese Link-Liste arbeite ich jeden (oder jeden zweiten) Tag ab, und entscheide was mit ihnen passiert. Wenn es eine Webseite ist, die ich vielleicht später noch einmal suchen werde, landen in meinem del.icio.us Account (eine online Bookmarkverwaltung). Wenn ich die URL in 2 Wochen noch einmal brauche, speichere ich sie in meinem Kalender (bei Google). Ansonsten lese ich sie und lösche sie dann aus der Liste. Wenn ich hier immer noch keine Zeit habe, mich näher damit zu beschäftigen, speichere ich sie in meinem privaten Wiki auf der "incubator"-Seite. Siese Seite schaue ich immer an, wenn es mir langweilig ist oder ich für ein paar Stunden nichts zu tun habe.

Ein weiterer "Container" ist mein E-Mail Postfach, das ich von überall per IMAP abrufen kann. Hier sammle ich in meiner Inbox E-Mails von allen meinen Konten, die Newsgroups sind extra. Mein Server teilt mir über diesen "Kanal" über Status E-Mails auch mit, wenn was nicht stimmt. Meine Inbox muss immer leer sein, da bin ich sehr penibel. Gelesene und beantwortete E-Mails werden sofort an ihren Platz verschoben, Spam wird natürlich gelöscht. Wenn eine E-Mail noch auf etwas wartet (z.B. die Bestellbestätigung oder eine E-Mail die mich an etwas erinnert, das ich machen muss) landet diese in dem Ordner @ACTIONS. Diesen Ordner schaue ich für gewöhnlich mehrmals am Tag an und arbeite die angefallenen E-Mails ab.

Mein PC-Desktop ist eine weitere Sammelstelle für alle eingehenden Dateien. Diese können vom Internet, USB-Sticks oder von meinem Handy kommen. Alles landet zuerst einmal auf meinem Desktop (vorausgesetzt es hat nicht von Anfang an einen bestimmten Platz). Von hier aus wird es "verarbeitet" und dann entweder aufs RAID verschoben, hochgeladen oder gelöscht. Allerdings habe ich keinen überfüllten Desktop, hier geht es mir ähnlich wie mit meiner E-Mail Inbox. Alles was da länger als 2 Tage rumliegt wird gelöscht.

Wenn ich nicht zuhause bin oder kein Notebook zur Hand habe, schreibe ich meine Ideen, Gedanken, Termine oder lustige Zitate in ein kleines Notizbuch. Wenn ich zuhause bin, verwende ich meist mein Whiteboard für die Gedankenblitze und Telefonnotizen. Dies sind eigentlich nur "Zwischencontainer", denn die Informationen, die ich mir hier aufschreibe, übertrage ich dann meist in den Kalender, bookmarke sie in del.icio.us, schreibe es in mein Wiki oder blogge darüber. Ich habe lange nach einer digitalen Lösung gesucht, doch das schien mir am Ende doch das einfachste und schnellste. Kein booten, kein warten, kein klicken, kein Sync-Kabel.

Für alles Andere habe ich ein Ablagefach auf meinem Schreibtisch stehen, in das ich all den Kleinkram reinwerfe, der im laufe des Tages so anfällt. Die ungeöffnete Post, einen herausgerissenen Zeitungsausschnitt, die Bedienungsanleitung für das neue Telefon oder die leeren Batterien als Erinnerung, neue zu kaufen. Dieses Ablagefach ist zugleich mein "Ausgang", also alles was wieder raus muss. Da ich nicht so viel Zettelwirtschaft habe, funktioniert das auch ganz gut, doch wenn es mal mehr wird, wird es unübersichtlich.

Wie man sieht, habe ich grob drei Ablageplätze (del.icio.us, Kalender und Wiki) für Informationen die später nochmal gebracht werden. Das reicht mir völlig. Ich habe auch kein modernes Hängefachregister oder sonstige fancy GTD-Tools oder Software. Ich habe zwar alles mal ausprobiert, doch nichts passte wirklich. Mit dem Wiki (und einigen Plugins wie ToDo-Listen und Prioritäten), dem Google-Kalender und der Bookmarkverwaltung bin ich bis jetzt sehr zufrieden und klappt eigentlich ganz gut.

Was ich bis heute nicht wirklich hinbekommen habe, ist eine zentrale Kontaktverwaltung. Ich habe alles durch, von abook bis LDAP, aber mit nichts war ich zufrieden. Momentan verwende ich mein IMAP-Account dafür. In einem Ordner speichere ich für jeden Kontakt eine E-Mail mit einem Kolab-Groupware-Object. Das lässt sich gut mit dem Thunderbird-Plugin "Sync Kolab" mit dem lokalen Adressbuch synchronisieren. Keine optimale Lösung, aber besser als nichts. Wer eine bessere Idee hat, nur her damit.

Ich bin kein GTD-Fanatiker (mehr), aber dieses Systme funktioniert bei mir schon länger und ich bin wirklich besser organisiert als früher.

3 Kommentare, delicious bookmark del.icio.us


Code Kata

vom 18. July 2008

Auf codinghorror.com habe ich neulich einen interessanten Artikel gelesen, der sich mit der generellen Frage beschäftigt, ab wann man ein Experte ist, ab wann man dazulernt und warum Praxiserfahrung wichtig ist.

[...]Contrary to what you might believe, merely doing your job every day doesn't qualify as real practice. Going to meetings isn't practicing your people skills, and replying to mail isn't practicing your typing. You have to set aside some time once in a while and do focused practice in order to get better at something. I know a lot of great engineers -- that's one of the best perks of working at Amazon -- and if you watch them closely, you'll see that they practice constantly. As good as they are, they still practice. They have all sorts of ways of doing it, and this essay will cover a few of them.[...]

Von täglicher Routine wird man nicht besser. Erst wenn man sich Zeit für neues nimmt, an seine Grenzen stößt und genau dort weitermacht wo man scheitert, wird man besser. Jeff Atwood hat eine interessante Liste mit Tipps zusammengetragen, von denen ich ein paar für sehr wichtig halte und deshalb - zusammen mit einigen Tipps von mir - hier noch einmal aufführe.

Such dir einen Helden

Schau dir auf Wikipedia die Pioniere der Informatik an und such dir einen davon raus. Folge allen Links und lese über ihn. Finde heraus, mit was sich dein Held beschäftigt (hat) und versuche seine Lösungswege zu verstehen.

Lese fremden Code

Lies täglich 20 Minuten lang Code von anderen Programmierern. Wenn du keinen Code von Arbeitskollegen oder Freunden bekommen kannst, such dir ein Open-Source-Projekt das dir gefällt und arbeite dich in den Code ein. (Beispielsweise Firefox, RubyOnRails, Django, Subversion, o.ä.) Anfangs scheint es so, als wäre der Code in einem Leben nicht zu überschauen, doch mit der Zeit findet man Muster, versteht die Zusammenhänge, erkennt wie Probleme gelöst wurden und lernt Techniken und Architekturen kennen. Auch erkennt man auf diese Weise, was guten Programmcode ausmacht.

Lasse andere deinen Code lesen

Such dir eine Person, die deinen Code lesen will und lasse diese deinen Code bewerten. Gute- sowie schlechte Programmteile müssen hervorgehoben werden, nur so lernt man dazu. Ein "Ja, ist ganz nett" bringt niemanden weiter.

Nutze deine Programme besser

Mach eine Liste mit deinen 10 meistgenutzten Tools und lese für eine Stunde das Hanbruch/Dokumentaton/ManPage eines zufällig ausgewählten Programms. Versuche in dieser Stunde neues über dieses Programm herauszufinden und Arbeitsabläufe zu beschleunigen. Beispielsweise Shortcuts, Features von denen du noch nichts gehört hast oder Konfigurationseinstellungen, die die Bedienung einfacher machen.

Programmiere!

Theorie schön und gut, aber ohne richtige Programmiererfahung wird man nicht weit kommen. Der Beste Weg dafür ist es einfach zu tun. Probiere neue Technologien aus, schaue dir unterschiedliche Frameworks an und hacke was das zeug hält!

Wer ist der Beste?

Suche und arbeite zusammen im Team an Projekten. Finde heraus, was es bedeutet, der beste und der schlechteste Programmierer zu sein. So kannst du herausfinden, wie du besser werden kannst.

Code-Pflege

Lerne fremden Programmcode, den du nicht geschrieben hast zu warten. Am Besten funktioniert das, in dem man ein verwaistes Projekt wieder auferstehen lässt (fork) oder Fehler behebt (bug-fixing). So lernt man, was es ausmacht, wartbaren Code zu schreiben.

Lerne verschiedene Programmiersprachen

Such dir eine Programmiersprache heraus, die komplett andere Konzepte und Strategien verfolgt, welche du bis jetzt noch nie genutzt hast oder welche dir bis jetzt noch nie bewusst waren.

Für weitere Tipps und Kritik steht die Kommentarfunktion und die Anmerkungen am Linken Rand bereit :)

4 Kommentare, delicious bookmark del.icio.us


Distributed Proofreader

vom 8. July 2008

Letzten Winter bin ich auf das Projekt Gutenberg gestoßen und fand es eine wirklich tolle Idee. Wer es nicht kennt: Bücher deren Autoren bereits seit längerem gestorben sind, gelten als Allgemeingut und dürfen frei verwendet werden. Um dieses Allgemeingut zu gewährleisten hat sich das Gutenberg-Projekt zur Aufgabe gemacht, diese Bücher und Texte zu digitalisieren und allen in offenen Formaten zum freien Download bereitzustellen.

Doch die erste Frage die ich mir gestellt hatte war: Wer bezahlt die aufwändige Digitalisierung? Schnell bin ich über librarything.de auf pgdp.net gestoßen (Project Gutenberg Distributed Proovereaders). Hier helfen hinderte von freiwilligen beim manuellen Korrekturlesen und Einscannen der Bücher.

Der Vorgang ist recht simpel: Eine Person scannt alle Seiten eines Buches (welches als "Allgemeingut" gilt) mit einem normalen Scanner ein und jagt diese Bilder durch eine OCR-Software. Natürlich erkennt die Software nicht alles hundertprozentig und die Formatierung geht auch verloren. Gerade bei Frakturschrift wird viel falsch erkannt und muss manuell korrigiert werden. Auch Dinge wie Seitenzahlen oder Schmutz der als "Wort" erkannt wurde müssen entfernt werden, oder Überschriften müssen als solche gekennzeichnet werden. Diesen Job übernehmen die freiwilligen Helfer in mehreren Etappen. Die erste Durchsicht behebt nur die groben Fehler und kümmert sich garnicht um die Formatierung. Die zweite und dritte Durchsicht macht den Feinschliff und korrigiert evtl. vergessene Erkennungsfehler u.ä. Das fertige "Produkt" wird dann in PDF, TXT und andere Ausgabeformate transformiert und an das Projekt Gutenberg übergeben, welche dann für die Publikation und den Download sorgen.

Weltweit werden im Schnitt 200 Bücher pro Monat auf diese Weise digitalisiert. Eine beachtliche Leistung! Ich habe bereits 130 Seiten bei gaga.net (dem deutschen Ableger) korrigiert und dann bei pgdp.net weitergemacht. Der Grund dafür war die etwas merkwürdige Lizenzierung bei Gaga. Hier werden die Rechte teilweise auf spiegel.de übertragen. Beim amerikanischen Orginal landen alle korrigierten Bücher direkt bei gutenberg.

Mir hat es bis jetzt viel Spaß gemacht, ab und an ein paar Seiten zu korrigieren. Die Frakturschrift ist sehr gewöhnungsbedürftig, aber nach ein paar Seiten klappt das ganz gut. Vor allem ist es interessant, Texte zu lesen, die man sonst nie gelesen hätte. Da kommt man oft in Versuchung, immer weiter zu machen :) Für mich ein ideales Mittel, um mal für ein paar Minuten abzuschalten und zudem was für die Allgemeinheit zu tun.

Mitmachen kann jeder der Lust hat. Das Korrigieren läuft über den Browser und kann von überall (einigermaßen großer Bildschirm vorausgesetzt) gemacht werden. Anfangs darf man sich allerdings nur Bücher heraussuchen, die in Stufe P1 (erste Korrektur-Instanz) stehen, später darf man auch das Formatieren und die Korrektur der Korrektur übernehmen.

Kommentar schreiben, delicious bookmark del.icio.us


XML als DSL für GUIs

vom 21. June 2008

Ich bin schon seit vielen Jahren auf der Suche nach einer Möglichkeit, GUIs mit einem grafischen Editor zu erstellen und trotzdem nicht von einer IDE abhängig zu sein. Ich wünschte mir ein Tool, mit dem ich die GUI erstellen könnte und als "Output" eine (XML)-Beschreibung der erstellten Oberfläche erhalten würde.

AWT/Swing in Java war mir schon immer zuwieder (mit und ohne GUI-Builder). Der (live) generierte Java-Code ist meist dermaßen hässlich und nachträglich unwartbar, so dass ich meist wieder selbst Hand angelegt habe und die GUI-Elemente selbst plaziert habe. Dass es hier noch keine vernünftige Lösung gibt, wundert mich immer wieder.

Vor kurzem bin ich auf Ruby-Gnome2 gestoßen, was meine Erwartungen mehr als erfüllt hat: Mit dem general purpose GUI Builder GLADE erstellt man die GUI und speichert diese als Projekt ab. Das abgespeicherte Projekt besteht aus einer einzelnen XML-Datei, in der die komplette GUI inkl. "signals" (Event-/ActionListener für die Java-Programmierer) definiert sind. Diese XML-Datei lässt sich nun mit der GladeXML-Lib in Ruby mit wenigen Zeilen Code einlesen und ausführen. Auf Signale/Events kann man mühelos mit simplen Methoden zugreifen.

Wer mir nicht glaubt, hier eine kleine Demonstration: Zuerst wird mit GLADE die Oberfläche erstellt.

GLADE Interface Designer

Links (1) hat man die übliche Toolbox, die alle Komponenten enthält, die man für eine grafische Oberfläche braucht. Das "Designfenster" (5) in dem die GUI erstellt wrid, verhält sich genauso wie jedes andere Fenster. So kann man seine Oberfläche direkt live testen. Im Eigenschaften-Fenster lassen sich zu jeder platzierten Komponente Einstellungen wie Ausrichtung oder Beschriftung vornehmen (2). Unter dem Reiter "Packing" (3) lässt sich das Layout bestimmen. Unter "Signale" (4) lassen sich Events definieren und diesen Namen geben.

Hat man die Oberfläche soweit fertig, speichert man das Projekt ab. Als Resultat erhält man zwei Dateien. Eine projektname.glade und eine projektname.gladep. Die erste enthält die Beschreibung der GUI, und sieht ungefähr so aus (Ausschnitt!):

<glade-interface>
<widget class="GtkWindow" id="w1">
  <property name="visible">True</property>
  <property name="title" translatable="yes">Beispiel</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
...

Diese Datei lässt sich nun mit wenigen Zeilen Code in einer Ruby-Applikation einbinden. Wem das noch zu viel Arbeit ist, kann auch das Skript "ruby-glade-create-template" verwenden, um sich ein lauffähiges Grundgerüst zu generieren. Der so generierte Code sieht ungefähr so aus:

require 'libglade2'

class SampleGlade
  include GetText
  attr :glade
  
  def initialize(path_or_data, root = nil, domain = nil, localedir = nil, flag = GladeXML::FILE)
    bindtextdomain(domain, localedir, nil, "UTF-8")
    @glade = GladeXML.new(path_or_data, root, domain, localedir, flag) {|handler| method(handler)}
    
  end
  
  def on_button1_clicked(widget)
    puts "on_button1_clicked() is not implemented yet."
  end
end

if __FILE__ == $0
  SampleGlade.new("sample.glade", nil, "Sample App")
  Gtk.main
end

Das so erstellte GUI-Programm ist direkt unter Windows und Linux lauffähig (GTK, Ruby und die Ruby-Gnome2-Lib vorausgesetzt). Genau so stelle ich mir Oberflächenprogrammierung vor! Sauber, einfach, Plattformunabhängig und komplett vom restlichen Code getrennt.

Toll wäre es nun, wenn sich jemand die Mühe machen würde und GladeXML in Java und Swing nachbilden würde. So könnte man ein und die selbe GUI in einem Java-Programm und in einem Ruby-Programm nutzen!

12 Kommentare, delicious bookmark del.icio.us


Moderner Medienkonsum

vom 21. June 2008

Der Fernsehr ist eines der primitivsten Geräte die es zu kaufen gibt. Er besteht aus einem Schalter für an/aus und zwei Knöpfen: "Vor" und "Zurück". Das einzige was man mit diesem Kasten machen kann, ist die vorgefertigte Suppe passiv zu konsumieren oder zum nächsten Sender zu wechseln. Nur dumm, dass auf allen Sendern das selbe(?) läuft.

Vielleicht bin ich auch schon viel zu verwöhnt vom Internet, wo ich selbst entscheiden kann was ich wann wie und von wem konsumieren kann und auch aktiv mit dem Ersteller in Kontakt treten kann. (Ja, ich bin ein Web 2.0 Junky)

Seit ca. 5 Jahren schaue ich kein TV mehr und bin immer erstaunt, was für Müll spurlos an mir vorbeirauscht. Das ist mir erst neulich wieder bewusst geworden, als sich ein paar Kommilitonen über "Germany's next Topmodel" unterhalten haben und ganz erstaunt waren, dass ich nicht wusste wer Heidi Klumm ist.

Ich stille meinen Medien-Konsum fast ausschließlich durch das Internet. Meine Lieblings-Serien lade ich mir herunter (Ja, ich zahle auch GEZ!) oder kaufe mir die Staffel(n) bei eBay. Für den daily input schaue ich Vidcasts (revision3.com), den Rest erledigt alles digg.com, golem.de, spiegel.de und mein Feed-Reader.

Ich bin gespannt, wie lange sich dieses Medium noch halten wird.

5 Kommentare, delicious bookmark del.icio.us