|
Perl/FAQ/Классы, Объекты и связи
Материал из Wiki.crossplatform.ru
[править] Introduction
#-----------------------------
$object = {}; # hash reference
bless($object, "Data::Encoder"); # bless $object into Data::Encoder class
bless($object); # bless $object into current package
#-----------------------------
$obj = [3,5];
print ref($obj), " ", $obj->[1], "\n";
bless($obj, "Human::Cannibal");
print ref($obj), " ", $obj->[1], "\n";
ARRAY 5
Human::Cannibal 5
#-----------------------------
$obj->{Stomach} = "Empty"; # directly accessing an object's contents
$obj->{NAME} = "Thag"; # uppercase field name to make it stand out (optional)
#-----------------------------
$encoded = $object->encode("data");
#-----------------------------
$encoded = Data::Encoder->encode("data");
#-----------------------------
sub new {
my $class = shift;
my $self = {}; # allocate new hash for object
bless($self, $class);
return $self;
}
#-----------------------------
$object = Class->new();
#-----------------------------
$object = Class::new("Class");
#-----------------------------
sub class_only_method {
my $class = shift;
die "class method called on object" if ref $class;
# more code here
}
#-----------------------------
sub instance_only_method {
my $self = shift;
die "instance method called on class" unless ref $self;
# more code here
}
#-----------------------------
$lector = new Human::Cannibal;
feed $lector "Zak";
move $lector "New York";
#-----------------------------
$lector = Human::Cannibal->
new();
$lector->feed("Zak");
$lector->move("New York");
#-----------------------------
printf STDERR "stuff here\n";
#-----------------------------
move $obj->{FIELD}; # probably wrong
move $ary[$i]; # probably wrong
#-----------------------------
$obj->move->{FIELD}; # Surprise!
$ary->move->[$i]; # Surprise!
#-----------------------------
$obj->{FIELD}->
move()
; # Nope, you wish
$ary[$i]->
move;
# Nope, you wish
#-----------------------------
[править] Constructing an Object
#-----------------------------
sub new {
my $class = shift;
my $self = { };
bless($self, $class);
return $self;
}
#-----------------------------
sub new { bless( { }, shift ) }
#-----------------------------
sub new { bless({}) }
#-----------------------------
sub new {
my $self = { }; # allocate anonymous hash
bless($self);
# init two sample attributes/data members/fields
$self->{START} = time();
$self->{AGE} = 0;
return $self;
}
#-----------------------------
sub new {
my $classname = shift; # What class are we constructing?
my $self = {}; # Allocate new memory
bless($self, $classname); # Mark it of the right type
$self->{START} =
time();
# init data fields
$self->{AGE} =
0;
return $self; # And give it back
}
#-----------------------------
sub new {
my $classname = shift; # What class are we constructing?
my $self = {}; # Allocate new memory
bless($self, $classname); # Mark it of the right type
$self->_init(@_); # Call _init with remaining args
return $self;
}
# "private" method to initialize fields. It always sets START to
# the current time, and AGE to 0. If called with arguments, _init
# interprets them as key+value pairs to initialize the object with.
sub _init {
my $self = shift;
$self->{START} =
time();
$self->{AGE} = 0;
if (@_) {
my %extra = @_;
@$self{keys %extra} = values %extra;
}
}
#-----------------------------
[править] Destroying an Object
#-----------------------------
sub DESTROY {
my $self = shift;
printf("$self dying at %s\n", scalar localtime);
}
#-----------------------------
$self->{WHATEVER} = $self;
#-----------------------------
[править] Managing Instance Data
#-----------------------------
sub get_name {
my $self = shift;
return $self->{NAME};
}
sub set_name {
my $self = shift;
$self->{NAME} = shift;
}
#-----------------------------
sub name {
my $self = shift;
if (@_) { $self->{NAME} = shift }
return $self->{NAME};
}
#-----------------------------
sub age {
my $self = shift;
my $prev = $self->{AGE};
if (@_) { $self->{AGE} = shift }
return $prev;
}
# sample call of get and set: happy birthday!
$obj->age( 1 + $obj->age );
#-----------------------------
$him = Person->
new()
;
$him->{NAME} = "Sylvester";
$him->{AGE} = 23;
#-----------------------------
use Carp;
sub name {
my $self = shift;
return $self->{NAME} unless @_;
local $_ = shift;
croak "too many arguments" if @_;
if ($^W) {
/[^\s\w'-]/ && carp "funny characters in name"; #'
/\d/ && carp "numbers in name";
/\S+(\s+\S+)+/ || carp "prefer multiword name";
/\S/ || carp "name is blank";
}
s/(\w+)/\u\L$1/g; # enforce capitalization
$self->{NAME} = $_;
}
#-----------------------------
package Person;
# this is the same as before...
sub new {
my $that = shift;
my $class = ref($that) || $that;
my $self = {
NAME => undef,
AGE => undef,
PEERS => [],
};
bless($self, $class);
return $self;
}
use Alias qw(attr);
use vars qw($NAME $AGE @PEERS);
sub name {
my $self = attr shift;
if (@_) { $NAME = shift; }
return $NAME;
};
sub age {
my $self = attr shift;
if (@_) { $AGE = shift; }
return $AGE;
}
sub peers {
my $self = attr shift;
if (@_) { @PEERS = @_; }
return @PEERS;
}
sub exclaim {
my $self = attr shift;
return sprintf "Hi, I'm %s, age %d, working with %s",
$NAME, $AGE, join(", ", @PEERS);
}
sub happy_birthday {
my $self = attr shift;
return ++$AGE;
}
#-----------------------------
[править] Managing Class Data
#-----------------------------
package Person;
$Body_Count = 0;
sub population { return $Body_Count }
sub new { # constructor
$Body_Count++;
return bless({}, shift);
}
sub DESTROY { --$BodyCount } # destructor
# later, the user can say this:
package main;
for (1..10) { push @people, Person->new }
printf "There are %d people alive.\n", Person->population();
There are 10 people alive.
#-----------------------------
$him = Person->
new()
;
$him->gender("male");
$her = Person->
new()
;
$her->gender("female");
#-----------------------------
FixedArray->Max_Bounds(100); # set for whole class
$alpha = FixedArray->new();
printf "Bound on alpha is %d\n", $alpha->Max_Bounds();
100
$beta = FixedArray->new();
$beta->Max_Bounds(50); # still sets for whole class
printf "Bound on alpha is %d\n", $alpha->Max_Bounds();
50
#-----------------------------
package FixedArray;
$Bounds = 7; # default
sub new { bless( {}, shift ) }
sub Max_Bounds {
my $proto = shift;
$Bounds = shift if @_; # allow updates
return $Bounds;
}
#-----------------------------
sub Max_Bounds { $Bounds }
#-----------------------------
sub new {
my $class = shift;
my $self = bless({}, $class);
$self->{Max_Bounds_ref} = \$Bounds;
return $self;
}
#-----------------------------
[править] Using Classes as Structs
#-----------------------------
use Class::Struct; # load struct-building module
struct Person => { # create a definition for a "Person"
name => '$', # name field is a scalar
age => '$', # age field is also a scalar
peers => '@', # but peers field is an array (reference)
};
my $p = Person->
new()
; # allocate an empty Person struct
$p->name("Jason Smythe"); # set its name field
$p->age(13); # set its age field
$p->peers( ["Wilbur", "Ralph", "Fred" ] ); # set its peers field
# or this way:
@{$p->peers} = ("Wilbur", "Ralph", "Fred");
# fetch various values, including the zeroth friend
printf "At age %d, %s's first friend is %s.\n",
$p->age, $p->name, $p->peers(0);
#-----------------------------
use Class::Struct;
struct Person => {name => '$', age => '$'}; #'
struct Family => {head => 'Person', address => '$', members => '@'}; #'
$folks = Family->
new();
$dad = $folks->head;
$dad->name("John");
$dad->age(34);
printf("%s's age is %d\n", $folks->head->name, $folks->head->age);
#-----------------------------
sub Person::age {
use Carp;
my ($self, $age) = @_;
if (@_ > 2) { confess "too many arguments" }
elsif (@_ == 1) { return $struct->{'age'} }
elsif (@_ == 2) {
carp "age `$age' isn't numeric" if $age !~ /^\d+/;
carp "age `$age' is unreasonable" if $age > 150;
$self->{'age'} = $age;
}
}
#-----------------------------
if ($^W) {
carp "age `$age' isn't numeric" if $age !~ /^\d+/;
carp "age `$age' is unreasonable" if $age > 150;
}
#-----------------------------
my $gripe = $^W ? \&carp : \&croak;
$gripe->("age `$age' isn't numeric") if $age !~ /^\d+/;
$gripe->("age `$age' is unreasonable") if $age > 150;
#-----------------------------
struct Family => [head => 'Person', address => '$', members => '@']; #'
#-----------------------------
struct Card => {
name => '$',
color => '$',
cost => '$',
type => '$',
release => '$',
text => '$',
};
#-----------------------------
struct Card => map { $_ => '$' } qw(name color cost type release text); #'
#-----------------------------
struct hostent => { reverse qw{
$ name
@ aliases
$ addrtype
$ length
@ addr_list
}};
#-----------------------------
#define h_type h_addrtype
#define h_addr h_addr_list[0]
#-----------------------------
# make (hostent object)->
type()
same as (hostent object)->
addrtype()
*hostent::type = \&hostent::addrtype;
# make (hostenv object)->
addr()
same as (hostenv object)->addr_list(0)
sub hostent::addr { shift->addr_list(0,@_) }
#-----------------------------
package Extra::hostent;
use Net::hostent;
@ISA = qw(hostent);
sub addr { shift->addr_list(0,@_) }
1;
#-----------------------------
[править] Cloning Objects
#-----------------------------
my $proto = shift;
my $class = ref($proto) || $proto;
my $parent = ref($proto) && $proto;
#-----------------------------
$ob1 = SomeClass->
new()
;
# later on
$ob2 = (ref $ob1)->
new();
#-----------------------------
$ob1 = Widget->new();
$ob2 = $ob1->new();
#-----------------------------
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $parent = ref($proto) && $proto;
my $self;
# check whether we're shadowing a new from @ISA
if (@ISA && $proto->SUPER::can('new') ) {
$self = $proto->SUPER::new(@_);
} else {
$self = {};
bless ($self, $proto);
}
bless($self, $class);
$self->{PARENT} = $parent;
$self->{START} = time(); # init data fields
$self->{AGE} = 0;
return $self;
}
#-----------------------------
[править] Calling Methods Indirectly
#-----------------------------
$methname = "flicker";
$obj->$methname(10); # calls $obj->flicker(10);
# call three methods on the object, by name
foreach $m ( qw(start run stop) ) {
$obj->
$m();
}
#-----------------------------
@methods = qw(name rank serno);
%his_info = map { $_ => $ob->$_() } @methods;
# same as this:
%his_info = (
'name' => $ob->
name()
,
'rank' => $ob->
rank()
,
'serno' => $ob->
serno()
,
);
#-----------------------------
my $fnref = sub { $ob->method(@_) };
#-----------------------------
$fnref->(10, "fred");
#-----------------------------
$obj->method(10, "fred");
#-----------------------------
$obj->can('method_name')->($obj_target, @arguments)
if $obj_target->isa( ref $obj );
#-----------------------------
[править] Determining Subclass Membership
#-----------------------------
$obj->isa("HTTP::Message"); # as object method
HTTP::Response->isa("HTTP::Message"); # as class method
if ($obj->can("method_name")) { .... } # check method validity
#-----------------------------
$has_io = $fd->isa("IO::Handle");
$itza_handle = IO::Socket->isa("IO::Handle");
#-----------------------------
$his_print_method = $obj->can('as_string');
#-----------------------------
Some_Module->VERSION(3.0);
$his_vers = $obj->
VERSION()
;
#-----------------------------
use Some_Module 3.0;
#-----------------------------
use vars qw($VERSION);
$VERSION = '1.01';
#-----------------------------
[править] Writing an Inheritable Class
#-----------------------------
package Person;
sub new {
my $class = shift;
my $self = { };
return bless $self, $class;
}
sub name {
my $self = shift;
$self->{NAME} = shift if @_;
return $self->{NAME};
}
sub age {
my $self = shift;
$self->{AGE} = shift if @_;
return $self->{AGE};
}
#-----------------------------
use Person;
my $dude = Person->
new()
;
$dude->name("Jason");
$dude->age(23);
printf "%s is age %d.\n", $dude->name, $dude->age;
#-----------------------------
package Employee;
use Person;
@ISA = ("Person");
1;
#-----------------------------
use Employee;
my $empl = Employee->
new()
;
$empl->name("Jason");
$empl->age(23);
printf "%s is age %d.\n", $empl->name, $empl->age;
#-----------------------------
$him = Person::
new()
; # WRONG
#-----------------------------
[править] Accessing Overridden Methods
#-----------------------------
sub meth {
my $self = shift;
$self->SUPER::
meth()
;
}
#-----------------------------
$self->
meth();
# Call wherever first meth is found
$self->Where::
meth();
# Start looking in package "Where"
$self->SUPER::
meth();
# Call overridden version
#-----------------------------
sub new {
my $classname = shift; # What class are we constructing?
my $self = $classname->SUPER::new(@_);
$self->_init(@_);
return $self; # And give it back
}
sub _init {
my $self = shift;
$self->{START} = time(); # init data fields
$self->{AGE} = 0;
$self->{EXTRA} = { @_ }; # anything extra
}
#-----------------------------
$obj = Widget->new( haircolor => red, freckles => 121 );
#-----------------------------
my $self = bless {}, $class;
for my $class (@ISA) {
my $meth = $class . "::_init";
$self->$meth(@_) if $class->can("_init");
}
#-----------------------------
[править] Generating Attribute Methods Using AUTOLOAD
#-----------------------------
package Person;
use strict;
use Carp;
use vars qw($AUTOLOAD %ok_field);
# Authorize four attribute fields
for my $attr ( qw(name age peers parent) ) { $ok_field{$attr}++; }
sub AUTOLOAD {
my $self = shift;
my $attr = $AUTOLOAD;
$attr =~ s/.*:://;
return unless $attr =~ /[^A-Z]/; # skip DESTROY and all-cap methods
croak "invalid attribute method: ->
$attr()"
unless $ok_field{$attr};
$self->{uc $attr} = shift if @_;
return $self->{uc $attr};
}
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $parent = ref($proto) && $proto;
my $self = {};
bless($self, $class);
$self->parent($parent);
return $self;
}
1;
#-----------------------------
use Person;
my ($dad, $kid);
$dad = Person->new;
$dad->name("Jason");
$dad->age(23);
$kid = $dad->new;
$kid->name("Rachel");
$kid->age(2);
printf "Kid's parent is %s\n", $kid->parent->name;
#Kid's parent is Jason
#-----------------------------
sub AUTOLOAD {
my $self = shift;
my $attr = $AUTOLOAD;
$attr =~ s/.*:://;
return if $attr eq 'DESTROY';
if ($ok_field{$attr}) {
$self->{uc $attr} = shift if @_;
return $self->{uc $attr};
} else {
my $superior = "SUPER::$attr";
$self->$superior(@_);
}
}
#-----------------------------
[править] Solving the Data Inheritance Problem
#-----------------------------
sub Employee::age {
my $self = shift;
$self->{Employee_age} = shift if @_;
return $self->{Employee_age};
}
#-----------------------------
package Person;
use Class::Attributes; # see explanation below
mkattr qw(name age peers parent);
#-----------------------------
package Employee;
@ISA = qw(Person);
use Class::Attributes;
mkattr qw(salary age boss);
#-----------------------------
package Class::Attributes;
use strict;
use Carp;
use Exporter ();
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(mkattr);
sub mkattr {
my $hispack = caller();
for my $attr (@_) {
my($field, $method);
$method = "${hispack}::$attr";
($field = $method) =~ s/:/_/g;
no strict 'refs'; # here comes the kluglich bit
*$method = sub {
my $self = shift;
confess "too many arguments" if @_ > 1;
$self->{$field} = shift if @_;
return $self->{$field};
};
}
}
1;
#-----------------------------
[править] Coping with Circular Data Structures
#-----------------------------
$node->{NEXT} = $node;
#-----------------------------
package Ring;
# return an empty ring structure
sub new {
my $class = shift;
my $node = { };
$node->{NEXT} = $node->{PREV} = $node;
my $self = { DUMMY => $node, COUNT => 0 };
bless $self, $class;
return $self;
}
#-----------------------------
use Ring;
$COUNT = 1000;
for (1 .. 20) {
my $r = Ring->
new()
;
for ($i = 0; $i < $COUNT; $i++) { $r->insert($i) }
}
#-----------------------------
# when a Ring is destroyed, destroy the ring structure it contains
sub DESTROY {
my $ring = shift;
my $node;
for ( $node = $ring->{DUMMY}->{NEXT};
$node != $ring->{DUMMY};
$node = $node->{NEXT} )
{
$ring->delete_node($node);
}
$node->{PREV} = $node->{NEXT} = undef;
}
# delete a node from the ring structure
sub delete_node {
my ($ring, $node) = @_;
$node->{PREV}->{NEXT} = $node->{NEXT};
$node->{NEXT}->{PREV} = $node->{PREV};
--$ring->{COUNT};
}
#-----------------------------
# $node = $ring->search( $value ) : find $value in the ring
# structure in $node
sub search {
my ($ring, $value) = @_;
my $node = $ring->{DUMMY}->{NEXT};
while ($node != $ring->{DUMMY} && $node->{VALUE} != $value) {
$node = $node->{NEXT};
}
return $node;
}
# $ring->insert( $value ) : insert $value into the ring structure
sub insert {
my ($ring, $value) = @_;
my $node = { VALUE => $value };
$node->{NEXT} = $ring->{DUMMY}->{NEXT};
$ring->{DUMMY}->{NEXT}->{PREV} = $node;
$ring->{DUMMY}->{NEXT} = $node;
$node->{PREV} = $ring->{DUMMY};
++$ring->{COUNT};
}
# $ring->delete_value( $value ) : delete a node from the ring
# structure by value
sub delete_value {
my ($ring, $value) = @_;
my $node = $ring->search($value);
return if $node == $ring->{DUMMY};
$ring->delete_node($node);
}
1;
#-----------------------------
[править] Overloading Operators
#-----------------------------
use overload ('<=>' => \&threeway_compare);
sub threeway_compare {
my ($s1, $s2) = @_;
return uc($s1->{NAME}) cmp uc($s2->{NAME});
}
use overload ( '""' => \&stringify );
sub stringify {
my $self = shift;
return sprintf "%s (%05d)",
ucfirst(lc($self->{NAME})),
$self->{IDNUM};
}
#-----------------------------
package TimeNumber;
use overload '+' => \&my_plus,
'-' => \&my_minus,
'*' => \&my_star,
'/' => \&my_slash;
#-----------------------------
sub my_plus {
my($left, $right) = @_;
my $answer = $left->
new();
$answer->{SECONDS} = $left->{SECONDS} + $right->{SECONDS};
$answer->{MINUTES} = $left->{MINUTES} + $right->{MINUTES};
$answer->{HOURS} = $left->{HOURS} + $right->{HOURS};
if ($answer->{SECONDS} >= 60) {
$answer->{SECONDS} %= 60;
$answer->{MINUTES} ++;
}
if ($answer->{MINUTES} >= 60) {
$answer->{MINUTES} %= 60;
$answer->{HOURS} ++;
}
return $answer;
}
#-----------------------------
#!/usr/bin/perl
# show_strnum - demo operator overloading
use StrNum;
$x = StrNum("Red"); $y = StrNum("Black");
$z = $x + $y; $r = $z * 3;
print "values are $x, $y, $z, and $r\n";
print "$x is ", $x < $y ? "LT" : "GE", " $y\n";
# values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
# Red is GE Black
#-----------------------------
# download the following standalone program
package StrNum;
use Exporter ();
@ISA = 'Exporter';
@EXPORT = qw(StrNum); # unusual
use overload (
'<=>' => \&spaceship,
'cmp' => \&spaceship,
'""' => \&stringify,
'bool' => \&boolify,
'0+' => \&numify,
'+' => \&concat,
'*' => \&repeat,
);
# constructor
sub StrNum($) {
my ($value) = @_;
return bless \$value;
}
sub stringify { ${ $_[0] } }
sub numify { ${ $_[0] } }
sub boolify { ${ $_[0] } }
# providing <=> gives us <, ==, etc. for free.
sub spaceship {
my ($s1, $s2, $inverted) = @_;
return $inverted ? $$s2 cmp $$s1 : $$s1 cmp $$s2;
}
# this uses stringify
sub concat {
my ($s1, $s2, $inverted) = @_;
return StrNum $inverted ? ($s2 . $s1) : ($s1 . $s2);
}
# this uses stringify
sub repeat {
my ($s1, $s2, $inverted) = @_;
return StrNum $inverted ? ($s2 x $s1) : ($s1 x $s2);
}
1;
#-----------------------------
#!/usr/bin/perl
# demo_fixnum - show operator overloading
use FixNum;
FixNum->places(5);
$x = FixNum->new(40);
$y = FixNum->new(12);
print "sum of $x and $y is ", $x + $y, "\n";
print "product of $x and $y is ", $x * $y, "\n";
$z = $x / $y;
printf "$z has %d places\n", $z->places;
$z->places(2) unless $z->places;
print "div of $x by $y is $z\n";
print "square of that is ", $z * $z, "\n";
sum of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 52
product of STRFixNum: 40 and STRFixNum: 12 is STRFixNum: 480
STRFixNum: 3 has 0 places
div of STRFixNum: 40 by STRFixNum: 12 is STRFixNum: 3.33
square of that is STRFixNum: 11.11
#-----------------------------
# download the following standalone program
package FixNum;
use strict;
my $PLACES = 0;
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $parent = ref($proto) && $proto;
my $v = shift;
my $self = {
VALUE => $v,
PLACES => undef,
};
if ($parent && defined $parent->{PLACES}) {
$self->{PLACES} = $parent->{PLACES};
} elsif ($v =~ /(\.\d*)/) {
$self->{PLACES} = length($1) - 1;
} else {
$self->{PLACES} = 0;
}
return bless $self, $class;
}
sub places {
my $proto = shift;
my $self = ref($proto) && $proto;
my $type = ref($proto) || $proto;
if (@_) {
my $places = shift;
($self ? $self->{PLACES} : $PLACES) = $places;
}
return $self ? $self->{PLACES} : $PLACES;
}
sub _max { $_[0] > $_[1] ? $_[0] : $_[1] }
use overload '+' => \&add,
'*' => \&multiply,
'/' => \÷,
'<=>' => \&spaceship,
'""' => \&as_string,
'0+' => \&as_number;
sub add {
my ($this, $that, $flipped) = @_;
my $result = $this->new( $this->{VALUE} + $that->{VALUE} );
$result->places( _max($this->{PLACES}, $that->{PLACES} ));
return $result;
}
sub multiply {
my ($this, $that, $flipped) = @_;
my $result = $this->new( $this->{VALUE} * $that->{VALUE} );
$result->places( _max($this->{PLACES}, $that->{PLACES} ));
return $result;
}
sub divide {
my ($this, $that, $flipped) = @_;
my $result = $this->new( $this->{VALUE} / $that->{VALUE} );
$result->places( _max($this->{PLACES}, $that->{PLACES} ));
return $result;
}
sub as_string {
my $self = shift;
return sprintf("STR%s: %.*f", ref($self),
defined($self->{PLACES}) ? $self->{PLACES} : $PLACES,
$self->{VALUE});
}
sub as_number {
my $self = shift;
return $self->{VALUE};
}
sub spaceship {
my ($this, $that, $flipped) = @_;
$this->{VALUE} <=> $that->{VALUE};
}
1;
#-----------------------------
[править] Creating Magic Variables with tie
#-----------------------------
tie $s, "SomeClass"
#-----------------------------
SomeClass->
TIESCALAR()
#-----------------------------
$p = $s
#-----------------------------
$p = $obj->
FETCH()
#-----------------------------
$s = 10
#-----------------------------
$obj->STORE(10)
#-----------------------------
#!/usr/bin/perl
# demo_valuering - show tie class
use ValueRing;
tie $color, 'ValueRing', qw(red blue);
print "$color $color $color $color $color $color\n";
red blue red blue red blue
$color = 'green';
print "$color $color $color $color $color $color\n";
green red blue green red blue
#-----------------------------
# download the following standalone program
package ValueRing;
# this is the constructor for scalar ties
sub TIESCALAR {
my ($class, @values) = @_;
bless \@values, $class;
return \@values;
}
# this intercepts read accesses
sub FETCH {
my $self = shift;
push(@$self, shift(@$self));
return $self->[-1];
}
# this intercepts write accesses
sub STORE {
my ($self, $value) = @_;
unshift @$self, $value;
return $value;
}
1;
#-----------------------------
no UnderScore;
#-----------------------------
#!/usr/bin/perl
# nounder_demo - show how to ban $_ from your program
no UnderScore;
@tests = (
"Assignment" => sub { $_ = "Bad" },
"Reading" => sub { print },
"Matching" => sub { $x = /badness/ },
"Chop" => sub { chop },
"Filetest" => sub { -x },
"Nesting" => sub { for (1..3) { print } },
);
while ( ($name, $code) = splice(@tests, 0, 2) ) {
print "Testing $name: ";
eval { &$code };
print $@ ? "detected" : "missed!";
print "\n";
}
#-----------------------------
Testing Assignment: detected
Testing Reading: detected
Testing Matching: detected
Testing Chop: detected
Testing Filetest: detected
Testing Nesting: 123missed!
#-----------------------------
# download the following standalone program
package UnderScore;
use Carp;
sub TIESCALAR {
my $class = shift;
my $dummy;
return bless \$dummy => $class;
}
sub FETCH { croak "Read access to \$_ forbidden" }
sub STORE { croak "Write access to \$_ forbidden" }
sub unimport { tie($_, _ _PACKAGE_ _) }
sub import { untie $_ }
tie($_, _ _PACKAGE_ _) unless tied $_;
1;
#-----------------------------
#!/usr/bin/perl
# appendhash_demo - show magic hash that autoappends
use Tie::AppendHash;
tie %tab, 'Tie::AppendHash';
$tab{beer} = "guinness";
$tab{food} = "potatoes";
$tab{food} = "peas";
while (my($k, $v) = each %tab) {
print "$k => [@$v]\n";
}
#-----------------------------
food => [potatoes peas]
beer => [guinness]
#-----------------------------
# download the following standalone program
package Tie::AppendHash;
use strict;
use Tie::Hash;
use Carp;
use vars qw(@ISA);
@ISA = qw(Tie::StdHash);
sub STORE {
my ($self, $key, $value) = @_;
push @{$self->{key}}, $value;
}
1;
#-----------------------------
#!/usr/bin/perl
# folded_demo - demo hash that magically folds case
use Tie::Folded;
tie %tab, 'Tie::Folded';
$tab{VILLAIN} = "big ";
$tab{herOine} = "red riding hood";
$tab{villain} .= "bad wolf";
while ( my($k, $v) = each %tab ) {
print "$k is $v\n";
}
#-----------------------------
heroine is red riding hood
villain is big bad wolf
#-----------------------------
# download the following standalone program
package Tie::Folded;
use strict;
use Tie::Hash;
use vars qw(@ISA);
@ISA = qw(Tie::StdHash);
sub STORE {
my ($self, $key, $value) = @_;
return $self->{lc $key} = $value;
}
sub FETCH {
my ($self, $key) = @_;
return $self->{lc $key};
}
sub EXISTS {
my ($self, $key) = @_;
return exists $self->{lc $key};
}
sub DEFINED {
my ($self, $key) = @_;
return defined $self->{lc $key};
}
1;
#-----------------------------
#!/usr/bin/perl -w
# revhash_demo - show hash that permits key *or* value lookups
use strict;
use Tie::RevHash;
my %tab;
tie %tab, 'Tie::RevHash';
%tab = qw{
Red Rojo
Blue Azul
Green Verde
};
$tab{EVIL} = [ "No way!", "Way!!" ];
while ( my($k, $v) = each %tab ) {
print ref($k) ? "[@$k]" : $k, " => ",
ref($v) ? "[@$v]" : $v, "\n";
}
#-----------------------------
[No way! Way!!] => EVIL
EVIL => [No way! Way!!]
Blue => Azul
Green => Verde
Rojo => Red
Red => Rojo
Azul => Blue
Verde => Green
#-----------------------------
# download the following standalone program
package Tie::RevHash;
use Tie::RefHash;
use vars qw(@ISA);
@ISA = qw(Tie::RefHash);
sub STORE {
my ($self, $key, $value) = @_;
$self->SUPER::STORE($key, $value);
$self->SUPER::STORE($value, $key);
}
sub DELETE {
my ($self, $key) = @_;
my $value = $self->SUPER::FETCH($key);
$self->SUPER::DELETE($key);
$self->SUPER::DELETE($value);
}
1;
#-----------------------------
use Counter;
tie *CH, 'Counter';
while (<CH>) {
print "Got $_\n";
}
#-----------------------------
# download the following standalone program
package Counter;
sub TIEHANDLE {
my $class = shift;
my $start = shift;
return bless \$start => $class;
}
sub READLINE {
my $self = shift;
return ++$$self;
}
1;
#-----------------------------
use Tie::Tee;
tie *TEE, 'Tie::Tee', *STDOUT, *STDERR;
print TEE "This line goes both places.\n";
#-----------------------------
#!/usr/bin/perl
# demo_tietee
use Tie::Tee;
use Symbol;
@handles = (*STDOUT);
for $i ( 1 .. 10 ) {
push(@handles, $handle = gensym());
open($handle, ">/tmp/teetest.$i");
}
tie *TEE, 'Tie::Tee', @handles;
print TEE "This lines goes many places.\n";
#-----------------------------
# download the following standalone program
package Tie::Tee;
sub TIEHANDLE {
my $class = shift;
my $handles = [@_];
bless $handles, $class;
return $handles;
}
sub PRINT {
my $href = shift;
my $handle;
my $success = 0;
foreach $handle (@$href) {
$success += print $handle @_;
}
return $success == @$href;
}
1;
#-----------------------------
|