Kategorien
Allgemein

Perl im Schnelldurchlauf

Beim letzten Treffen habe ich einen Einblick in Perl gegeben.

So richtig kurz war er dann doch nicht, aber anscheinend waren die anderen ByteWerfler doch motiviert genug um einige kritische Fragen zu stellen, die mir bei der Erstellung der Folien gar nicht eingefallen waren.

Kategorien
Allgemein

Wie sage ich es meiner Funktion

Ein Kollege fragte mich heute, wie man in Perl nochmal Parameter an eine Funktion übergibt.

Das ist ja im Prinzip ganz einfach. Eine Funktion in Perl erhält als Übergabeparameter eine einfache Liste mit Namen @_. Aber damit fängt es dann erst an.

Nehmen wir mal an, wir haben eine einfache Funktion mit zwei Parametern:

sub twoParamFunction {
    my ($one, $two) = @_;
    print "$one and $twon";
}

Hier wird die Liste @_ einer Liste mit gerade frisch deklarierten Variablen zugewiesen. Es werden also alle zusätzlichen Parameter verworfen. Diese Art der Parameterübergabe ist vermutlich eine der gebräuchlichsten und einfachsten.

Aber natürlich war das nicht die Art der Übergabe, die mein Kollege im Sinn hatte. Also gut, wir können auch benannte Parameter mit Standard-Werten benutzen:

sub hashAsParam {
    my %hash_param = ( 
        'one' => 'Eins',
        'two' => 'Zwei',
        @_ );
    print "$hash_param{one} und $hash_param{two}n";
}

Hier wird die Liste in eine Hashstruktur eingeblendet und so können die benannte Parameter one und two sowohl übergeben, als auch weggelassen werden – dann erhalten sie eben ihre Standardwerte.

Aber ach, das war es auch nicht. Nun gut, wir können beide Methoden mischen:

sub mixedParams {
    my ($one, $two, %optional) = @_;
    print "$one and $twon";
    print join("; ", keys %optional) . "n";
}

Hier werden aus der Parameterlliste die ersten beiden Elemente $one und $two zugewiesen, und der Rest wird wie in hashAsParam einer Hashstruktur aufgehen. Wenn hierbei auch noch Standardwerte vorgegeben werden sollen, kann das in einer extra Zeile geschehen.

Hmhm, das war es auch nicht. Ok, wir können natürlich auch noch die Liste einzeln abarbeiten:

sub oneStep {
    my $one = shift;
    my $two = shift;
    print "$one and $twon";
}

Ja, genau das wars! Aber wo wir jetzt schon so viele Varianten hatten, können wir eine noch zusätzlich anbieten:

sub selectAFew {
    my ($one, $four) = @_[0,3];
    print "$one a $fourn";
}

Äh? Aber ab hier sollte eigentlich spätestens klar sein, dass man perldata und man perlsyn mal wieder besucht werden sollten. Um den ganzen die Krone aufzusetzen, kam mein Kollege eine halbe Stunde später wieder an. Diesmal wollte er aber die Möglichkeiten der Rückgaben aus Perl-Funktionen wissen…

Kategorien
Allgemein

Perl ausprobieren

Immer mal wieder frage ich mich, was ein einfacher Perl-Ausdruck denn nun wirklich macht. Da kommt der eingebaute Debugger genau richtig.

Für einfache Ausdrucke und Versuche ist es natürlich am einfachsten einen Perl-Einzeiler zu benutzen. Also

perl -e 'print "Hallo Weltn"'

Für Ausdrucke, die aber auf Variablen zur Laufzeit angewiesen ist, kann es recht müßig sein die History der Shell zu nutzen, oder eine Datei zu öffnen und dort  zu experimentieren.

Debugger to the rescue

Da bietet sich dann an den Debugger zu benutzen. Aber ich will ja eigentlich keine Datei entwanzen. Das Programm für den Debugger muss aber auch gar nicht gross sein. Eine einfache 0 tut es da auch. Mit perl -d -e 0 gelangt man mit dem folgenden Ausdruck in seine persönliche Perl Spielwiese:

user@rechner:~$ perl -d -e 0
Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(-e:1):    0
  DB<1> $a="hallo"

  DB<2> print "$a weltn"
hallo welt

Wie man sieht, kann man im Debugger Variablen setzen und auf diese im nächsten Ausdruck zugreifen.

Mit dem üblichen Debugger Befehl x – Untersuche (eXamine) diesen Ausdruck/diese Variable – können wunderbar auch geschachtelte Datenstruckturen anschaulich dargestellt werden.

  DB<3> @a=qw(hallo welt)

  DB<4> x @a
0  'hallo'
1  'welt'
  DB<5> x @a
0  ARRAY(0x8404d64)
   0  'hallo'
   1  'welt'
  DB<6>

Macht man einen Fehler, so gibt der Debugger eine Fehlermeldung aus und man hat wieder einen neuen Versuch.

  DB<6> $a[a)
syntax error at (eval 11)[/usr/share/perl/5.8/perl5db.pl:628] line 2, near "a)"
Missing right curly or square bracket at (eval 11)[/usr/share/perl/5.8/perl5db.pl:628] line 4, at end of line
Kategorien
Allgemein

Immer eins nach dem anderen

Ein kleines Beispiel zeigt, wo die Reihenfolge der Deklaration von Variablen in Java Klassen wichtig ist.

Auf der Tomcat Mailingliste kam heute eine Mail, in der jemand fragte, warum sein Singleton im Tomcat nicht einamlig in der JVM vorhanden war. Die Lösung war wohl, dass er zwei verschiedene Classloader benutzte und daher zwei verschiedene Instanzen.

Ein anderes Problem seines Codes war aber ganz anderer Natur. Der Code sah etwa so aus:

public class Singleton {
    private static Singleton instance = new Singleton();
    private static int counter = 0;

    private Singleton() {
         System.out.println("Counter: " + (++counter));
    }

    public static getInstance() {
        return instance;
    }

    public int getCounter() {
        return counter;
    }
}

Auf den ersten Blick sieht das in Ordnung aus. Der Konstruktor gibt brav ein "Counter: 1" aus und man ahnt nichts böses. Bis dann ein kleiner Testfall mit jUnit aufkreuzt:

public class SingletonTest extends TestCase {
     public void testSingletonCounter() {
          Singleton instance = Singleton.getInstance();
          assertEquals(instance.getCounter(), 1,
              "Es kann nur einen geben");
     }
}

Hier wird dann plötzlich klar, dass intern der counter trotz anderweitiger Ausgabe wieder auf 0 gesetzt wurde. Das liegt daran, dass die Initialisierung der statischen Klassenvariablen der Reihe nach geschieht. Im Konstruktor ist counter also erstmal nicht definiert – implizit also null – und wird dann auf eins inkrementiert. Nach dem Konstruktor wird dann die vermeintlich noch nicht initialsierte Variable auf 0 gesetzt.

Das kann ganz schön ins Auge gehen.

Kategorien
Allgemein

Hohe Hürden für wahre Helden

Ich kann die armen PHP Programmierer nur bedauern. Das schreiben von einfachen Anwendungen mit PHP soll ja recht einfach sein, aber wehe es gibt einen Fehler. Das Debuggen von PHP Programmen ist nicht einfach.

Aus dem Grunde werden wohl die meisten PHP Anwender das Debuggen per echo oder var_dump anwenden. Wenn das aber nicht mehr ausreicht, so muss doch ein echter Debugger her. Da gibt es mehrere in der OpenSource Welt.

Ausprobiert habe ich XDebug und DBG.

DBG war als erstes an der Reihe. Nachdem ich das Modul nach Anleitung kompiliert und installiert hatte, dauerte es auch nur noch einen halben Tag, bis ich mich mit PHPEclipse mit dem Debugging anfangen konnte. DBG kann sich pro Anfrage per Parameter auf einen anderen entfernten Debugger einlassen. Was von Vorteil ist, wenn mehrere Entwickler mit einem Webserver arbeiten. Leider habe ich es nicht hinbekommen, dass auch jede Anfrage an den Debugger weitergeleitet wurde. Alles in allem, keine schöne Erfahrung.

Danach habe ich noch XDebug ausprobiert, da es von der pdt Erweiterung des Eclipse-Projektes genutzt werden kann. Mit der von DBG gewonnenen Erfahrung bin ich schon viel schneller zum Debuggen gekommen. Aber anscheinend geht XDebug davon aus, das immer nur ein Entwickler debuggen will. Denn das PHP Modul verbindet sich immer mit einem bei Apache Start voreingestellten Host und Port. Dafür zeigte sich der Debugger etwas stabiler als DBG. Als nettes Beiwerk verändert XDebug die Ausgabe von var_dump und den Fehlermeldungen von PHP. Auch ist ein einfacher Profiler einschaltbar, dessen Ergebnisse mit kcachegrind auswertbar sind. (DBG soll auch einen Profiler enthalten, aber die Doku ist quasi nicht vorhanden.)

Alles in allem: Arme PHP Entwickler.

Kategorien
Allgemein

Einfacher Interpreter einer einfachen Sprache

Nachdem Jörg mir ein paar Perl-Bücher geliehen hatte und schon seit längerer Zeit das „Hello World!“ Plakat auf unserem stillen Örtchen hängt, musste ich mich einfach an einen Brainfuck Interpreter versuchen.

Und hier ist das Ergebnis:

#!/usr/bin/perl
{
  package BF;
  use strict;
  use warnings;

  my @REGISTERS=('PP', 'MP', 'LOOP');
  my %OPERATORS=(
      '+' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]++;
          $self->{_registers}{'PP'}++;
      },
      '-' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_memory}[$self->{_registers}{'MP'}]--;
          $self->{_registers}{'PP'}++;
      },
      '.' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          push @{ $self->{_output} }, $self->{_memory}[$self->{_registers}{'MP'}];
          $self->{_registers}{'PP'}++;
      },
      '>' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}++;
          $self->{_registers}{'PP'}++;
      },
      '<' => sub {
          my ($self) = @_;
          return if $self->{_registers}{'LOOP'} < 0;
          $self->{_registers}{'MP'}--;
          $self->{_registers}{'PP'}++;
      },
      '[' => sub {
          my ($self) = @_;
          if ($self->{_memory}[$self->{_registers}{'MP'}] > 0) {
              push @{ $self->{_stack} }, $self->{_registers}{'PP'};
          } else {
              $self->{_registers}{'LOOP'}--;
          }
          $self->{_registers}{'PP'}++;
          return;
      },
      ']' => sub {
          my ($self) = @_;
          my $loop_stacktart = pop @{ $self->{_stack} };
          if ($self->{_memory}[$self->{_registers}{'MP'}]) {
              $self->{_registers}{'PP'} = $loop_stacktart;
          } else {
              $self->{_registers}{'PP'}++;
          }
          $self->{_registers}{'LOOP'}++;
      },
  );

  sub new {
      my $class = shift;
      my $data = {
          _registers => { map { $_ => 0 } @REGISTERS },
          _memory => [],
          _stack => [],
          _output => [],
      };
      bless $data, $class;
  };

  sub run {
      my $self = shift;
      while ($self->{_registers}{PP} < $self->{_max_program_size}) {
          my $op_char = $self->{_program}[$self->{_registers}{PP}];
          my $op = $OPERATORS{$op_char};
          $op->($self);
      }
  }

  sub set_memory {
      my ($self, $memory) = @_;
      $self->{_program} = [ split //, $memory ];
      $self->{_max_program_size} = @{ $self->{_program} };
  }

  sub output {
      my $self = shift;
      return join ',', @{ $self->{_output} };
  }

  sub char_output {
      my $self = shift;
      return join '', map { chr($_) } @{ $self->{_output} };
  }
}

package main;
use strict;
use warnings;

use Test::More qw(no_plan);

my %test_data = (
    '++.' => '2',
    '++-.' => '1',
    '++.>+.<+.' => '2,1,3',
    '+++.[-.].' => '3,2,1,0,0',
    '++.[->++[.-]<]' => '2,2,1,2,1',
    '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' => '72,101,108,108,111,32,87,111,114,108,100,33,10',
);
while (my ($prog, $exptected_output) = each %test_data) {
    my $bf = BF->new();
    $bf->set_memory($prog);
    $bf->run();
    is($bf->output(), $exptected_output, $prog);
}

my $bf = BF->new();
$bf->set_memory('++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.');
$bf->run();
is("Hello World!n", $bf->char_output(), "Hello World!");

Eine tolle sinnlose Beschäftigung.

Und wen es interessiert: Das Plakat zum Programm.

Kategorien
Allgemein

Refcards

Wenn man sich immer mal wieder fragt, wie hiess noch mal der Befehl, oder die welche Tastenkombination war die richtige?

Auf http://refcards.com/ gibt es jede Menge etwa A4 grosser PDF Dateien zu unterschiedlichen Themen, wie perl, javascript oder auch Firefox.

Da lohnt es sich auf jeden Fall mal vorbeizuschauen.

Kategorien
Allgemein

Gefilterte Listen

sind keine Neuigkeit und daher auch schon von anderen Implementiert.

Ich wollte aus einer Liste von Elementen nur ganz bestimmte Elemente holen und über diese direkt iterieren. Da die Elemente immer wieder nach unterschiedlichen Kriterien gefiltert werden sollten, dachte ich mir dass ich das ja verallgemeinern könnte. Eventuell mit einem Filter Interface:

interface Filter<T> {
   boolean isCase(T element);
}

Und einer allgemeinen Filterfunktion:

List<T> filterList(List<T> listOfElements, Filter<T> filter) {
   List<T> filteredElements = new ArrayList<T>();
   for (T element: listOfElements) {
      if (filter.isCase(element)) {
         filteredElements.add(element);
      }
   }
   return filteredElements;
}

Dann habe ich einfach mal filter und list und java in google eingegeben und bin (natürlich) über andere Leute mit demselben Problem und ähnlichen Ansätzen gestossen.

Bei google-collections und bei den jakarta-commons (mit generics). Dort werden allerdings auch direkt schon Iteratoren bereitgestellt, die filtern können und vieles mehr.

Also auch diesmal muss man das Rad nicht neu erfinden. Schade 🙂

Bei google-collections heissen die Filter im übrigen Predicates.

Kategorien
Allgemein

Prototypenärger

Warum Prototypen nur in den seltensten Fällen sinn machen.

Nachdem ich vor einiger Zeit schon mal über einen Sprachwechselfehler in einem meiner Perl Skripte gestossen bin.

Ich hatte eine Funktion wie folgt deklariert:

sub func1() {
    my $var = shift;
    ...
}

Und der Perl Compiler mäkelte irgendwas vonwegen too many arguments… Klar das () weist Perl an, das diese Funktion keine Parameter bekommen darf. Also das () entfernt und alles war wieder gut.

Objekte und Prototypen

Dann bin ich jetzt über eine Code Stelle gestossen, die folgendermassen als Methode in einem Objekt deklariert war:

sub method1( $ ) {
    my $self = shift;
    ....
    my $var = shift;
    ...
}

Da wollte der Autor wohl festhalten, dass diese Methode nur einen Parameter annimmt, und hat das $var=shift später übersehen, oder hat sich gedacht, $self=shift würde vorher ausgewertet werden. Es ist aber so, dass in Methodenaufrufen die Protoypen ignoriert werden.

Als Funktion aufgerufen macht die method1() aber auch nicht das was sie soll, da $self mit hoher Wahrscheinlichkeit nicht den richtigen Inhlat hat.

Praktisch sind sie aber doch

Zum Beispiel um eine Funktion wie grep nachzubauen:

sub my_grep(&@) {
    my ($grepper, @list) = @_;
    my @result = ();
    foreach (@list) {
        push @result, $_
          if ($grepper->());
    }
    return @result;
}

Diese Funktion kann dann wie das eingebaute grep benutzt werden:

my @odd = my_grep { $_[0] % 2 } ( 1 .. 20 );
Kategorien
Allgemein

Aliasing in Perl

Da nutzt man seit Jahren Perl, und dann so was…

In Perl werden Schleifenvariablen über Arrays (und Hashes) als Aliase genutzt. Verändert man diese, so verändert man die Inhalt in den Arrays.

#!/usr/bin/perl
use warnings;
use strict;

use Test::More qw(no_plan);

my @names = qw( Felix Urte Sven );
for my $name (@names) {
    $name =~ s/e//;
}

my @new_names = qw( Flix Urt Svn );
is_deeply( @names, @new_names );

Bisher ist mir das nie aufgefallen. Hoffentlich habe ich tatsächlich nirgendwo solche Nebeneffekte produziert.

Oioioioi…