Tuesday, January 05, 2010

Der Flip-Flop-Operator

Nachdem ich in letzter Zeit wieder einige Threads in verschiedenen Foren gesehen habe, wo das ziemlich nützlich gewesen wäre, hier mal ein Artikel aus der 7. Ausgabe von $foo - Der Flip-Flop-Operator.

Hinter dem Flip-Flop-Operator versteckt sich der Range-Operator. Dessen gebräuchlichste Verwendung ist es wohl, Listen zu erstellen. In vielen Programmen sieht man so etwas wie

for( 1..10 ){
# do anything
}

Hier wird eine Liste von 1 bis 10 aufgebaut. Mit dieser Eigenschaft dürften wohl die meisten den Range-Operator kennen. Weit seltener kommt der Range-Operator mit seiner Eigenschaft als Flip-Flop vor.

Im Skalaren Kontext liefert der Range-Operator einen Boolschen Wert zurück und ist somit "bistabil". Diese Flip-Flop-Eigenschaft eignet sich zum Beispiel sehr gut, wenn man einen bestimmten Bereich aus einem Text ausgeben will und man nicht selbständig mit if's einen Boolschen Zustand pflegen will.

In Listing x ist ein Textausschnitt zu sehen, von dem nur der Text zwischen start und stop interessant ist.

Dies ist ein längerer Text
mit vielen unnötigen Zeilen
vor dem eigentlich Wichtigen.

START
Wichtiger Text
über drei
Zeilen
STOP

Und noch unwichtigeren Zeilen
nachher


Hier ein Code, wie man es mit einem eigenen "Flip-Flop" machen kann (Der eben gezeigte Text ist im __DATA__-Bereich):

#!/usr/bin/perl

use strict;
use warnings;

my $bool = 0;

while( my $line = <DATA> ){
if( $line =~ /^START/ ){
$bool = 1;
}

print $line if $bool;

if( $line =~ /^STOP/ ){
$bool = 0;
}
}

Der Flip-Flop ist solange false wie der Ausdruck auf der linken Seite falsch ist. Ist der linke Ausdruck wahr, wird der Flip-Flop auch true und bleibt die solange bis der Ausdruck auf der rechten Seite wahr ist. Danach wird der Flip-Flop wieder false. Damit wird es recht einfach, den wichtigen Teil aus dem Text zu ziehen:


#!/usr/bin/perl

use strict;
use warnings;

my $bool = 0;

while( my $line = <DATA> ){
print $line if $line =~ /^START/ .. $line =~ /^STOP/;
}

Wichtig ist noch zu wissen, dass jeder Flip-Flop sein eigenen Boolschen Status hat, so dass Flop-Flops auch verschachtelt werden können:

if( $line =~ /^START/ .. $line =~ /^STOP/ ){
if( $line =~ /ber/ .. $line =~ /drei/ ){
chomp $line;
print '*' . $line . "*\n";
}
else{
print $line;
}
}


In diesem Listing ist aber auch ein anderes Phänomen dargestellt. Der Flip-Flop kann im selben Durchlauf erst true und gleich wieder false werden, der if-Block wird dennoch mindestens das eine Mal ausgeführt. Das hat damit zu tun, dass der rechte Ausdruck evaluiert wird, sobald der Range-Operator evaluiert wird. Dadurch ergibt sich die Ausgabe:

START
Wichtiger Text
*über drei*
Zeilen
STOP


Möchte man dieses Verhalten unterbinden, soll der rechte Ausdruck also erst im nächsten Durchlauf evaluiert werden, muss man statt .. einfach ...> verwenden. Dadurch ergibt sich folgende Ausgabe:

START
Wichtiger Text
*über drei*
*Zeilen*
*STOP*


In den meisten Fällen liefern aber sowohl ... als auch .. die gleichen Ergebnisse.

3 comments:

rainboxx said...

Hi,

schau dir mal deinen Code an... Blogger.com scheint die >-Zeichen zu verstecken.

Grüße

ReneeB said...

Danke... Ist korrigiert.

Rüdiger Plantiko said...

Hallo Renée,

ein schöner Blog - und ein schönes Anwendungsbeispiel, weil das Script nicht nur funktioniert, sondern auch von der Semantik zur Aufgabe passt: Man versteht die ".." ja als "von-bis", und die Schleife

while (<DATA>) {
print if /^START/ .. /^STOP/;
}

liest man daher wie "Drucke alles von START bis STOP" - genau das, was der Code macht.

Also: Lesbarer Code, dank des "Prinzips der kleinsten Überraschung"!