Upload
wayne-griswold
View
215
Download
0
Embed Size (px)
Citation preview
Take control
Introductie in het wijzigen van standaard Perl gedrag
use Workshop::Perl::Dutch 5;date( '2008-02-29' );author( abeltje => 'Abe Timmerman' );
28/2/2008 Take control 2
Technieken
• overload
• tie
• CORE::GLOBAL
• Attribute::Handlers
28/2/2008 Take control 3
overload gebruikers?
28/2/2008 Take control 4
overload
• stringify– String functies
• Numify– Rekenkundige
bewerkingen– Rekenkundige functies
• overload::constant()
28/2/2008 Take control 5
overload API
• Operator overloading met sub
• Unary operators:– 1 argument
• Binary operators– 3 argumenten
• 1ste altijd een object• 2de object of constante• 3de geeft aan of 1ste en 2de zijn verwisseld
28/2/2008 Take control 6
Coords.pmpackage Coords;
sub new { my( $class, $x, $y ) = @_; bless { _x => $x || 0, _y => $y || 0 }, $class;}
sub move { my( $self, $dx, $dy ) = @_; ref $dx eq __PACKAGE__ and ( $dx, $dy ) = ( $dx->{_x}, $dx->{_y} ); $self->{_x} += $dx; $self->{_y} += $dy; return $self;}
sub as_string { my( $self ) = @_; return sprintf "(%d, %d)", $self->{_x}, $self->{_y};}
28/2/2008 Take control 7
Coords.pmpackage Coords;
use overload q{""} => \&as_string, fallback => 1;
sub new { my( $class, $x, $y ) = @_; bless { _x => $x || 0, _y => $y || 0 }, $class;}
sub move { my( $self, $dx, $dy ) = @_; ref $dx eq __PACKAGE__ and ( $dx, $dy ) = ( $dx->{_x}, $dx->{_y} ); $self->{_x} += $dx; $self->{_y} += $dy; return $self;}
sub as_string { my( $self ) = @_; return sprintf "(%d, %d)", $self->{_x}, $self->{_y};}
28/2/2008 Take control 8
Testing Coords.pm
use Test::More 'no_plan';
my $c = Coords->new( 150, 150 );is $c, $c->as_string, "overloaded stringify: $c";
$c->move( -50, 50 );is $c, "(100, 200)", "->move(-50,50): $c";
my $m = Coords->new( 50, -50 );$c->move( $m );is $c, "(150, 150)", "->move$m: $c";
# overload/coords/1fase/ -> prove -lv t/*.t
28/2/2008 Take control 9
Using '+' to moveuse Test::More 'no_plan';
my $c1 = Coords->new( 150, 150 );is $c1, $c1->as_string, "overloaded stringify: $c1";
my $c2 = Coords->new( -50, 50 );is $c2, $c2->as_string, "overloaded stringify: $c2";
my $r2 = $c1 + $c2;isa_ok $r2, 'Coords';is $r2, "(100, 200)", "overloaded addition: $r2";
28/2/2008 Take control 10
Using '+' to moveuse Test::More 'no_plan';
my $c1 = Coords->new( 150, 150 );is $c1, $c1->as_string, "overloaded stringify: $c1";
my $c2 = Coords->new( -50, 50 );is $c2, $c2->as_string, "overloaded stringify: $c2";
my $r1 = $c1->copy;isa_ok $r1, 'Coords';is $r1, $c1, "->copy: $r1";
$r1->move( $c2 );is $r1, "(100, 200)", "->move$c2: $r1";
my $r2 = $c1 + $c2;isa_ok $r2, 'Coords';is $r2, "(100, 200)", "overloaded addition: $r2";
28/2/2008 Take control 11
Using '+' to movepackage Coords;
use overload q{""} => \&as_string,
fallback => 1;
sub new { my( $class, $x, $y ) = @_; bless { _x => $x || 0, _y => $y || 0 }, $class;}
sub copy { return bless { _x => $_[0]->{_x}, _y => $_[0]->{_y} }, ref $_[0]; }
sub move { my( $self, $dx, $dy ) = @_; ref $dx and ( $dx, $dy ) = ( $dx->{_x}, $dx->{_y} ); $self->{_x} += $dx; $self->{_y} += $dy; return $self;}
sub as_string { my( $self ) = @_; return sprintf "(%d, %d)", $self->{_x}, $self->{_y};}
28/2/2008 Take control 12
Using '+' to movepackage Coords;
use overload q{""} => \&as_string, q{+} => \&add_move, fallback => 1;
sub new { my( $class, $x, $y ) = @_; bless { _x => $x || 0, _y => $y || 0 }, $class;}
sub copy { return bless { _x => $_[0]->{_x}, _y => $_[0]->{_y} }, ref $_[0]; }
sub move { my( $self, $dx, $dy ) = @_; ref $dx and ( $dx, $dy ) = ( $dx->{_x}, $dx->{_y} ); $self->{_x} += $dx; $self->{_y} += $dy; return $self;}
sub add_move { my( $a1, $a2 ) = @_; ref $a2 or die "Cannot move() with constants!"; $a1->copy->move( $a2 );}
sub as_string { my( $self ) = @_; return sprintf "(%d, %d)", $self->{_x}, $self->{_y};}
28/2/2008 Take control 13
Meer informatie
• perldoc overload
28/2/2008 Take control 14
tie() gebruikers?
28/2/2008 Take control 15
• Geef een object de interface van een Perl variabele type
• Mogelijke typen:– Scalar– Array– Hash– Handle
• Toegang tot het onderliggende object met behulp van tied()
tie()
28/2/2008 Take control 16
• API:– TIEARRAY constructor– FETCH, STORE– FETCHSIZE, STORESIZE– CLEAR, EXTEND– EXISTS, DELETE– PUSH, POP,– SHIFT, UNSHIFT, SPLICE– UNTIE, DESTROY
TIEARRAY API
28/2/2008 Take control 17
TIEARRAY
• Geef een object de interface van een array
• In het voorbeeld: gebruik een scalar als array– substr() <-> push/pop/unshift/shift/slice
28/2/2008 Take control 18
CharArray (src1)package CharArray;use warnings;use strict;
sub TIEARRAY { my $class = shift; ref $_[0] or die "Usage: tie my @a, CharArray => \$scalar;"; bless $_[0], $class;}
sub UNTIE { }
sub FETCHSIZE { my $self = shift; defined $$self ? length( $$self ) : 0;}
28/2/2008 Take control 19
CharArray (src2)sub FETCH { my( $self, $index ) = @_; $index > length $$self and $$self .= "" x ( 1 + $index - length $$self ); defined $$self ? substr $$self, $index, 1 : undef;}sub STORE { my( $self, $index, $value ) = @_; $index > length $$self and $$self .= "" x ( 1 + $index - length $$self ); substr $$self, $index, 1, $value;}sub PUSH { my $self = shift; $$self .= join "", @_; length $$self;}sub POP { my $self = shift; my $last = substr $$self, -1, 1; $$self = substr $$self, 0, length( $$self ) - 1; $last;}1;
28/2/2008 Take control 20
Testing CharArray.pm#! /usr/bin/perluse warnings;use strict;
use Test::More 'no_plan';
use_ok 'CharArray';
{ my $orig = 'value'; tie my @ca, 'CharArray', \$orig; is $ca[0], 'v', "First value '@ca' (${ tied( @ca ) })"; push @ca, 's'; is @ca, 6, "new length (${ tied( @ca ) })";
my $sorted = join "", sort @ca; is $sorted, 'aelsuv', "sorting the array works ($sorted)";
untie @ca; is $orig, 'values', "still the changed value in original ($orig)";}
# tie/array/ -> prove -lv t/*.t
28/2/2008 Take control 21
• API:– TIEHANDLE constructor– schrijven
• PRINT, PRINTF• WRITE
– lezen• READLINE• READ, GETC
– CLOSE– UNTIE, DESTROY
TIEHANDLE API
28/2/2008 Take control 22
TIEHANDLE (output)
• Output– STDOUT, STDERR– Iedere andere GLOB
• Methods:– TIEHANDLE()– PRINT– PRINTF
28/2/2008 Take control 23
CatchOut.pmpackage CatchOut;use strict;use warnings;
our $VERSION = 0.04;
# tie *HANDLE, CatchOut => <\*TIEDHANDLE | \$buf>sub TIEHANDLE { my $class = shift; ref $_[0] eq __PACKAGE__ and return $_[0]; ref $_[0] eq 'SCALAR' or die "Usage:\n\ttie *HANDLE, CatchOut => <\*TIEDHANDLE | \$buf>"; bless $_[0], $class;}
sub PRINT { my $self = shift; $$self .= join "", @_;}
sub PRINTF { my $self = shift; my( $fmt, @args ) = @_; $$self .= sprintf $fmt, @args;}
1;
28/2/2008 Take control 24
Testing CatchOut.pm#! perluse warnings;use strict;
use Test::More 'no_plan';
use_ok 'CatchOut';
{ my $outbuf; { local *OUT; tie *OUT, 'CatchOut', \$outbuf; print OUT "Testline\n"; untie *OUT; } is $outbuf, <<' __EOTEST__', "Caught the right output";Testline __EOTEST__}
28/2/2008 Take control 25
TIEHANDLE (input)
• Input– STDIN– Elke andere GLOB
• Methods:– TIEHANDLE– READLINE
28/2/2008 Take control 26
FeedIn.pmpackage FeedIn;use warnings;use strict;
our $VERSION = 0.01;
# tie *FH, FeedIn => $text;sub TIEHANDLE { my( $class, $store ) = @_; bless \$store, $class;}
sub READLINE { my $self = shift; defined $$self or return; length $$self or return; if ( ! defined $/ ) { # slurp-mode my $all = $$self; $$self = undef; return $all; } if ( wantarray ) { my @lines = grep length $_ => $$self =~ m{(.*?(?:$/|\z))}sg; $$self = undef; return @lines; } else { return defined $$self =~ s{(.*?(?:$/|\z))}{}s ? $1 : undef; }}1;
28/2/2008 Take control 27
Testing FeedIn.pm#! perluse warnings;use strict;use Test::More 'no_plan';use_ok 'FeedIn';{ local *IN; tie *IN, 'FeedIn', "regel 1\nregel 2"; my @line = <IN>; is scalar @line, 2, "2 lines in list-context";
is $line[0], "regel 1\n", "Read a line '$line[0]'"; is $line[1], "regel 2", "Read a line '$line[1]'";}{ local *IN; tie *IN, 'FeedIn', "regel 1\nregel 2"; my @line; while ( <IN> ) { push @line, $_ } is scalar @line, 2, "2 lines in list-context";
is $line[0], "regel 1\n", "Read a line '$line[0]'"; is $line[1], "regel 2", "Read a line '$line[1]'";}{ local *IN; tie *IN, 'FeedIn', "regel 1\nregel 2"; my $lines = do { local $/; <IN> }; is $lines, "regel 1\nregel 2", "Slurp-mode";}
28/2/2008 Take control 28
Meer informatie
• perldoc perltie
28/2/2008 Take control 29
CORE::GLOBAL gebruikers?
28/2/2008 Take control 30
CORE::GLOBAL::
• Herdefinieren interne functies– prototype CORE::– In de compileer fase (BEGIN)
• Origineel altijd nog beschikbaar– CORE::
28/2/2008 Take control 31
CORE::GLOBAL::gmtime#! /usr/bin/perluse warnings;use strict;
BEGIN { # 29 Feb 2008 12:00:00 GMT *CORE::GLOBAL::gmtime = sub (;$) { my $stamp = @_ ? $_[0] : 1204286400; CORE::gmtime( $stamp ); };}
printf "[ empty] %s\n", scalar gmtime( );printf "[time()] %s\n", scalar gmtime( time );
28/2/2008 Take control 32
Een test case voor open()
• Ik wil de volgende soort code testen:– open my $fh, '<', '/proc/cpuinfo'
• Herdefinieer – CORE::GLOBAL::open
• Gebruik een tied handle voor invoer– FeedIn.pm
28/2/2008 Take control 33
MyOpen.pmpackage MyOpen;use warnings;use strict;
our $VERSION = 0.01;
sub core_open (*;$@) { my( $handle, $mode, $file, @list ) = @_;
# make sure filehandles are in their own package my $pkg = caller; if ( defined $handle and !ref $handle ) { # bareword handle no strict 'refs'; $handle = *{ "$pkg\:\:$handle" }; } elsif ( !defined $handle ) { # undefined scalar, provide GLOBref $_[0] = $handle = do { no strict 'refs'; \*{ sprintf "%s::NH%d%d%d", $pkg, $$, time, rand 100 }; }; }
# convert to two argumented open() defined $file and $mode .= " $file"; CORE::open( $handle, $mode );};
# prepare open() for runtime overrideBEGIN { *CORE::GLOBAL::open = \&core_open }
1;
28/2/2008 Take control 34
Testing MyOpen.pm#! perluse warnings;use strict;
use Test::More 'no_plan';
BEGIN { use_ok 'MyOpen' }ok defined &CORE::GLOBAL::open, "CORE::GLOBAL::open() defined";
my $content;{ CORE::open( my $fh, '<', $0 ) or die "Cannot CORE::open($0): $!"; isa_ok $fh, 'GLOB'; $content = do { local $/; <$fh> }; close $fh; like $content, qr/BEGIN { use_ok 'MyOpen' }/, "contains MyOpen";}{ open my $fh, '<', $0 or die "Cannot open($0): $!"; isa_ok $fh, 'GLOB'; my $file = do { local $/; <$fh> }; close $fh; is $file, $content, "contents still the same";}
28/2/2008 Take control 35
Bringing it togther (1/2)#! perluse warnings;use strict;
use Test::More 'no_plan';
BEGIN { use_ok 'MyOpen' }ok defined &CORE::GLOBAL::open, "CORE::GLOBAL::open() defined";
use_ok 'FeedIn';{ no warnings 'redefine'; local *CORE::GLOBAL::open = \&tied_open; open my $fh, '<', $0 or die "Cannot tied_open($0): $!"; isa_ok tied( $fh ), 'FeedIn'; my $file = do { local $/; <$fh> }; close $fh;
is $file, "open: $0", "tied_open() returned '$file'";}
28/2/2008 Take control 36
Bringing it together (2/2)sub tied_open (*;$@) { my( $handle, $mode, $file ) = @_;
# make sure filehandles are in their own package my $pkg = caller; if ( defined $handle and !ref $handle ) { # bareword handle no strict 'refs'; $handle = *{ "$pkg\:\:$handle" }; } elsif ( !defined $handle ) { # undefined scalar, provide a GLOB $_[0] = $handle = do { no strict 'refs'; *{ sprintf "%s::NH%d%d%d", $pkg, $$, time, rand 100 }; }; }
# convert to two argumented open() defined $file and $mode .= " $file";
# do the magic-tie for open "< $0" or pass to CORE::open() if ( $mode =~ m/^(?:<\s*)?($0)/ ) { tie $handle, FeedIn => "open: $1"; } else { CORE::open( $handle, $mode ); }}
28/2/2008 Take control 37
Meer informatie
• perldoc perlsub
28/2/2008 Take control 38
Attribute::Handler gebruikers?
28/2/2008 Take control 39
Attribute::Handlers
• Perl heeft syntax voor attributes– :my_attribute(data)
• Perl heeft twee geïmplementeerde attributes– :lvalue– :ATTR
• Via :ATTR is de attribute implementatie uit te breiden– Een attribute is een sub met die naam die
het :ATTR attribute heeft
28/2/2008 Take control 40
Types voor een attribute
• Deze typen kunnen een attribute krijgen– SCALAR– ARRAY– HASH– CODE (sub)
28/2/2008 Take control 41
Aandachtspunten
• De handler sub moet bekend zijn in de aanroepende namespace– use base– Declareer in UNIVERSAL::
• Argumenten aan de handler sub– Aanroepende package– Referentie naar de symbol table (CODE)– Referentie naar de variabele/code– Attribute naam– Data die aan het attribute wordt mee gegeven– Fase voor de handler (BEGIN,CHECK,INIT,END)
28/2/2008 Take control 42
Een attribute voor tie()package Tie_OddEven;use strict;use warnings;
our $VERSION = 0.01;
use Attribute::Handlers;
sub OddEven :ATTR(SCALAR) { my( $pkg, $symbol, $referent, $attr, $data ) = @_; tie $$referent, __PACKAGE__, $data;}sub TIESCALAR { my $class = shift; bless \(my $self = shift), $class;}sub FETCH { my $self = shift; return $$self % 2 == 0 ? 'even' : 'odd';}sub STORE { my $self = shift; $$self = shift;}1;
28/2/2008 Take control 43
Voorbeeld code voor gebruik#! /usr/bin/perluse warnings;use strict;
use lib 'lib';use Tie_OddEven;
tie my $oe, Tie_OddEven => 0;while ( 1 ) { print "Number: "; chomp( my $input = <> ); last unless $input =~ /^-?\d+$/; $oe = $input; printf "$input is $oe (%d)\n", ${ tied $oe };}
28/2/2008 Take control 44
Voorbeeld code voor gebruik#! /usr/bin/perluse warnings;use strict;
use lib 'lib';use base 'Tie_OddEven';
my $oe :OddEven(0);while ( 1 ) { print "Number: "; chomp( my $input = <> ); last unless $input =~ /^-?\d+$/; $oe = $input; printf "$input is $oe (%d)\n", ${ tied $oe };}
28/2/2008 Take control 45
Oorspronkelijke attributepackage Tie_OddEven;use strict;use warnings;
our $VERSION = 0.01;
use Attribute::Handlers;
sub OddEven :ATTR(SCALAR) { my( $pkg, $symbol, $referent, $attr, $data ) = @_; tie $$referent, __PACKAGE__, $data;}sub TIESCALAR { my $class = shift; bless \(my $self = shift), $class;}sub FETCH { my $self = shift; return $$self % 2 == 0 ? 'even' : 'odd';}sub STORE { my $self = shift; $$self = shift;}1;
28/2/2008 Take control 46
Een UNIVERSAL:: attributepackage Universal_OddEven;use strict;use warnings;
our $VERSION = 0.01;
use Attribute::Handlers;
sub UNIVERSAL::OddEven :ATTR(SCALAR) { my( $pkg, $symbol, $referent, $attr, $data ) = @_; tie $$referent, __PACKAGE__, $data;}sub TIESCALAR { my $class = shift; bless \(my $self = shift), $class;}sub FETCH { my $self = shift; return $$self % 2 == 0 ? 'even' : 'odd';}sub STORE { my $self = shift; $$self = shift;}1;
28/2/2008 Take control 47
Oorspronkelijke voorbeeld#! /usr/bin/perluse warnings;use strict;
use lib 'lib';use base 'Tie_OddEven';
my $oe :OddEven(0);while ( 1 ) { print "Number: "; chomp( my $input = <> ); last unless $input =~ /^-?\d+$/; $oe = $input; printf "$input is $oe (%d)\n", ${ tied $oe };}
28/2/2008 Take control 48
Gebruik UNIVERSAL attribute#! /usr/bin/perluse warnings;use strict;
use lib 'lib';use Universal_OddEven;
my $oe :OddEven(0);while ( 1 ) { print "Number: "; chomp( my $input = <> ); last unless $input =~ /^-?\d+$/; $oe = $input; printf "$input is $oe (%d)\n", ${ tied $oe };}
28/2/2008 Take control 49
Oorspronkelijke attributepackage Tie_OddEven;use strict;use warnings;
our $VERSION = 0.01;
use Attribute::Handlers;
sub OddEven :ATTR(SCALAR) { my( $pkg, $symbol, $referent, $attr, $data ) = @_; tie $$referent, __PACKAGE__, $data;}sub TIESCALAR { my $class = shift; bless \(my $self = shift), $class;}sub FETCH { my $self = shift; return $$self % 2 == 0 ? 'even' : 'odd';}sub STORE { my $self = shift; $$self = shift;}1;
28/2/2008 Take control 50
Een attribute en autotiepackage Auto_OddEven;use strict;use warnings;
our $VERSION = 0.01;
use Attribute::Handlers autotie => { '__CALLER__::OddEven' => __PACKAGE__};
sub TIESCALAR { my $class = shift; bless \(my $self = shift), $class;}sub FETCH { my $self = shift; return $$self % 2 == 0 ? 'even' : 'odd';}sub STORE { my $self = shift; $$self = shift;}1;
28/2/2008 Take control 51
Gebruik autotie attribute#! /usr/bin/perluse warnings;use strict;
use lib 'lib';use Auto_OddEven;
my $oe :OddEven(0);while ( 1 ) { print "Number: "; chomp( my $input = <> ); last unless $input =~ /^-?\d+$/; $oe = $input; printf "$input is $oe (%d)\n", ${ tied $oe };}
28/2/2008 Take control 52
Meer informatie
• perldoc Attribute::Handlers
28/2/2008 Take control 61
Vragen?
28/2/2008 Take control 62
Dank je wel!