Search This Blog

Friday, June 12, 2020

PERL cheat sheet

                      

Perl Cheat Sheet

Get information on a function by typing, e.g., perldoc -f chomp at the command line.
Scalar variables
while (defined ($x=<>)) {code}
False if variable has never been set (or when you try to read past the end of an input file)
length($x)
Length of a string
chomp($line);
Remove a newline at the end of a string
$short=substr ($long, 2, 5)
Characters 3-7 of $long (first char is 0!)
Arrays
push @arr, $x
Add to end of array
$x = pop @arr;
Remove last element of array, put in $x
shift @arr;  (See also unshift)
Remove first element of an arrray, put in $x
$size = scalar @arr;
Number of things in the array
See also: split, join,
splice, sort
split string->array, join array->string,
delete part of array, sort array in many ways
Hashes
@key = keys %hash
The lookup terms in the hash
if (exists $hh{“Hsp”}) {...}
See whether hash %hh has a value for that key
Input/Output and Files
open(HANDLE, ">outfile") or die “Can’t open $outfile: $!\n”
Open outfile for writing, and associate it with filehandle HANDLE. Use “<infile” for reading
print $x;
print HANDLE $x;
Prints to standard output (screen),
Print to filehandle HANDLE
warn “Something wrong\n”;
Prints to standard error (screen)
$x=<HANDLE>
Read a line from filehandle HANDLE, put in $x
close(HANDLE);
Stop reading/writing a previously opened file
Exit
exit;
Exits the program
die "Something broke!\n";
Exits the program with error message

Operators and Loops

Assign value
$x = 1
Sets variable to a value. Don’t confuse with ==, which tests whether numerical values are equal
Math
print 1 * (2 + 3/4)
Regular math symbols
10%3==1; 12%3==0
Modulus (remainder) operator
$x += 4;
Same as $x=$x=4; Also -= *= /=
$x++;
Same as $x=$x+1;
Conditions
if (.1 == 0.1) {print “same num”}
Are numbers equal? Don’t confuse with =
if (1 != 2) {print “diff num”}
Are numbers different?
> < >= <=
Are numbers greater than, less than, etc.
if (“a” eq “a”) {print “same text”}
Does text have exactly the same letters?
if (“A” ne “a”) {print “diff text”}
Does text have different letters?
if (($x > 1) && ($x < 2)) {code}
AND (true if both sides are true)
if (($x > 10) || ($x < -10)) {code}
OR (true if one or both sides are true)
=~ !~
Test for a match: See Matching cheat sheet
Loops
foreach $i (1 .. 100) {code}
(for and foreach are equivalent)
Sets $i to 1 and does code. Sets $i to 2, …
up to (and including) 100

while ($a < $b) {code}
Does code while the condition is true
(If condition is false, never enters the loop.)


Matching and Regular Expressions

Test for Match
=~
Test for match
if ($x =~ /abc/) {
    ...}
Does $x have the string “abc” anywhere in it?
!~
Test for non-match
if ($x !~ /abc/) {
    ...}
Does $x NOT have the string “abc” anywhere in it?
$_
Default variable
if (/abcd/) {
    s/bc/x/ }
// and s/// work on $_ by default, no =~ needed
Substitute
s///
Do a Substitution
$x =~ s/abc/def/;
Replace (only) first occurrence of “abc” in $x with def
Options to Search / Substitution
i
Ignore case.
/abc/i
Matches abc, ABC, aBc, etc.
g
Global substitution.
s/a/c/g
Replace ALL occurrences
Special Match Items
.
Any one character (except \n)
/a.c/
“arc”, “a9c”, but not “ac”.
[ ]
Any one of.
/[abc]/
Any one of “a”, “b”, or “c”.  [a-zA-Z] matches any letter
\d
Digit (Same as [0-9])
/\d\d:\d\d/
“10:30” (but not “9:30”)
\s
Space, tab, or newline
/^\s*$/
An empty line.
\
Literally match special characters:  + * ( ) / [ ] \ | { } ^ $ @
/1\+2/
“1+2”, not “1112”. The backslash “quotes” or “escapes” the plus sign.
Item Locations
^
Beginning of a line
/^a/
"arginine" but not "valine”.
$
End of a line
/a$/
"beta" but not "beat".
Item Repetitions
?
An optional thing
/ab?c/
“ac” or “abc”.
*
Any number of copies OR nothing at all
/a*/
"", "a", "aaa".
+
Any number of copies
/a+b/
"ab" or "aaab" but not "b".
{ }
m to n copies
/ab{2,4}c/
“abbc”, “abbbc”, “abbbbc”, but not “abc” or “abbbbbc”
Misc
|
One or the other
/abc|def/
“abc” or “def”
( )
Group things together AND capture in numbered variables
/a(b(..)e)f/
“abcdef”. This will also set $1 to “c” and $2 to “bcde”.


#####################################
1:An Overview of Perl/Getting Started
#####################################

print "Howdy, world!\n";

######################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax
######################################################################

$phrase = "Howdy, world!\n";          # Set a variable.
print $phrase;                        # Print the variable.

####################################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax/Singularities
####################################################################################

$answer = 42;                # an integer
$pi = 3.14159265;            # a "real" number
$avocados = 6.02e23;         # scientific notation
$pet = "Camel";              # string
$sign = "I love my $pet";    # string with interpolation
$cost = 'It costs $100';     # string without interpolation
$thence = $whence;           # another variable's value
$salsa = $moles * $avocados; # a gastrochemical expression
$exit = system("vi $file");  # numeric status of a command
$cwd = `pwd`;                # string output from a command

--------------

$ary = \@myarray;            # reference to a named array
$hsh = \%myhash;             # reference to a named hash
$sub = \&mysub;              # reference to a named subroutine

$ary = [1,2,3,4,5];          # reference to an unnamed array
$hsh = {Na => 19, Cl => 35}; # reference to an unnamed hash
$sub = sub { print $state }; # reference to an unnamed subroutine

$fido = new Camel "Amelia";  # ref to an object

--------------

$camels = '123';
print $camels + 1, "\n";

--------------

$fido = new Camel "Amelia";
if (not $fido) { die "dead camel"; }
$fido->saddle();

##########################################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax/Pluralities/Arrays.
##########################################################################################

@home = ("couch", "chair", "table", "stove");

--------------

($potato, $lift, $tennis, $pipe) = @home;

--------------

($alpha,$omega) = ($omega,$alpha);

--------------

$home[0] = "couch";
$home[1] = "chair";
$home[2] = "table";
$home[3] = "stove";

##########################################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax/Pluralities/Hashes.
##########################################################################################

%longday = ("Sun", "Sunday", "Mon", "Monday", "Tue", "Tuesday",
            "Wed", "Wednesday", "Thu", "Thursday", "Fri",
            "Friday", "Sat", "Saturday");

--------------

%longday = (
    "Sun" => "Sunday",
    "Mon" => "Monday",
    "Tue" => "Tuesday",
    "Wed" => "Wednesday",
    "Thu" => "Thursday",
    "Fri" => "Friday",
    "Sat" => "Saturday",
);

--------------

$wife{"Adam"} = "Eve";

###################################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax/Complexities
###################################################################################

$wife{"Jacob"} = ("Leah", "Rachel", "Bilhah", "Zilpah");

--------------

$wife{"Jacob"} = ["Leah", "Rachel", "Bilhah", "Zilpah"];

--------------

$wife{"Jacob"}[0] = "Leah";
$wife{"Jacob"}[1] = "Rachel";
$wife{"Jacob"}[2] = "Bilhah";
$wife{"Jacob"}[3] = "Zilpah";

--------------

$kids_of_wife{"Jacob"} = {
    "Leah"   => ["Reuben", "Simeon", "Levi",
                 "Judah", "Issachar", "Zebulun"],
    "Rachel" => ["Joseph", "Benjamin"],
    "Bilhah" => ["Dan", "Naphtali"],
    "Zilpah" => ["Gad", "Asher"],
};

--------------

$kids_of_wife{"Jacob"}{"Leah"}[0]   = "Reuben";
$kids_of_wife{"Jacob"}{"Leah"}[1]   = "Simeon";
$kids_of_wife{"Jacob"}{"Leah"}[2]   = "Levi";
$kids_of_wife{"Jacob"}{"Leah"}[3]   = "Judah";
$kids_of_wife{"Jacob"}{"Leah"}[4]   = "Issachar";
$kids_of_wife{"Jacob"}{"Leah"}[5]   = "Zebulun";
$kids_of_wife{"Jacob"}{"Rachel"}[0] = "Joseph";
$kids_of_wife{"Jacob"}{"Rachel"}[1] = "Benjamin";
$kids_of_wife{"Jacob"}{"Bilhah"}[0] = "Dan";
$kids_of_wife{"Jacob"}{"Bilhah"}[1] = "Naphtali";
$kids_of_wife{"Jacob"}{"Zilpah"}[0] = "Gad";
$kids_of_wife{"Jacob"}{"Zilpah"}[1] = "Asher";

--------------

$fido = new Camel "Amelia";

###################################################################################
1:An Overview of Perl/Natural and Artificial Languages/Variable Syntax/Simplicities
###################################################################################

package Camel;

--------------

package Camel;
$fido = &fetch();

--------------

package Dog;
$fido = &fetch();

--------------

$fido = new Camel "Amelia";

--------------

$fido->saddle();

--------------

use Camel;

--------------

$fido = new Camel "Amelia";

--------------

use Some::Cool::Module;

--------------

use strict;

############################################################
1:An Overview of Perl/Natural and Artificial Languages/Verbs
############################################################

print "Adam's wife is $wife{'Adam'}.\n";

--------------

$e = exp(1);   # 2.718281828459 or thereabouts

########################################
1:An Overview of Perl/An Average Example
########################################

#!/usr/bin/perl

open(GRADES, "grades") or die "Can't open grades: $!\n";
while ($line = <GRADES>) {
    ($student, $grade) = split(" ", $line);
    $grades{$student} .= $grade . " ";
}

foreach $student (sort keys %grades) {
    $scores = 0;
    $total = 0;    
    @grades = split(" ", $grades{$student});
    foreach $grade (@grades) {
        $total += $grade;
        $scores++;
    }
    $average = $total / $scores;
    print "$student: $grades{$student}\tAverage: $average\n";
}

#####################################################
1:An Overview of Perl/An Average Example/How to Do It
#####################################################

% perl -e 'print "Hello, world!\n";'

--------------

% perl gradation

--------------

#!/usr/bin/perl

--------------

% gradation

--------------

% ../bin/gradation

--------------

#!/bin/sh -- # perl, to stop looping
eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
    if 0;

--------------

#!/usr/bin/perl -w

#################################
1:An Overview of Perl/Filehandles
#################################

open(SESAME, "filename")               # read from existing file
open(SESAME, "<filename")              #   (same thing, explicitly)
open(SESAME, ">filename")              # create file and write to it
open(SESAME, ">>filename")             # append to existing file
open(SESAME, "| output-pipe-command")  # set up an output filter
open(SESAME, "input-pipe-command |")   # set up an input filter

--------------

print STDOUT "Enter a number: ";          # ask for a number
$number = <STDIN>;                        # input the number
print STDOUT "The number is $number.\n";  # print the number

--------------

chop($number = <STDIN>);    # input number and remove newline

--------------

$number = <STDIN>;          # input number
chop($number);              # remove newline

################################################
1:An Overview of Perl/Operators/String Operators
################################################

$a = 123;
$b = 456;
print $a + $b;     # prints 579
print $a . $b;     # prints 123456

--------------

$a = 123;
$b = 3;
print $a * $b;     # prints 369
print $a x $b;     # prints 123123123

--------------

print $a . ' is equal to ' . $b . ".\n";    # dot operator
print $a, ' is equal to ', $b, ".\n";       # list
print "$a is equal to $b.\n";               # interpolation

--------------

print "-" x $scrwid, "\n";

####################################################
1:An Overview of Perl/Operators/Assignment Operators
####################################################

$a = $b;
$a = $b + 5;
$a = $a * 3;

--------------

$a *= 3;

--------------

$line .= "\n";  # Append newline to $line.
$fill x= 80;    # Make string $fill into 80 repeats of itself.
$val ||= "2";   # Set $val to 2 if it isn't already "true".

--------------

$a = $b = $c = 0;

--------------

($temp -= 32) *= 5/9;

--------------

chop($number = <STDIN>);

##########################################################
1:An Overview of Perl/Operators/Unary Arithmetic Operators
##########################################################

$a = 5;        # $a is assigned 5
$b = ++$a;     # $b is assigned the incremented value of $a, 6
$c = $a--;     # $c is assigned 6, then $a is decremented to 5

#################################################
1:An Overview of Perl/Operators/Logical Operators
#################################################

open(GRADES, "grades") or die "Can't open file grades: $!\n";

########################################################
1:An Overview of Perl/Operators/Some File Test Operators
########################################################

-e "/usr/bin/perl" or warn "Perl is improperly installed\n";
-f "/vmlinuz" and print "I see you are a friend of Linus\n";

#######################################################
1:An Overview of Perl/Control Structures/What Is Truth?
#######################################################

0          # would become the string "0", so false.
1          # would become the string "1", so true.
10 - 10    # 10-10 is 0, would convert to string "0", so false.
0.00       # equals 0, would convert to string "0", so false.
"0"        # the string "0", so false.
""         # a null string, so false.
"0.00"     # the string "0.00", neither "" nor "0", so true!
"0.00" + 0 # the number 0 (coerced by the +), so false.
\$a        # a reference to $a, so true, even if $a is false.
undef()    # a function returning the undefined value, so false.

####################################################################################
1:An Overview of Perl/Control Structures/What Is Truth?/The if and unless statements
####################################################################################

if ($debug_level > 0) {
    # Something has gone wrong.  Tell the user.
    print "Debug: Danger, Will Robinson, danger!\n";
    print "Debug: Answer was '54', expected '42'.\n";
}

--------------

if ($city eq "New York") {
    print "New York is northeast of Washington, D.C.\n";
}
elsif ($city eq "Chicago") {
    print "Chicago is northwest of Washington, D.C.\n";
}
elsif ($city eq "Miami") {
    print "Miami is south of Washington, D.C.  And much warmer!\n";
}
else {
    print "I don't know where $city is, sorry.\n";
}

--------------

unless ($destination eq $home) {
    print "I'm not going home.\n";
}

######################################################################################################
1:An Overview of Perl/Control Structures/Iterative (Looping) Constructs/The while and until statements
######################################################################################################

while ($tickets_sold < 10000) {
    $available = 10000 - $tickets_sold;
    print "$available tickets are available.  How many would you like: ";
    $purchase = <STDIN>;
    chomp($purchase);
    $tickets_sold += $purchase;
}

--------------

print "This show is sold out, please come back later.\n";

--------------

while (@ARGV) {
    process(shift @ARGV);
}

#########################################################################################
1:An Overview of Perl/Control Structures/Iterative (Looping) Constructs/The for statement
#########################################################################################

for ($sold = 0; $sold < 10000; $sold += $purchase) {
    $available = 10000 - $sold;
    print "$available tickets are available.  How many would you like: ";
    $purchase = <STDIN>;
    chomp($purchase);
}

#############################################################################################
1:An Overview of Perl/Control Structures/Iterative (Looping) Constructs/The foreach statement
#############################################################################################

foreach $user (@users) {
    if (-f "$home{$user}/.nexrc") {
        print "$user is cool... they use a perl-aware vi!\n";
    }
}

--------------

foreach $key (sort keys %hash) {

###################################################################################################
1:An Overview of Perl/Control Structures/Iterative (Looping) Constructs/Breaking out: next and last
###################################################################################################

foreach $user (@users) {
    if ($user eq "root" or $user eq "lp") {
        next;
    }
    if ($user eq "special") {
        print "Found the special account.\n";
        # do some processing
        last;
    }
}

--------------

LINE: while ($line = <ARTICLE>) {
    last LINE if $line eq "\n"; # stop on first blank line
    next LINE if $line =~ /^#/; # skip comment lines
    # your ad here
}

#########################################
1:An Overview of Perl/Regular Expressions
#########################################

if (/Windows 95/) { print "Time to upgrade?\n" }

--------------

s/Windows/Linux/;

--------------

($good, $bad, $ugly) = split(/,/, "vi,emacs,teco");

--------------

while ($line = <FILE>) {
    if ($line =~ /http:/) {
        print $line;
    }
}

--------------

while (<FILE>) {
    print if /http:/;
}

--------------

while (<FILE>) {
    print if /http:/;
    print if /ftp:/;
    print if /mailto:/;
    # What next?
}

#####################################################
1:An Overview of Perl/Regular Expressions/Quantifiers
#####################################################

$_ = "fred xxxxxxx barney";
s/x*//;

#############################################################
1:An Overview of Perl/Regular Expressions/Nailing Things Down
#############################################################

/\bFred\b/

--------------

next LINE if $line =~ /^#/;

########################################################
1:An Overview of Perl/Regular Expressions/Backreferences
########################################################

s/(\S+)\s+(\S+)/$2 $1/

#####################################
1:An Overview of Perl/List Processing
#####################################

@array = (1 + 2, 3 - 4, 5 * 6, 7 / 8);

--------------

sort @dudes, @chicks, other();

--------------

print reverse sort map {lc} keys %hash;

--------------

($hour, $min, $sec, $ampm) = /(\d+):(\d+):(\d+) *(\w+)/;

--------------

@hmsa = /(\d+):(\d+):(\d+) *(\w+)/;

#####################################
2:Bits and Pieces/Built-in Data Types
#####################################

$x = $y;

--------------

$x = $y + 1;

###########################
2:Bits and Pieces/Variables
###########################

@days = 1 .. 7;

#######################
2:Bits and Pieces/Names
#######################

$Santa::Helper::Reindeer::Rudolph::nose

####################################
2:Bits and Pieces/Names/Name Lookups
####################################

$bert

--------------

${ some_expression() }

################################################
2:Bits and Pieces/Scalar Values/Numeric literals
################################################

$x = 12345;                # integer
$x = 12345.67;             # floating point
$x = 6.02e23;              # scientific notation
$x = 4_294_967_296;        # underline for legibility
$x = 0377;                 # octal
$x = 0xffff;               # hexadecimal
$x = 0b1100_0000;          # binary

###############################################
2:Bits and Pieces/Scalar Values/String literals
###############################################

$Price = '$100';                    # not interpolated
print "The price is $Price.\n";     # interpolated

--------------

$days{'Feb'}

--------------

$days{Feb}

--------------

$days{'February 29th'}   # Ok.
$days{"February 29th"}   # Also ok. "" doesn't have to interpolate.
$days{ February 29th }   # WRONG, produces parse error.

--------------

@days{'Jan','Feb'}       # Ok.
@days{"Jan","Feb"}       # Also ok.
@days{ Jan,  Feb }       # Kinda wrong (breaks under use strict)

--------------

print "\n";              # Ok, print a newline.
print  \n ;              # WRONG, no interpolative context.

####################################################
2:Bits and Pieces/Scalar Values/Pick your own quotes
####################################################

$single = q!I said, "You said, 'She said it.'"!;

$double = qq(Can't we get some "good" $variable?);

$chunk_of_code = q {
    if ($condition) {
        print "Gotcha!";
    }
};

--------------

tr (a-f)
   [A-F];

--------------

s {foo}   # Replace foo
  {bar};  #    with bar.

tr [a-f]  # Translate lowercase hex
   [A-F]; #        to uppercase hex

################################################################
2:Bits and Pieces/Scalar Values/Or leave the quotes out entirely
################################################################

@days = (Mon,Tue,Wed,Thu,Fri);
print STDOUT hello, ' ', world, "\n";

--------------

@days = qw(Mon Tue Wed Thu Fri);
print STDOUT "hello world\n";

--------------

use strict 'subs';

--------------

no strict 'subs';

--------------

"${verb}able"
$days{Feb}

##########################################################
2:Bits and Pieces/Scalar Values/Interpolating array values
##########################################################

$temp = join( $", @ARGV );
print $temp;

print "@ARGV";

################################################
2:Bits and Pieces/Scalar Values/"Here" documents
################################################

    print <<EOF;    # same as earlier example
The price is $Price.
EOF

    print <<"EOF";  # same as above, with explicit quotes
The price is $Price.
EOF

    print <<'EOF';    # single-quoted quote
All things (e.g. a camel's journey through
A needle's eye) are possible, it's true.
But picture how the camel feels, squeezed out
In one long bloody thread, from tail to snout.
                                -- C.S. Lewis
EOF

    print << x 10;    # print next line 10 times
The camels are coming!  Hurrah!  Hurrah!

    print <<"" x 10;  # the preferred way to write that
The camels are coming!  Hurrah!  Hurrah!

    print <<`EOC`;    # execute commands
echo hi there
echo lo there
EOC

    print <<"dromedary", <<"camelid"; # you can stack them
I said bactrian.
dromedary
She said llama.
camelid

    funkshun(<<"THIS", 23, <<'THAT');
Here's a line
or two.
THIS
And here's another.
THAT

--------------

    print <<'odd'
2345
odd
    + 10000;   # prints 12345

--------------

($quote = <<'QUOTE') =~ s/^\s+//gm;
    Christians and camels receive
    their blessings on their knees.
QUOTE

--------------

@sauces = <<End_Lines =~ m/(\S.*\S)/g;
   normal tomato
   spicy tomato
   green chile
   pesto
   white wine
End_Lines

#################################################
2:Bits and Pieces/Scalar Values/V-String Literals
#################################################

$crlf = v13.10;           # ASCII carriage return, line feed

--------------

"\x{1}\x{14}\x{12c}\x{fa0}"
pack("U*", 1, 20, 300, 4000)
chr(1) . chr(20) . chr(300) . chr(4000)

--------------

print v9786;              # prints UTF-8 encoded SMILEY, "\x{263a}"
print v102.111.111;       # prints "foo"
print 102.111.111;        # same thing

use 5.6.0;                # require a particular Perl version (or later)

$ipaddr = 204.148.40.9;   # the IPv4 address of oreilly.com

#################################################
2:Bits and Pieces/Context/Scalar and list context
#################################################

$x         = funkshun();  # scalar context
$x[1]      = funkshun();  # scalar context
$x{"ray"}  = funkshun();  # scalar context

--------------

@x         = funkshun();  # list context
@x[1]      = funkshun();  # list context
@x{"ray"}  = funkshun();  # list context
%x         = funkshun();  # list context

--------------

($x,$y,$z) = funkshun();  # list context
($x)       = funkshun();  # list context

--------------

my $x      = funkshun();  # scalar context
my @x      = funkshun();  # list context
my %x      = funkshun();  # list context
my ($x)    = funkshun();  # list context

#########################################
2:Bits and Pieces/Context/Boolean context
#########################################

unlink @files;      # Delete all files, ignoring errors.

--------------

while (@files) {
    my $file = shift @files;
    unlink $file or warn "Can't delete $file: $!\n";
}

######################################
2:Bits and Pieces/Context/Void context
######################################

"Camel Lot";

########################################
2:Bits and Pieces/List Values and Arrays
########################################

@stuff = ("one", "two", "three");

--------------

$stuff = ("one", "two", "three");

--------------

@stuff = ("one", "two", "three");
$stuff = @stuff;

--------------

(@stuff,@nonsense,funkshun())

--------------

@releases = (
    "alpha",
    "beta",
    "gamma",
);

--------------

@froots = qw(
    apple       banana      carambola
    coconut     guava       kumquat
    mandarin    nectarine   peach
    pear        persimmon   plum
);

--------------

# Stat returns list value.
$modification_time = (stat($file))[9];

# SYNTAX ERROR HERE.
$modification_time = stat($file)[9];  # OOPS, FORGOT PARENS

# Find a hex digit.
$hexdigit = ('a','b','c','d','e','f')[$digit-10];

# A "reverse comma operator".
return (pop(@foo),pop(@foo))[0];

# Get multiple values as a slice.
($day, $month, $year) = (localtime)[3,4,5];

########################################################
2:Bits and Pieces/List Values and Arrays/List assignment
########################################################

($a, $b, $c) = (1, 2, 3);

($map{red}, $map{green}, $map{blue}) = (0xff0000, 0x00ff00, 0x0000ff);

--------------

($dev, $ino, undef, undef, $uid, $gid) = stat($file);

--------------

($a, $b, @rest) = split;
my ($a, $b, %rest) = @arg_list;

--------------

() = funkshun();

--------------

$x = ( ($a, $b) = (7,7,7) );    # set $x to 3, not 2
$x = ( ($a, $b) = funk() );     # set $x to funk()'s return count
$x = ( () = funk() );           # also set $x to funk()'s return count

--------------

while (($login, $password) = getpwent) {
    if (crypt($login, $password) eq $password) {
        print "$login has an insecure password!\n";
    }
}

#####################################################
2:Bits and Pieces/List Values and Arrays/Array length
#####################################################

@days + 0;      # implicitly force @days into a scalar context
scalar(@days)   # explicitly force @days into a scalar context

--------------

@whatever = ();
$#whatever = -1;

--------------

scalar(@whatever) == $#whatever + 1;

########################
2:Bits and Pieces/Hashes
########################

%map = ('red',0xff0000,'green',0x00ff00,'blue',0x0000ff);

--------------

%map = ();            # clear the hash first
$map{red}   = 0xff0000;
$map{green} = 0x00ff00;
$map{blue}  = 0x0000ff;

--------------

%map = (
    red   => 0xff0000,
    green => 0x00ff00,
    blue  => 0x0000ff,
);

--------------

$rec = {
    NAME  => 'John Smith',
    RANK  => 'Captain',
    SERNO => '951413',
};

--------------

$field = radio_group(
             NAME      => 'animals',
             VALUES    => ['camel', 'llama', 'ram', 'wolf'],
             DEFAULT   => 'camel',
             LINEBREAK => 'true',
             LABELS    => \%animal_names,
         );

###########################################
2:Bits and Pieces/Typeglobs and Filehandles
###########################################

$fh = *STDOUT;

--------------

$fh = \*STDOUT;

--------------

sub newopen {
    my $path = shift;
    local *FH;          # not my() nor our()
    open(FH, $path) or return undef;
    return *FH;         # not \*FH!
}
$fh = newopen('/etc/passwd');

--------------

*foo = *bar;

--------------

*foo = \$bar;

--------------

local *Here::blue = \$There::green;

###################################################################
2:Bits and Pieces/Input Operators/Command input (backtick) operator
###################################################################

$info = `finger $user`;

--------------

$perl_info  = qx(ps $$);            # that's Perl's $$
$shell_info = qx'ps $$';            # that's the shell's $$

#############################################################
2:Bits and Pieces/Input Operators/Line input (angle) operator
#############################################################

while (defined($_ = <STDIN>)) { print $_; }   # the longest way
while ($_ = <STDIN>) { print; }               # explicitly to $_
while (<STDIN>) { print; }                    # the short way
for (;<STDIN>;) { print; }                    # while loop in disguise
print $_ while defined($_ = <STDIN>);         # long statement modifier
print while $_ = <STDIN>;                     # explicitly to $_
print while <STDIN>;                          # short statement modifier

--------------

while (<FH1> && <FH2>) { ... }          # WRONG: discards both inputs
if (<STDIN>)      { print; }            # WRONG: prints old value of $_
if ($_ = <STDIN>) { print; }            # suboptimal: doesn't test defined
if (defined($_ = <STDIN>)) { print; }   # best

--------------

while (local $_ = <STDIN>) { print; }   # use local $_

--------------

while (my $line = <STDIN>) { print $line; } # now private

--------------

$one_line = <MYFILE>;   # Get first line.
@all_lines = <MYFILE>;  # Get the rest of the lines.

--------------

while (<>) {
    ...                     # code for each line
}

--------------

@ARGV = ('-') unless @ARGV;     # assume STDIN iff empty
while (@ARGV) {
    $ARGV = shift @ARGV;        # shorten @ARGV each time
    if (!open(ARGV, $ARGV)) {
        warn "Can't open $ARGV: $!\n";
        next;
    }
    while (<ARGV>) {
        ...                     # code for each line
    }
}

--------------

# default to README file if no args given
@ARGV = ("README") unless @ARGV;

--------------

while (@ARGV and $ARGV[0] =~ /^-/) {
    $_ = shift;
    last if /^--$/;
    if (/^-D(.*)/) { $debug = $1 }
    if (/^-v/)     { $verbose++  }
    ...             # other switches
}
while (<>) {
    ...             # code for each line
}

--------------

$fh = \*STDIN;
$line = <$fh>;

--------------

open($fh, "<data.txt");
$line = <$fh>;

############################################################
2:Bits and Pieces/Input Operators/Filename globbing operator
############################################################

@files = <*.xml>;

--------------

@files = glob("*.xml");

--------------

while (glob "*.c") {
    chmod 0644, $_;
}

--------------

while (<*.c>) {
    chmod 0644, $_;
}

--------------

chmod 0644, <*.c>;

--------------

($file) = <blurch*>;  # list context

--------------

$file = <blurch*>;    # scalar context

--------------

@files = <$dir/*.[ch]>;         # Works, but avoid.
@files = glob("$dir/*.[ch]");   # Call glob as function.
@files = glob $some_pattern;    # Call glob as operator.

############################
3:Unary and Binary Operators
############################

! $x                # a unary operator
$x * $y             # a binary operator
$x ? $y : $z        # a trinary operator
print $x, $y, $z    # a list operator

--------------

2 + 3 * 4           # yields 14, not 20

--------------

2 * 3 * 4           # means (2 * 3) * 4, left associative
2 ** 3 ** 4         # means 2 ** (3 ** 4), right associative
2 != 3 != 4         # illegal, non-associative

################################################################
3:Unary and Binary Operators/Terms and List Operators (Leftward)
################################################################

chdir $foo    || die;       # (chdir $foo) || die
chdir($foo)   || die;       # (chdir $foo) || die
chdir ($foo)  || die;       # (chdir $foo) || die
chdir +($foo) || die;       # (chdir $foo) || die

--------------

chdir $foo * 20;            # chdir ($foo * 20)
chdir($foo) * 20;           # (chdir $foo) * 20
chdir ($foo) * 20;          # (chdir $foo) * 20
chdir +($foo) * 20;         # chdir ($foo * 20)

--------------

rand 10 * 20;               # rand (10 * 20)
rand(10) * 20;              # (rand 10) * 20
rand (10) * 20;             # (rand 10) * 20
rand +(10) * 20;            # rand (10 * 20)

--------------

@ary = (1, 3, sort 4, 2);
print @ary;         # prints 1324

--------------

# These evaluate exit before doing the print:
print($foo, exit);  # Obviously not what you want.
print $foo, exit;   # Nor this.

# These do the print before evaluating exit:
(print $foo), exit; # This is what you want.
print($foo), exit;  # Or this.
print ($foo), exit; # Or even this.

--------------

print ($foo & 255) + 1, "\n";   # prints ($foo & 255)

###############################################
3:Unary and Binary Operators/The Arrow Operator
###############################################

$aref->[42]                 # an array dereference
$href->{"corned beef"}      # a hash dereference
$sref->(1,2,3)              # a subroutine dereference

--------------

$yogi = Bear->new("Yogi");  # a class method call
$yogi->swipe($picnic);      # an object method call

############################################################
3:Unary and Binary Operators/Autoincrement and Autodecrement
############################################################

print ++($foo = '99');      # prints '100'
print ++($foo = 'a0');      # prints 'a1'
print ++($foo = 'Az');      # prints 'Ba'
print ++($foo = 'zz');      # prints 'aaa'

##############################################
3:Unary and Binary Operators/Binding Operators
##############################################

$string !~ /pattern/
not $string =~ /pattern/

--------------

if ( ($k,$v) = $string =~ m/(\w+)=(\w*)/ ) {
    print "KEY $k VALUE $v\n";
}

#####################################################
3:Unary and Binary Operators/Multiplicative Operators
#####################################################

print '-' x 80;             # print row of dashes

print "\t" x ($tab/8), ' ' x ($tab%8);      # tab over

--------------

@ones = (1) x 80;           # a list of 80 1's
@ones = (5) x @ones;        # set all elements to 5

--------------

@keys = qw(perls before swine);
@hash{@keys} = ("") x @keys;

--------------

$hash{perls}  = "";
$hash{before} = "";
$hash{swine}  = "";

###############################################
3:Unary and Binary Operators/Additive Operators
###############################################

$almost = "Fred" . "Flintstone";    # returns FredFlintstone

--------------

$fullname = "$firstname $lastname";

############################################
3:Unary and Binary Operators/Shift Operators
############################################

1 << 4;     # returns 16
32 >> 4;    # returns 2

################################################################
3:Unary and Binary Operators/Named Unary and File Test Operators
################################################################

sleep 4 | 3;

--------------

(sleep 4) | 3;

--------------

print 4 | 3;

--------------

print (4 | 3);

--------------

next if length < 80;

--------------

next if length() < 80;
next if (length) < 80;
next if 80 > length;
next unless length >= 80;

--------------

while (<>) {
    chomp;
    next unless -f $_;      # ignore "special" files
    ...
}

--------------

next unless -f $file && -T $file;

--------------

print "Can do.\n" if -r $a || -w _ || -x _;

stat($filename);
print "Readable\n" if -r _;
print "Writable\n" if -w _;
print "Executable\n" if -x _;
print "Setuid\n" if -u _;
print "Setgid\n" if -g _;
print "Sticky\n" if -k _;
print "Text\n" if -T _;
print "Binary\n" if -B _;

--------------

next unless -M $file > .5;      # files older than 12 hours
&newfile if -M $file < 0;       # file is newer than process
&mailwarning if int(-A) == 90;  # file ($_) accessed 90 days ago today

--------------

$^T = time;

##############################################
3:Unary and Binary Operators/Bitwise Operators
##############################################

"123.45" & "234.56"

--------------

"020.44"

--------------

"123.45" & 234.56

--------------

123.45 & 234.56

--------------

123 & 234

--------------

if ( "fred" & "\1\2\3\4" ) { ... }

--------------

if ( ("fred" & "\1\2\3\4") =~ /[^\0]/ ) { ... }

######################################################################
3:Unary and Binary Operators/C-style Logical (Short Circuit) Operators
######################################################################

open(FILE, "somefile") || die "Can't open somefile: $!\n";

--------------

$home = $ENV{HOME}
     || $ENV{LOGDIR}
     || (getpwuid($<))[7]
     || die "You're homeless!\n";

--------------

@a = @b || @c;             # This doesn't do the right thing
@a = scalar(@b) || @c;     # because it really means this.
@a = @b ? @b : @c;         # This works fine, though.

###########################################
3:Unary and Binary Operators/Range Operator
###########################################

if (101 .. 200) { print; }  # print 2nd hundred lines
next line if (1 .. /^$/);   # skip header lines
s/^/> / if (/^$/ .. eof()); # quote body

--------------

for (101 .. 200) { print; }            # prints 101102...199200
@foo = @foo[0 .. $#foo];               # an expensive no-op
@foo = @foo[ -5 .. -1];                # slice last 5 items

--------------

@alphabet = ('A' .. 'Z');

--------------

$hexdigit = (0 .. 9, 'a' .. 'f')[$num & 15];

--------------

@z2 = ('01' .. '31');  print $z2[$mday];

--------------

@combos = ('aa' .. 'zz');

--------------

@bigcombos = ('aaaaaa' .. 'zzzzzz');

#################################################
3:Unary and Binary Operators/Conditional Operator
#################################################

$a = $ok ? $b : $c;  # get a scalar
@a = $ok ? @b : @c;  # get an array
$a = $ok ? @b : @c;  # get a count of an array's elements

--------------

printf "I have %d camel%s.\n",
               $n,     $n == 1 ? "" : "s";

--------------

$leapyear =
    $year % 4 == 0
        ? $year % 100 == 0
          ? $year % 400 == 0
            ? 1
            : 0
          : 1
        : 0;

--------------

$leapyear =
    $year % 4
        ? 0
        : $year % 100
          ? 1
          : $year % 400
            ? 0
            : 1;

--------------

$leapyear =
    $year %   4 ? 0 :
    $year % 100 ? 1 :
    $year % 400 ? 0 : 1;

--------------

printf "Yes, I like my %s book!\n",
    $i18n eq "french"   ? "chameau"          :
    $i18n eq "german"   ? "Kamel"            :
    $i18n eq "japanese" ? "\x{99F1}\x{99DD}" :
                          "camel"

--------------

($a_or_b ? $a : $b) = $c;  # sets either $a or $b to equal $c

--------------

$a % 2 ? $a += 10 : $a += 2         # WRONG

--------------

(($a % 2) ? ($a += 10) : $a) += 2

#################################################
3:Unary and Binary Operators/Assignment Operators
#################################################

$var[$a++] += $value;               # $a is incremented once
$var[$a++] = $var[$a++] + $value;   # $a is incremented twice

--------------

($tmp = $global) += $constant;

--------------

$tmp = $global + $constant;

--------------

($a += 2) *= 3;

--------------

$a += 2;
$a *= 3;

--------------

($new = $old) =~ s/foo/bar/g;

--------------

$a = $b = $c = 0;

--------------

while (($key, $value) = each %gloss) { ... }

next unless ($dev, $ino, $mode) = stat $file;

############################################
3:Unary and Binary Operators/Comma Operators
############################################

$a = (1, 3);

--------------

@a = (1, 3);

--------------

atan2(1, 3);

##########################################################
3:Unary and Binary Operators/Logical and, or, not, and xor
##########################################################

unlink "alpha", "beta", "gamma"
        or gripe(), next LINE;

--------------

unlink("alpha", "beta", "gamma")
        || (gripe(), next LINE);

--------------

$xyz = $x || $y || $z;

--------------

$xyz = $x or $y or $z;      # WRONG

--------------

$xyz = ( $x or $y or $z );

##########################################################
3:Unary and Binary Operators/C Operators Missing from Perl
##########################################################

$ref_to_var = \$var;

###############################################
4:Statements and Declarations/Simple Statements
###############################################

$trash->take('out') if $you_love_me;
shutup() unless $you_want_me_to_leave;

--------------

$expression++ while -e "$file$expression";
kiss('me') until $I_die;

--------------

s/java/perl/ for @resumes;
print "field: $_\n" foreach split /:/, $dataline;

--------------

do {
    $line = <STDIN>;
    ...
} until $line eq ".\n";

#################################################
4:Statements and Declarations/Compound Statements
#################################################

unless (open(FOO, $foo))    { die "Can't open $foo: $!" }
if (!open(FOO, $foo))       { die "Can't open $foo: $!" }

die "Can't open $foo: $!"   unless open(FOO, $foo);
die "Can't open $foo: $!"   if !open(FOO, $foo);

open(FOO, $foo)             || die "Can't open $foo: $!";
open FOO, $foo              or die "Can't open $foo: $!";

--------------

chdir $dir                  or die "chdir $dir: $!";
open FOO, $file             or die "open $file: $!";
@lines = <FOO>              or die "$file is empty?";
close FOO                   or die "close $file: $!";

######################################################
4:Statements and Declarations/If and Unless Statements
######################################################

unless ($x == 1) ...

--------------

if ($x != 1) ...

--------------

if (!($x == 1)) ...

--------------

if ((my $color = <STDIN>) =~ /red/i) {
    $value = 0xff0000;
}
elsif ($color =~ /green/i) {
    $value = 0x00ff00;
}
elsif ($color =~ /blue/i) {
    $value = 0x0000ff;
}
else {
    warn "unknown RGB component `$color', using black instead\n";
    $value = 0x000000;
}

########################################################################
4:Statements and Declarations/Loop Statements/While and Until Statements
########################################################################

while (my $line = <STDIN>) {
    $line = lc $line;
}
continue {
    print $line;   # still visible
}
# $line now out of scope here

#######################################################
4:Statements and Declarations/Loop Statements/For Loops
#######################################################

LABEL:
  for (my $i = 1; $i <= 10; $i++) {
      ...
  }

--------------

{
    my $i = 1;
  LABEL:
    while ($i <= 10) {
        ...
    }
    continue {
        $i++;
    }
}

--------------

for ($i = 0, $bit = 0; $i < 32; $i++, $bit <<= 1) {
    print "Bit $i is set\n" if $mask & $bit;
}
# the values in $i and $bit persist past the loop

--------------

for (my ($i, $bit) = (0, 1); $i < 32; $i++, $bit <<= 1) {
    print "Bit $i is set\n" if $mask & $bit;
}
# loop's versions of $i and $bit now out of scope

--------------

$on_a_tty = -t STDIN && -t STDOUT;
sub prompt { print "yes? " if $on_a_tty }
for ( prompt(); <STDIN>; prompt() ) {
    # do something
}

--------------

for (;;) {
    ...
}

--------------

while (1) {
    ...
}

###########################################################
4:Statements and Declarations/Loop Statements/Foreach Loops
###########################################################

$sum = 0; foreach $value (@array) { $sum += $value }

for $count (10,9,8,7,6,5,4,3,2,1,'BOOM') {  # do a countdown
    print "$count\n"; sleep(1);
}

for (reverse 'BOOM', 1 .. 10) {             # same thing
    print "$_\n"; sleep(1);
}

for $field (split /:/, $data) {             # any LIST expression
    print "Field contains: `$field'\n";
}

foreach $key (sort keys %hash) {
    print "$key => $hash{$key}\n";
}

--------------

foreach $pay (@salaries) {               # grant 8% raises
    $pay *= 1.08;
}

for (@christmas, @easter) {              # change menu
    s/ham/turkey/;
}
s/ham/turkey/ for @christmas, @easter;   # same thing

for ($scalar, @array, values %hash) {
    s/^\s+//;                            # strip leading  whitespace
    s/\s+$//;                            # strip trailing whitespace
}

--------------

for my  $i    (1 .. 10) { ... }         # $i always lexical
for our $Tick (1 .. 10) { ... }         # $Tick always global

--------------

for ($i = 0; $i < @ary1; $i++) {
    for ($j = 0; $j < @ary2; $j++) {
        if ($ary1[$i] > $ary2[$j]) {
            last;         # Can't go to outer loop. :-(
        }
        $ary1[$i] += $ary2[$j];
    }
    # this is where that last takes me
}

--------------

WID: foreach $this (@ary1) {
    JET: foreach $that (@ary2) {
        next WID if $this > $that;
        $this += $that;
    }
}

##########################################################
4:Statements and Declarations/Loop Statements/Loop Control
##########################################################

next LINE if /^#/;      # discard comments

--------------

LINE: while (<STDIN>) {
    last LINE if /^$/;      # exit when done with mail header
    ...
}

--------------

LINE: while (<STDIN>) {
    next LINE if /^#/;      # skip comments
    next LINE if /^$/;      # skip blank lines
    ...
} continue {
    $count++;
}

--------------

while (<>) {
    chomp;
    if (s/\\$//) {
        $_ .= <>;
        redo unless eof;    # don't read past each file's eof
    }
    # now process $_
}

--------------

LINE: while (defined($line = <ARGV>)) {
    chomp($line);
    if ($line =~ s/\\$//) {
        $line .= <ARGV>;
        redo LINE unless eof(ARGV);
    }
    # now process $line
}

--------------

ARG: while (@ARGV && $ARGV[0] =~ s/^-(?=.)//) {
    OPT: for (shift @ARGV) {
            m/^$/       && do {                             next ARG; };
            m/^-$/      && do {                             last ARG; };
            s/^d//      && do { $Debug_Level++;             redo OPT; };
            s/^l//      && do { $Generate_Listing++;        redo OPT; };
            s/^i(.*)//  && do { $In_Place = $1 || ".bak";   next ARG; };
            say_usage("Unknown option: $_");
    }
}

--------------

open FILE, $file
     or warn "Can't open $file: $!\n", next FILE;   # WRONG

--------------

open FILE, $file
     or warn("Can't open $file: $!\n"), next FILE;   # okay

--------------

unless (open FILE, $file) {
     warn "Can't open $file: $!\n";
     next FILE;
}

#########################################
4:Statements and Declarations/Bare Blocks
#########################################

if (/pattern/) {{
    last if /alpha/;
    last if /beta/;
    last if /gamma/;
    # do something here only if still in if()
}}

--------------

do {{
    next if $x == $y;
    # do something here
}} until $x++ > $z;

--------------

{
    do {
        last if $x = $y ** 2;
        # do something here
    } while $x++ <= $z;
}

--------------

DO_LAST: {
            do {
DO_NEXT:          {
                    next DO_NEXT if $x == $y;
                    last DO_LAST if $x =  $y ** 2;
                    # do something here
                  }
            } while $x++ <= $z;
         }

--------------

for (;;) {
    next if $x == $y;
    last if $x =  $y ** 2;
    # do something here
    last unless $x++ <= $z;
}

#########################################################
4:Statements and Declarations/Bare Blocks/Case Structures
#########################################################

SWITCH: {
    if (/^abc/) { $abc = 1; last SWITCH; }
    if (/^def/) { $def = 1; last SWITCH; }
    if (/^xyz/) { $xyz = 1; last SWITCH; }
    $nothing = 1;
}

--------------

SWITCH: {
    /^abc/      && do { $abc = 1; last SWITCH; };
    /^def/      && do { $def = 1; last SWITCH; };
    /^xyz/      && do { $xyz = 1; last SWITCH; };
    $nothing = 1;
}

--------------

SWITCH: {
    /^abc/      && do {
                        $abc = 1;
                        last SWITCH;
                   };
    /^def/      && do {
                        $def = 1;
                        last SWITCH;
                   };
    /^xyz/      && do {
                        $xyz = 1;
                        last SWITCH;
                    };
    $nothing = 1;
}

--------------

if    (/^abc/) { $abc = 1 }
elsif (/^def/) { $def = 1 }
elsif (/^xyz/) { $xyz = 1 }
else           { $nothing = 1 }

--------------

for ($very_nasty_long_name[$i++][$j++]->method()) {
    /this pattern/      and do { push @flags, '-e'; last; };
    /that one/          and do { push @flags, '-h'; last; };
    /something else/    and do {                    last; };
    die "unknown value: `$_'";
}

--------------

for ($user_color_preference) {
    $value = /red/      ?   0xFF0000  :
             /green/    ?   0x00FF00  :
             /blue/     ?   0x0000FF  :
                            0x000000  ;   # black if all fail
}

--------------

%color_map = (
    azure       => 0xF0FFFF,
    chartreuse  => 0x7FFF00,
    lavender    => 0xE6E6FA,
    magenta     => 0xFF00FF,
    turquoise   => 0x40E0D0,
);

--------------

$value = $color_map{ lc $user_color_preference } || 0x000000;

##################################
4:Statements and Declarations/Goto
##################################

goto(("FOO", "BAR", "GLARCH")[$i]);         # hope 0 <= i < 3

@loop_label = qw/FOO BAR GLARCH/;
goto $loop_label[rand @loop_label];         # random teleport

#################################################
4:Statements and Declarations/Global Declarations
#################################################

sub count (@);       # Compiler now knows how to call count().
my $x;               # Compiler now knows about lexical variable.
$x = count(3,2,1);   # Compiler can validate function call.
sub count (@) { @_ } # Compiler now knows what count() means.

--------------

sub myname;
$me = myname $0             or die "can't get myname";

--------------

sub myname ($);
$me = myname $0             || die "can't get myname";

##############################################################################
4:Statements and Declarations/Scoped Declarations/Scoped Variable Declarations
##############################################################################

my $nose;
our $House;
local $TV_channel;

--------------

my ($nose, @eyes, %teeth);
our ($House, @Autos, %Kids);
local (*Spouse, $phone{HOME});

--------------

my ($foo) = <STDIN>;
my @array = <STDIN>;

--------------

my $foo = <STDIN>;

--------------

my $foo, $bar = 1;              # WRONG

--------------

my $foo;
$bar = 1;

--------------

sub check_warehouse {
    for my $widget (our @Current_Inventory) {
        print "I have a $widget in stock today.\n";
    }
}

###################################################################################
4:Statements and Declarations/Scoped Declarations/Lexically Scoped Variables: C<my>
###################################################################################

my $name = "fred";
my @stuff = ("car", "house", "club");
my ($vehicle, $home, $tool) = @stuff;

--------------

{
    my $state = 0;

    sub on     { $state = 1 }
    sub off    { $state = 0 }
    sub toggle { $state = !$state }
}

--------------

my $x = $x;

##############################################################################################
4:Statements and Declarations/Scoped Declarations/Lexically Scoped Global Declarations: C<our>
##############################################################################################

sub check_warehouse {
    our @Current_Inventory;
    my  $widget;
    foreach $widget (@Current_Inventory) {
        print "I have a $widget in stock today.\n";
    }
}

--------------

our $PROGRAM_NAME = "waiter";
{
    our $PROGRAM_NAME = "server";
    # Code called here sees "server".
    ...
}
# Code executed below still sees "server".

--------------

my $i = 10;
{
    my $i = 99;
    ...
}
# Code compiled below sees outer variable.

local $PROGRAM_NAME = "waiter";
{
    local $PROGRAM_NAME = "server";
    # Code called here sees "server".
    ...
}
# Code executed below sees "waiter" again.

--------------

{
    local our @Current_Inventory = qw(bananas);
    check_warehouse();  # no, we haven't no bananas :-)
}

########################################################################################
4:Statements and Declarations/Scoped Declarations/Dynamically Scoped Variables: C<local>
########################################################################################

{
    local $var = $newvalue;
    some_func();
    ...
}

--------------

{
    $oldvalue = $var;
    $var = $newvalue;
    some_func();
    ...
}
continue {
    $var = $oldvalue;
}

--------------

# WARNING: Changes are temporary to this dynamic scope.
local $Some_Global = $Some_Global;

#####################################
4:Statements and Declarations/Pragmas
#####################################

use warnings;
use strict;
use integer;
use bytes;
use constant pi => ( 4 * atan2(1,1) );

##########################################################
4:Statements and Declarations/Pragmas/Controlling Warnings
##########################################################

use warnings;       # Enable warnings from here till end of file.
...
{
    no warnings;    # Disable warnings through end of block.
    ...
}
# Warnings are automatically enabled again here.

--------------

{
    local $^W = 0;
    ...
}

####################################################################
4:Statements and Declarations/Pragmas/Controlling the Use of Globals
####################################################################

use strict 'vars';

--------------

no strict 'vars'

##################
5:Pattern Matching
##################

match( $string, $pattern );
subst( $string, $pattern, $replacement );

##################################################
5:Pattern Matching/The Regular Expression Bestiary
##################################################

/Frodo/

--------------

/Frodo|Pippin|Merry|Sam/

--------------

/(Frodo|Drogo|Bilbo) Baggins/

--------------

/(Frod|Drog|Bilb)o Baggins/

--------------

/(bar){3}/

#############################################
5:Pattern Matching/Pattern Matching Operators
#############################################

$foo = "bar";
/$foo$/;

--------------

/bar$/;

--------------

print "matches" if $somestring =~ $somepattern;

--------------

print "matches" if $somestring =~ m/$somepattern/;

--------------

$haystack =~ m/needle/                # match a simple pattern
$haystack =~  /needle/                # same thing

$italiano =~ s/butter/olive oil/      # a healthy substitution

$rotate13 =~ tr/a-zA-Z/n-za-mN-ZA-M/  # easy encryption (to break)

--------------

/new life/ and              # search in $_ and (if found)
    /new civilizations/     #    boldly search $_ again

s/sugar/aspartame/          # substitute a substitute into $_

tr/ATCG/TAGC/               # complement the DNA stranded in $_

--------------

"onshore" =~ s/on/off/;      # WRONG: compile-time error

--------------

if ((lc $magic_hat->fetch_contents->as_string) =~ /rabbit/) {
    print "Nyaa, what's up doc?\n";
}
else {
    print "That trick never works!\n";
}

--------------

if ($song !~ /words/) {
    print qq/"$song" appears to be a song without words.\n/;
}

--------------

$path =~ s#/tmp#/var/tmp/scratch#;

if ($dir =~ m[/bin]) {
    print "No binary directories please.\n";
}

--------------

s(egg)<larva>;
s{larva}{pupa};
s[pupa]/imago/;

--------------

s (egg)   <larva>;
s {larva} {pupa};
s [pupa]  /imago/;

--------------

"hot cross buns" =~ /cross/;
print "Matched: <$`> $& <$'>\n";    # Matched: <hot > cross < buns>
print "Left:    <$`>\n";            # Left:    <hot >
print "Match:   <$&>\n";            # Match:   <cross>
print "Right:   <$'>\n";            # Right:   < buns>

--------------

$_ = "Bilbo Baggins's birthday is September 22";
/(.*)'s birthday is (.*)/;
print "Person: $1\n";
print "Date: $2\n";

###############################################################
5:Pattern Matching/Pattern Matching Operators/Pattern Modifiers
###############################################################

m/\w+:(\s+\w+)\s*\d+/;       # A word, colon, space, word, space, digits.

m/\w+: (\s+ \w+) \s* \d+/x;  # A word, colon, space, word, space, digits.

m{
    \w+:                     # Match a word and a colon.
    (                        # (begin group)
         \s+                 # Match one or more spaces.
         \w+                 # Match another word.
    )                        # (end group)
    \s*                      # Match zero or more spaces.
    \d+                      # Match some digits
}x;

--------------

# Find duplicate words in paragraphs, possibly spanning line boundaries.
#   Use /x for space and comments, /i to match the both `is'
#   in "Is is this ok?", and use /g to find all dups.
$/ = "";        # paragrep mode
while (<>) {
    while ( m{
                \b            # start at a word boundary
                (\w\S+)       # find a wordish chunk
                (
                    \s+       # separated by some whitespace
                    \1        # and that chunk again
                ) +           # repeat ad lib
                \b            # until another word boundary
             }xig
         )
    {
        print "dup word '$1' at paragraph $.\n";
    }
}

#########################################################################
5:Pattern Matching/Pattern Matching Operators/The m// Operator (Matching)
#########################################################################

if ($shire =~ m/Baggins/) { ... }  # search for Baggins in $shire
if ($shire =~ /Baggins/)  { ... }  # search for Baggins in $shire

if ( m#Baggins# )         { ... }  # search right here in $_
if ( /Baggins/ )          { ... }  # search right here in $_

--------------

if (($key,$value) = /(\w+): (.*)/) { ... }

--------------

if (@perls = $paragraph =~ /perl/gi) {
    printf "Perl mentioned %d times.\n", scalar @perls;
}

--------------

$string = "password=xyzzy verbose=9 score=0";

--------------

%hash = (password => "xyzzy", verbose => 9, score => 0);

--------------

%hash = $string =~ /(\w+)=(\w+)/g;

--------------

open DICT, "/usr/dict/words" or die "Can't open words: $!\n";
while (<DICT>) {
    $first = $1 if ?(^neur.*)?;
    $last  = $1 if /(^neur.*)/;
}
print $first,"\n";          # prints "neurad"
print $last,"\n";           # prints "neurypnology"

##############################################################################
5:Pattern Matching/Pattern Matching Operators/The s/// Operator (Substitution)
##############################################################################

$lotr = $hobbit;           # Just copy The Hobbit
$lotr =~ s/Bilbo/Frodo/g;  #   and write a sequel the easy way.

--------------

if ($lotr =~ s/Bilbo/Frodo/) { print "Successfully wrote sequel." }
$change_count = $lotr =~ s/Bilbo/Frodo/g;

--------------

s/revision|version|release/\u$&/g;  # Use | to mean "or" in a pattern

--------------

s/version ([0-9.]+)/the $Names{$1} release/g;

--------------

s{
    version
    \s+
    (
        [0-9.]+
    )
}{
    $Names{$1}
        ? "the $Names{$1} release"
        : $&
}xge;

###########################################################################################################
5:Pattern Matching/Pattern Matching Operators/The s/// Operator (Substitution)/Modifying strings en passant
###########################################################################################################

$lotr = $hobbit;
$lotr =~ s/Bilbo/Frodo/g;

--------------

($lotr = $hobbit) =~ s/Bilbo/Frodo/g;

--------------

for (@chapters) { s/Bilbo/Frodo/g }  # Do substitutions chapter by chapter.
s/Bilbo/Frodo/g for @chapters;       # Same thing.

--------------

@oldhues = ('bluebird', 'bluegrass',  'bluefish', 'the blues');
for (@newhues = @oldhues) { s/blue/red/ }
print "@newhues\n";  # prints: redbird redgrass redfish the reds

--------------

for ($string) {
    s/^\s+//;       # discard leading whitespace
    s/\s+$//;       # discard trailing whitespace
    s/\s+/ /g;      # collapse internal whitespace
}

--------------

$string = join(" ", split " ", $string);

--------------

for ($newshow = $oldshow) {
    s/Fred/Homer/g;
    s/Wilma/Marge/g;
    s/Pebbles/Lisa/g;
    s/Dino/Bart/g;
}

##################################################################################################################################
5:Pattern Matching/Pattern Matching Operators/The s/// Operator (Substitution)/When a Global Substitution Just Isn't Global Enough
##################################################################################################################################

# put commas in the right places in an integer
1 while s/(\d)(\d\d\d)(?!\d)/$1,$2/;

# expand tabs to 8-column spacing
1 while s/\t+/' ' x (length($&)*8 - length($`)%8)/e;

# remove (nested (even deeply nested (like this))) comments
1 while s/\([^()]*\)//g;

# remove duplicate words (and triplicate (and quadruplicate...))
1 while s/\b(\w+) \1\b/$1/gi;

##################################################################################
5:Pattern Matching/Pattern Matching Operators/The tr/// Operator (Transliteration)
##################################################################################

$message =~ tr/A-Za-z/N-ZA-Mn-za-m/;    # rot13 encryption.

--------------

tr/aeiou/!/;                 # change any vowel into a !
tr{/\\\r\n\b\f. }{_};        # change strange chars into an underscore

tr/A-Z/a-z/ for @ARGV;       # canonicalize to lower case ASCII

$count = ($para =~ tr/\n//); # count the newlines in $para
$count = tr/0-9//;           # count the digits in $_

$word =~ tr/a-zA-Z//s;       # bookkeeper -> bokeper

tr/@$%*//d;                  # delete any of those
tr#A-Za-z0-9+/##cd;          # remove non-base64 chars

# change en passant
($HOST = $host) =~ tr/a-z/A-Z/;

$pathname =~ tr/a-zA-Z/_/cs; # change non-(ASCII)alphas to single underbar

tr [\200-\377]
   [\000-\177];              # strip 8th bit, bytewise

--------------

tr/AAA/XYZ/

--------------

$count = eval "tr/$oldlist/$newlist/";
die if $@;  # propagates exception from illegal eval contents

#################################################
5:Pattern Matching/Metacharacters and Metasymbols
#################################################

 \ | ( ) [ { ^ $ * + ? .

######################################################################
5:Pattern Matching/Metacharacters and Metasymbols/Wildcard Metasymbols
######################################################################

if ($pathname =~ /\.(.)\z/s) {
    print "Ends in $1\n";
}

--------------

use utf8;
use charnames qw/:full/;
$BWV[887] = "G\N{MUSIC SHARP SIGN} minor";
($note, $black, $mode) = $BWV[887] =~ /^([A-G])(.)\s+(\S+)/;
print "That's lookin' sharp!\n" if $black eq chr(9839);

###########################################################################
5:Pattern Matching/Character Classes/Classic Perl Character Class Shortcuts
###########################################################################

if ($var =~ /\D/)        { warn "contains non-digit" }
if ($var =~ /[^\w\s.]/)  { warn "contains non-(word, space, dot)" }

#######################################################
5:Pattern Matching/Character Classes/Unicode Properties
#######################################################

if ($var =~ /^\p{IsAlpha}+$/)      { print "all alphabetic" }
if ($var =~ s/[\p{Zl}\p{Zp}]/\n/g) { print "fixed newline wannabes" }

--------------

perl -MConfig -le 'print $Config{privlib}'

################################################################################
5:Pattern Matching/Character Classes/Unicode Properties/Unicode block properties
################################################################################

print "It's Greek to me!\n" if chr(931) =~ /\p{InGreek}/;

##############################################################################################
5:Pattern Matching/Character Classes/Unicode Properties/Defining your own character properties
##############################################################################################

sub InKana {
    return <<'END';
3040    309F
30A0    30FF
END
}

--------------

sub InKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
END
}

--------------

sub IsKana {
    return <<'END';
+utf8::InHiragana
+utf8::InKatakana
-utf8::IsCn
END
}

--------------

sub IsNotKana {
    return <<'END';
!utf8::InHiragana
-utf8::InKatakana
+utf8::IsCn
END
}

##################################################################
5:Pattern Matching/Character Classes/POSIX-Style Character Classes
##################################################################

42 =~ /^[:digit:]$/         # WRONG

--------------

42 =~ /^[[:digit:]]+$/

##############################
5:Pattern Matching/Quantifiers
##############################

"exasperate" =~ /e(.*)e/    #  $1 now "xasperat"

--------------

"exasperate" =~ /e(.*?)e/   #  $1 now "xasp"

--------------

"exasperate" =~ /.*e(.*?)e/   #  $1 now "rat"

######################################################################
5:Pattern Matching/Positions/Beginnings: The C<\A> and C<^> Assertions
######################################################################

/\Abar/      # Matches "bar" and "barstool"
/^bar/       # Matches "bar" and "barstool"
/^bar/m      # Matches "bar" and "barstool" and "sand\nbar"

--------------

s/^\s+//gm;             # Trim leading whitespace on each line
$total++ while /^./mg;  # Count nonblank lines

###########################################################################
5:Pattern Matching/Positions/Endings: The C<\z>, C<\Z>, and C<$> Assertions
###########################################################################

/bot\z/      # Matches "robot"
/bot\Z/      # Matches "robot" and "abbot\n"
/bot$/       # Matches "robot" and "abbot\n"
/bot$/m      # Matches "robot" and "abbot\n" and "robot\nrules"

/^robot$/    # Matches "robot" and "robot\n"
/^robot$/m   # Matches "robot" and "robot\n" and "this\nrobot\n"
/\Arobot\Z/  # Matches "robot" and "robot\n"
/\Arobot\z/  # Matches only "robot" -- but why didn't you use eq?

--------------

s/\s*$//gm;   # Trim trailing whitespace on each line in paragraph

while (/^([^:]+):\s*(.*)/gm ) {  # get mail header
    $headers{$1} = $2;
}

#######################################################################
5:Pattern Matching/Positions/Boundaries: The C<\b> and C<\B> Assertions
#######################################################################

/\bis\b/   # matches "what it is" and "that is it"
/\Bis\B/   # matches "thistle" and "artist"
/\bis\B/   # matches "istanbul" and "so--isn't that butter?"
/\Bis\b/   # matches "confutatis" and "metropolis near you"

#################################################
5:Pattern Matching/Positions/Progressive Matching
#################################################

$burglar = "Bilbo Baggins";
while ($burglar =~ /b/gi) {
    printf "Found a B at %d\n", pos($burglar)-1;
}

$burglar = "Bilbo Baggins";
while ($burglar =~ /b/gci) {        # ADD /c
    printf "Found a B at %d\n", pos($burglar)-1;
}
while ($burglar =~ /i/gi) {
    printf "Found an I at %d\n", pos($burglar)-1;
}

####################################################################
5:Pattern Matching/Positions/Where You Left Off: The C<\G> Assertion
####################################################################

($recipe = <<'DISH') =~ s/^\s+//gm;
    Preheat oven to 451 deg. fahrenheit.
    Mix 1 ml. dilithium with 3 oz. NaCl and
    stir in 4 anchovies.  Glaze with 1 g.
    mercury.  Heat for 4 hours and let cool
    for 3 seconds.  Serves 10 aliens.
DISH

$recipe =~ /\d+ /g;
$recipe =~ /\G(\w+)/;           # $1 is now "deg"
$recipe =~ /\d+ /g;
$recipe =~ /\G(\w+)/;           # $1 is now "ml"
$recipe =~ /\d+ /g;
$recipe =~ /\G(\w+)/;           # $1 is now "oz"

--------------

pos($recipe) = 0;                 # Just to be safe, reset \G to 0
while ( $recipe =~ /(\d+) /g ) {
    my $amount = $1;
    if ($recipe =~ / \G (\w{0,3}) \. \s+ (\w+) /x) {  # abbrev. + word
        print "$amount $1 of $2\n";
    } else {
        $recipe =~ / \G (\w+) /x;                     # just a word
        print "$amount $1\n";
    }
}

#####################################################
5:Pattern Matching/Capturing and Clustering/Capturing
#####################################################

/(\d)(\d)/  # Match two digits, capturing them into $1 and $2
/(\d+)/     # Match one or more digits, capturing them all into $1
/(\d)+/     # Match a digit one or more times, capturing the last into $1

--------------

/\b(\w+) \1\b/i

--------------

From: gnat@perl.com
To: camelot@oreilly.com
Date: Mon, 17 Jul 2000 09:00:00 -1000
Subject: Eye of the needle

--------------

while (<>) {
    /^(.*?): (.*)$/;    # Pre-colon text into $1, post-colon into $2
    $fields{$1} = $2;
}

--------------

s/^(\S+) (\S+)/$2 $1/;  # Swap first two words

--------------

/^((\w+) (\w+))$/

--------------

($first, $last)        =  /^(\w+) (\w+)$/;
($full, $first, $last) =  /^((\w+) (\w+))$/;

--------------

%fields = /^(.*?): (.*)$/gm;

--------------

$_ = "Speak, <EM>friend</EM>, and enter.";
m[ (<.*?>) (.*?) (</.*?>) ]x;     # A tag, then chars, then an end tag
print "prematch: $`\n";           # Speak,
print "match: $&\n";              # <EM>friend</EM>
print "postmatch: $'\n";          # , and enter.
print "lastmatch: $+\n";          # </EM>

--------------

#!/usr/bin/perl
$alphabet = "abcdefghijklmnopqrstuvwxyz";
$alphabet =~ /(hi).*(stu)/;

print "The entire match began at $-[0] and ended at $+[0]\n";
print "The first  match began at $-[1] and ended at $+[1]\n";
print "The second match began at $-[2] and ended at $+[2]\n";

--------------

/\(e.g. .*?\)/

######################################################
5:Pattern Matching/Capturing and Clustering/Clustering
######################################################

@fields = split(/\b(?:a|b|c)\b/)

--------------

@fields = split(/\b(a|b|c)\b/)

########################################################################
5:Pattern Matching/Capturing and Clustering/Cloistered Pattern Modifiers
########################################################################

/Harry (?i:s) Truman/

--------------

/Harry (?x: [A-Z] \.? )? Truman/

--------------

/Harry (?ix: [A-Z] \.? )? Truman/

--------------

/Harry (?x-i: [A-Z] \.? )? Truman/i

--------------

/(?i)foo/            # Equivalent to /foo/i
/foo((?-i)bar)/i     # "bar" must be lower case
/foo((?x-i) bar)/    # Enables /x and disables /i for "bar"

##############################
5:Pattern Matching/Alternation
##############################

/Gandalf|Saruman|Radagast/

--------------

/prob|n|r|l|ate/    # Match prob, n, r, l, or ate
/pro(b|n|r|l)ate/   # Match probate, pronate, prorate, or prolate
/pro(?:b|n|r|l)ate/ # Match probate, pronate, prorate, or prolate

--------------

/(Sam|Samwise)/

--------------

"'Sam I am,' said Samwise" =~ /(Samwise|Sam)/;   # $1 eq "Sam"

--------------

"'Sam I am,' said Samwise" =~ /.*(Samwise|Sam)/; # $1 eq "Samwise"

--------------

"'Sam I am,' said Samwise" =~ /(Samwise|Sam)$/;  # $1 eq "Samwise"

--------------

#!/usr/bin/perl
while (<>) {
    print if /^__DATA__|^__END__/;
}

--------------

/^cat|dog|cow$/

--------------

/^(cat|dog|cow)$/

--------------

/^cat$|^dog$|^cow$/

--------------

/com(pound|)/;      # Matches "compound" or "com"
/com(pound(s|)|)/;  # Matches "compounds", "compound", or "com"

--------------

/com(pound)?/;      # Matches "compound" or "com"
/com(pound(s?))?/;  # Matches "compounds", "compound", or "com"
/com(pounds?)?/;    # Same, but doesn't use $2

##############################################################
5:Pattern Matching/Staying in Control/Letting Perl Do the Work
##############################################################

/Gandalf|Saruman|Radagast/

--------------

/Gandalf/ || /Saruman/ || /Radagast/

--------------

while (<CONF>) {
    next if /^#/;
    next if /^\s*(#|$)/;
    chomp;
    munchabunch($_);
}

--------------

warn "has nondigits"        if     /\D/;
warn "not a natural number" unless /^\d+$/;             # rejects -3
warn "not an integer"       unless /^-?\d+$/;           # rejects +3
warn "not an integer"       unless /^[+-]?\d+$/;
warn "not a decimal number" unless /^-?\d+\.?\d*$/;     # rejects .2
warn "not a decimal number" unless /^-?(?:\d+(?:\.\d*)?|\.\d+)$/;
warn "not a C float"
       unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;

############################################################
5:Pattern Matching/Staying in Control/Variable Interpolation
############################################################

if ($num =~ /^[-+]?\d+\.?\d*$/) { ... }

--------------

$sign = '[-+]?';
$digits = '\d+';
$decimal = '\.?';
$more_digits = '\d*';
$number = "$sign$digits$decimal$more_digits";
...
if ($num =~ /^$number$/o) { ... }

--------------

chomp($answer = <STDIN>);
if    ("SEND"  =~ /^\Q$answer/i) { print "Action is send\n"  }
elsif ("STOP"  =~ /^\Q$answer/i) { print "Action is stop\n"  }
elsif ("ABORT" =~ /^\Q$answer/i) { print "Action is abort\n" }
elsif ("LIST"  =~ /^\Q$answer/i) { print "Action is list\n"  }
elsif ("EDIT"  =~ /^\Q$answer/i) { print "Action is edit\n"  }

####################################################################################
5:Pattern Matching/Staying in Control/Variable Interpolation/When backslashes happen
####################################################################################

($col1, $col2) = /(.*?) \t+ (.*?)/x;

--------------

$colsep = "\t+";                         # (double quotes)
($col1, $col2) = /(.*?) $colsep (.*?)/x;

--------------

$var = '\U';
/${var}frodo/;

--------------

$hobbit = 'Frodo';
$var = '$hobbit';           # (single quotes)
/$var/;                     # means m'$hobbit', not m'Frodo'.

--------------

#!/usr/bin/perl
$pattern = shift;
while (<>) {
    print if /$pattern/o;
}

--------------

% pgrep '\t\d' *.c

--------------

% pgrep '(?i)ring' LotR*.pod

##########################################################################################
5:Pattern Matching/Staying in Control/Variable Interpolation/The qr// quote regex operator
##########################################################################################

print if /$pattern/o;

--------------

foreach $item (@data) {
    foreach $patstr (@patterns) {
        if ($item =~ /$patstr/) { ... }
    }
}

--------------

$regex = qr/my.STRING/is;
s/$regex/something else/;

--------------

s/my.STRING/something else/is;

--------------

@regexes = ();
foreach $patstr (@patterns) {
    push @regexes, qr/$patstr/;
}

--------------

@regexes = map { qr/$patstr/ } @patterns;

--------------

foreach $item (@data) {
    foreach $re (@regexes) {
        if ($item =~ /$re/) { ... }
    }
}

--------------

$regex = qr/$pattern/;
$string =~ /foo${regex}bar/;   # interpolate into larger patterns

--------------

$re = qr/my.STRING/is;
print $re;                  # prints (?si-xm:my.STRING)

--------------

$re = qr/$pat/is;                      # might escape and eat you
$re = eval { qr/$pat/is } || warn ...  # caught it in an outer cage

########################################################
5:Pattern Matching/Staying in Control/The Regex Compiler
########################################################

#!/usr/bin/perl
use re "debug";
"Smeagol" =~ /^Sm(.*)g[aeiou]l$/;

##########################################################################
5:Pattern Matching/Staying in Control/The Little Engine that /Could(n't)?/
##########################################################################

/x*y*/

--------------

$a = 'nobody';
$b = 'bodysnatcher';
if ("$a $b" =~ /^(\w+)(\w+) \2(\w+)$/) {
    print "$2 overlaps in $1-$2-$3\n";
}

#######################################################
5:Pattern Matching/Fancy Patterns/Lookaround Assertions
#######################################################

$_ = "Paris in THE THE THE THE spring.";

# remove duplicate words (and triplicate (and quadruplicate...))
1 while s/\b(\w+) \1\b/$1/gi;

--------------

s/ \b(\w+) \s (?= \1\b ) //gxi;

--------------

s/ \b(\w+) \s (?= \1\b (?! '\w))//xgi;

--------------

s/ \b(\w+) \s (?= \1\b (?! '\w | \s particular))//gix;

--------------

s/ \b(\w+) \s (?= \1\b (?! '\w | \s particular | \s nation))//igx;

--------------

@thatthat = qw(particular nation);
local $" = '|';
s/ \b(\w+) \s (?= \1\b (?! '\w | \s (?: @thatthat )))//xig;

--------------

s/ \b(\w+) \s (?= \1\b (?! '\w | (?<= that) \s (?: @thatthat )))//ixg;

--------------

s/(?<!c)ei/ie/g

##############################################################
5:Pattern Matching/Fancy Patterns/Non-Backtracking Subpatterns
##############################################################

$_ = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
/a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*[b]/;

--------------

/(?>a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*)[b]/;

--------------

#!/usr/bin/perl -00p
while ( /( (.+) ( (?<=\\) \n .* )+ ) /gx) {
    print "GOT $.: $1\n\n";
}

--------------

(.+(?:(?<=\\)\n.*)+)

--------------

((?>.+)(?:(?<=\\)\n.*)+)

##########################################################################
5:Pattern Matching/Fancy Patterns/Programmatic Patterns/Generated patterns
##########################################################################

#!/usr/bin/perl
$vowels = 'aeiouy';
$cons   = 'cbdfghjklmnpqrstvwxzy';
%map = (C => $cons, V => $vowels);  # init map for C and V

for $class ($vowels, $cons) {       # now for each type
    for (split //, $class) {        # get each letter of that type
        $map{$_} .= $class;         # and map the letter back to the type
    }
}

for $char (split //, shift) {       # for each letter in template word
    $pat .= "[$map{$char}]";        # add appropriate character class
}

$re = qr/^${pat}$/i;                # compile the pattern
print "REGEX is $re\n";             # debugging output
@ARGV = ('/usr/dict/words')         # pick a default dictionary
    if -t && !@ARGV;

while (<>) {                        # and now blaze through the input
    print if /$re/;                 # printing any line that matches
}

################################################################################
5:Pattern Matching/Fancy Patterns/Programmatic Patterns/Substitution evaluations
################################################################################

s/(\d+)/$1 * 2/;     # Replaces "42" with "42 * 2"
s/(\d+)/$1 * 2/e;    # Replaces "42" with "84"

--------------

$_ = "Preheat oven to 233C.\n";
s/\b(\d+\.?\d*)C\b/int($1 * 1.8 + 32) . "F"/e;   # convert to 451F

--------------

% perl -pi -e 's/^(\d+)(?=:)/100 + $1/e' filename

--------------

s/(\$\w+)/$1/eeg;       # Interpolate most scalars' values

--------------

$_ = "I have 4 + 19 dollars and 8/2 cents.\n";
s{ (
        \d+ \s*         # find an integer
        [+*/-]          # and an arithmetical operator
        \s* \d+         # and another integer
   )
}{ $1 }eegx;            # then expand $1 and run that code
print;                  # "I have 23 dollars and 4 cents."

##################################################################################
5:Pattern Matching/Fancy Patterns/Programmatic Patterns/Match-time code evaluation
##################################################################################

"glyph" =~ /.+ (?{ print "hi" }) ./x;  # Prints "hi" twice.

--------------

$_ = 'lothlorien';
m/  (?{ $i = 0 })                    # Set $i to 0
    (.    (?{ $i++ })    )*          # Update $i, even after backtracking
    lori                             # Forces a backtrack
 /x;

--------------

$_ = 'lothlorien';
m/  (?{ $i = 0 })
    (. (?{ local $i = $i + 1; }) )*  # Update $i, backtracking-safe.
    lori
    (?{ $result = $i })              # Copy to non-localized location.
 /x;

--------------

"glyph" =~ /.+(?(?{ $foo{bar} gt "symbol" }).|signet)./;

--------------

"glyph" =~ m{
    .+                              # some anythings
    (?(?{                           # if
            $foo{bar} gt "symbol"   # this is true
        })
            .                       # match another anything
        |                           # else
            signet                  # match signet
    )
    .                               # and one more anything
}x;

--------------

/(.*?) (?{length($1) < 3 && warn}) $suffix/;  # Error without use re 'eval'

--------------

/foo${pat}bar/

--------------

"abcdef" =~ / .+ (?{print "Matched so far: $&\n"}) bcdef $/x;

########################################################################################
5:Pattern Matching/Fancy Patterns/Programmatic Patterns/Match-time pattern interpolation
########################################################################################

/\w (??{ if ($threshold > 1) { "red" } else { "blue" } }) \d/x;

--------------

/^ (.+) .? (??{quotemeta reverse $1}) $/xi;

--------------

$text =~ /( \(+ ) (.*?) (??{ '\)' x length $1 })/x;

--------------

$np = qr{
           \(
           (?:
              (?> [^()]+ )    # Non-parens without backtracking
            |
              (??{ $np })     # Group with matching parens
           )*
           \)
        }x;

--------------

$funpat = qr/\w+$np/;
'myfunfun(1,(2*(3+4)),5)' =~ /^$funpat$/;   # Matches!

#################################################################################
5:Pattern Matching/Fancy Patterns/Programmatic Patterns/Conditional interpolation
#################################################################################

#!/usr/bin/perl
$x = 'Perl is free.';
$y = 'ManagerWare costs $99.95.';

foreach ($x, $y) {
    /^(\w+) (?:is|(costs)) (?(2)(\$\d+)|\w+)/;  # Either (\$\d+) or \w+
    if ($3) {
        print "$1 costs money.\n";         # ManagerWare costs money.
    } else {
        print "$1 doesn't cost money.\n";  # Perl doesn't cost money.
    }
}

--------------

/[ATGC]+(?(?<=AA)G|C)$/;

##############################################################
5:Pattern Matching/Fancy Patterns/Defining Your Own Assertions
##############################################################

use Tagger;
$_ = '<I>camel</I>';
print "Tagged camel found" if /\tag\w+\tag/;

--------------

package Tagger;
use overload;

sub import { overload::constant 'qr' => \&convert }

sub convert {
    my $re = shift;
    $re =~ s/ \\tag  /<.*?>/xg;
    $re =~ s/ \\w    /[A-Za-z]/xg;
    return $re;
}

1;

--------------

$re = '\tag\w+\tag';   # This string begins with \t, a tab
print if /$re/;        # Matches a tab, followed by an "a"...

--------------

$re = '\tag\w+\tag';         # This string begins with \t, a tab
$re = Tagger::convert $re;   # expand \tag and \w
print if /$re/;              # $re becomes <.*?>[A-Za-z]+<.*?>

#######################
6:Subroutines/Semantics
#######################

sub razzle {
    print "Ok, you've been razzled.\n";
}

--------------

razzle();

###################################################
6:Subroutines/Semantics/Tricks with Parameter Lists
###################################################

sub maysetenv {
    my ($key, $value) = @_;
    $ENV{$key} = $value unless $ENV{$key};
}

--------------

sub max {
    my $max = shift(@_);
    for my $item (@_) {
        $max = $item if $max < $item;
    }
    return $max;
}

$bestday = max($mon,$tue,$wed,$thu,$fri);

--------------

sub configuration {
    my %options = @_;
    print "Maximum verbosity.\n" if $options{VERBOSE} == 9;
}

configuration(PASSWORD => "xyzzy", VERBOSE => 9, SCORE => 0);

--------------

upcase_in($v1, $v2);  # this changes $v1 and $v2
sub upcase_in {
    for (@_) { tr/a-z/A-Z/ }
}

--------------

upcase_in("frederick");

--------------

($v3, $v4) = upcase($v1, $v2);
sub upcase {
    my @parms = @_;
    for (@parms) { tr/a-z/A-Z/ }
    # Check whether we were called in list context.
    return wantarray ? @parms : $parms[0];
}

--------------

@newlist = upcase(@list1, @list2);
@newlist = upcase( split /:/, $var );

--------------

(@a, @b) = upcase(@list1, @list2);   # WRONG

#########################################
6:Subroutines/Semantics/Error Indications
#########################################

if ($something_went_awry) {
    return if defined wantarray;  # good, not void context.
    die "Pay attention to my error, you danglesocket!!!\n";
}

######################################
6:Subroutines/Semantics/Scoping Issues
######################################

&foo(1,2,3);    # pass three arguments
foo(1,2,3);     # the same

foo();          # pass a null list
&foo();         # the same

&foo;           # foo() gets current args, like foo(@_), but faster!
foo;            # like foo() if sub foo predeclared, else bareword "foo"

--------------

# top of file
my $x = 10;         # declare and initialize variable
sub bumpx { $x++ }  # function can see outer lexical variable

--------------

{
    my $counter = 0;
    sub next_counter { return ++$counter }
    sub prev_counter { return --$counter }
}

--------------

BEGIN {
    my @scale = ('A' .. 'G');
    my $note  = -1;
    sub next_pitch { return $scale[ ($note += 1) %= @scale ] };
}

################################
6:Subroutines/Passing References
################################

$total = sum ( \@a );

sub sum {
    my ($aref)  = @_;
    my ($total) = 0;
    foreach (@$aref) { $total += $_ }
    return $total;
}

--------------

@tailings = popmany ( \@a, \@b, \@c, \@d );

sub popmany {
    my @retlist = ();
    for my $aref (@_) {
        push @retlist, pop @$aref;
    }
    return @retlist;
}

--------------

@common = inter( \%foo, \%bar, \%joe );
sub inter {
    my %seen;
    for my $href (@_) {
        while (my $k = each %$href ) {
            $seen{$k}++;
        }
    }
    return grep { $seen{$_} == @_ } keys %seen;
}

--------------

(@a, @b) = func(@c, @d);

--------------

(%a, %b) = func(%c, %d);

--------------

($aref, $bref) = func(\@c, \@d);
print "@$aref has more than @$bref\n";
sub func {
    my ($cref, $dref) = @_;
    if (@$cref > @$dref) {
        return ($cref, $dref);
    } else {
        return ($dref, $cref);
    }
}

########################
6:Subroutines/Prototypes
########################

sub mypush (\@@);

--------------

use Symbol 'qualify_to_ref';

sub foo (*) {
    my $fh = qualify_to_ref(shift, caller);
    ...
}

--------------

mytime +2;

--------------

sub try (&$) {
    my ($try, $catch) = @_;
    eval { &$try };
    if ($@) {
        local $_ = $@;
        &$catch;
    }
}
sub catch (&) { $_[0] }

try {
    die "phooey";
}                   # not the end of the function call!
catch {
    /phooey/ and print "unphooey\n";
};

--------------

sub mygrep (&@) {
    my $coderef = shift;
    my @result;
    foreach $_ (@_) {
        push(@result, $_) if &$coderef;
    }
    return @result;
}

####################################################
6:Subroutines/Prototypes/Inlining Constant Functions
####################################################

sub pi ()           { 3.14159 }             # Not exact, but close.
sub PI ()           { 4 * atan2(1, 1) }     # As good as it gets

--------------

sub FLAG_FOO ()     { 1 << 8 }
sub FLAG_BAR ()     { 1 << 9 }
sub FLAG_MASK ()    { FLAG_FOO | FLAG_BAR }

sub OPT_GLARCH ()   { (0x1B58 & FLAG_MASK) == 0 }
sub GLARCH_VAL () {
    if (OPT_GLARCH) { return 23 }
    else            { return 42 }
}

sub N () { int(GLARCH_VAL) / 3 }
BEGIN {                 # compiler runs this block at compile time
    my $prod = 1;       # persistent, private variable
    for (1 .. N) { $prod *= $_ }
    sub NFACT () { $prod }
}

--------------

sub not_inlined () {
    return 23 || $$;
}

#############################################
6:Subroutines/Prototypes/Care with Prototypes
#############################################

sub func ($) {
    my $n = shift;
    print "you gave me $n\n";
}

--------------

func @foo;                  # counts @foo elements
func split /:/;             # counts number of fields returned
func "a", "b", "c";         # passes "a" only, discards "b" and "c"
func("a", "b", "c");        # suddenly, a compiler error!

--------------

sub func (\$) {
    my $nref = shift;
    print "you gave me $$nref\n";
}

--------------

func @foo;              # compiler error, saw @, want $
func split/:/;          # compiler error, saw function, want $
func $s;                # this one is ok -- got real $ symbol
func $a[3];             # and this one
func $h{stuff}[-1];     # or even this
func 2+5;               # scalar expr still a compiler error
func ${ \(2+5) };       # ok, but is the cure worse than the disease?

##########################################################################
6:Subroutines/Subroutine Attributes/The C<locked> and C<method> Attributes
##########################################################################

# Only one thread is allowed into this function.
sub afunc : locked { ... }

# Only one thread is allowed into this function on a given object.
sub afunc : locked method { ... }

--------------

sub afunc : method { ... }

--------------

sub fnord (&\%) : switch(10,foo(7,3))  :  expensive;
sub plugh () : Ugly('\(") :Bad;
sub xyzzy : _5x5 { ... }

--------------

sub fnord : switch(10,foo();  # ()-string not balanced
sub snoid : Ugly('(');        # ()-string not balanced
sub xyzzy : 5x5;              # "5x5" not a valid identifier
sub plugh : Y2::north;        # "Y2::north" not a simple identifier
sub snurt : foo + bar;        # "+" not a colon or space

###########################################################
6:Subroutines/Subroutine Attributes/The C<lvalue> attribute
###########################################################

my $val;
sub canmod : lvalue {
    $val;
}
sub nomod {
    $val;
}

canmod() = 5;   # Assigns to $val.
nomod()  = 5;   # ERROR

--------------

canmod $x  = 5;     # assigns 5 to $x first!
canmod 42  = 5;     # can't change a constant; compile-time error
canmod($x) = 5;     # this is ok
canmod(42) = 5;     # and so is this

--------------

canmod = 5;

--------------

$obj->canmod = 5;

--------------

data(2,3) = get_data(3,4);

--------------

(data(2,3)) = get_data(3,4);

--------------

(data(2),data(3)) = get_data(3,4);

#########
7:Formats
#########

$value =~ tr/\n\t\f/ /;

--------------

# a report on the /etc/passwd file
format STDOUT_TOP =
                         Passwd File
Name                Login    Office   Uid   Gid Home
------------------------------------------------------------------
.
format STDOUT =
@<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<<
$name,              $login,  $office,$uid,$gid, $home
.

# a report from a bug report form
format STDOUT_TOP =
                         Bug Reports
@<<<<<<<<<<<<<<<<<<<<<<<     @|||         @>>>>>>>>>>>>>>>>>>>>>>>
$system,                      $%,         $date
------------------------------------------------------------------
.
format STDOUT =
Subject: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
         $subject
Index: @<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
       $index,                       $description
Priority: @<<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
          $priority,        $date,   $description
From: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      $from,                         $description
Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
             $programmer,            $description
~                                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                     $description
~                                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                     $description
~                                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                     $description
~                                    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                                     $description
~                                    ^<<<<<<<<<<<<<<<<<<<<<<<...
                                     $description
.

###########################
7:Formats//Format Variables
###########################

select((select(OUTF), 
        $~ = "My_Other_Format",
        $^ = "My_Top_Format"
       )[0]);

--------------

$ofh = select(OUTF);
$~ = "My_Other_Format";
$^ = "My_Top_Format";
select($ofh);

--------------

use English;
$ofh = select(OUTF);
$FORMAT_NAME     = "My_Other_Format";
$FORMAT_TOP_NAME = "My_Top_Format";
select($ofh);

--------------

use FileHandle;
OUTF->format_name("My_Other_Format");
OUTF->format_top_name("My_Top_Format");

--------------

format Ident = 
    @<<<<<<<<<<<<<<<
    commify($n)
.

--------------

format Ident = 
I have an @ here.
         "@"
.

--------------

format Ident = 
@||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
                          "Some text line"
.

--------------

$format  = "format STDOUT = \n"
         . '^' . '<' x $cols . "\n"
         . '$entry' . "\n"
         . "\t^" . "<" x ($cols-8) . "~~\n"
         . '$entry' . "\n"
         . ".\n";
print $format if $Debugging;
eval $format; 
die $@ if $@;

--------------

format STDOUT = 
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$entry
    ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~
$entry
.

--------------

format = 
^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ~~
$_

.

$/ = "";
while (<>) {
    s/\s*\n\s*/ /g;
    write;
}

#########################################
7:Formats//Accessing Formatting Internals
#########################################

$str = formline <<'END', 1,2,3;
@<<<  @|||  @>>>
END

print "Wow, I just stored `$^A' in the accumulator!\n";

--------------

use Carp;
sub swrite {
    croak "usage: swrite PICTURE ARGS" unless @_;
    my $format = shift;
    $^A = "";
    formline($format, @_);
    return $^A;

$string = swrite(<<'END', 1, 2, 3);
Check me out
@<<<  @|||  @>>>
END
print $string;

--------------

use FileHandle;
STDOUT->formline("^" . ("<" x 72) . "~~\n", $long_text);


############
8:References
############

@john = (47, "brown", 186);
@mary = (23, "hazel", 128);
@bill = (35, "blue",  157);

--------------

@vitals = ('john', 'mary', 'bill');

#######################################################
8:References/Creating References/The Backslash Operator
#######################################################

$scalarref = \$foo;
$constref  = \186_282.42;
$arrayref  = \@ARGV;
$hashref   = \%ENV;
$coderef   = \&handler;
$globref   = \*STDOUT;

############################################################################
8:References/Creating References/Anonymous Data/The anonymous array composer
############################################################################

$arrayref = [1, 2, ['a', 'b', 'c', 'd']];

--------------

$table = [ [ "john", 47, "brown", 186],
           [ "mary", 23, "hazel", 128],
           [ "bill", 35, "blue",  157] ];

###########################################################################
8:References/Creating References/Anonymous Data/The anonymous hash composer
###########################################################################

$hashref = {
    'Adam'   => 'Eve',
    'Clyde'  => $bonnie,
    'Antony' => 'Cleo' . 'patra',
};

--------------

$table = {
            "john" => [ 47, "brown", 186 ],
            "mary" => [ 23, "hazel", 128 ],
            "bill" => [ 35, "blue",  157 ],
};

--------------

$table = {
           "john" => { age    => 47,
                       eyes   => "brown",
                       weight => 186,
                     },
           "mary" => { age    => 23,
                       eyes   => "hazel",
                       weight => 128,
                     },
           "bill" => { age    => 35,
                       eyes   => "blue",
                       weight => 157,
                     },
 };

--------------

sub hashem {        { @_ } }   # Silently WRONG -- returns @_.
sub hashem {       +{ @_ } }   # Ok.
sub hashem { return { @_ } }   # Ok.

#################################################################################
8:References/Creating References/Anonymous Data/The anonymous subroutine composer
#################################################################################

$coderef = sub { print "Boink!\n" };  # Now &$coderef prints "Boink!"

####################################################
8:References/Creating References/Object Constructors
####################################################

$objref = Doggie::->new(Tail => 'short', Ears => 'long');  #1
$objref = new Doggie:: Tail => 'short', Ears => 'long';    #2
$objref = Doggie->new(Tail => 'short', Ears => 'long');    #3
$objref = new Doggie Tail => 'short', Ears => 'long';      #4

##################################################
8:References/Creating References/Handle References
##################################################

splutter(\*STDOUT);

sub splutter {
    my $fh = shift;
    print $fh "her um well a hmmm\n";
}

$rec = get_rec(\*STDIN);
sub get_rec {
    my $fh = shift;
    return scalar <$fh>;
}

--------------

for $file (@names) {
    local *FH;
    open(*FH, $file) || next;
    $handle{$file} = *FH;
}

--------------

for $file (@names) {
    my $fh;
    open($fh, $file) || next;
    $handle{$file} = $fh;
}

########################################################
8:References/Creating References/Symbol Table References
########################################################

$scalarref = *foo{SCALAR};   # Same as \$foo
$arrayref  = *ARGV{ARRAY};   # Same as \@ARGV
$hashref   = *ENV{HASH};     # Same as \%ENV
$coderef   = *handler{CODE}; # Same as \&handler
$globref   = *foo{GLOB};     # Same as \*foo
$ioref     = *STDIN{IO};     # Er...

--------------

splutter(*STDOUT);
splutter(*STDOUT{IO});

sub splutter {
    my $fh = shift;
    print $fh "her um well a hmmm\n";
}

######################################################################
8:References/Using Hard References/Using a Variable as a Variable Name
######################################################################

$foo         = "three humps";
$scalarref   = \$foo;         # $scalarref is now a reference to $foo
$camel_model = $$scalarref;   # $camel_model is now "three humps"

--------------

$bar = $$scalarref;

push(@$arrayref, $filename);
$$arrayref[0] = "January";            # Set the first element of @$arrayref
@$arrayref[4..6] = qw/May June July/; # Set several elements of @$arrayref

%$hashref = (KEY => "RING", BIRD => "SING");  # Initialize whole hash
$$hashref{KEY} = "VALUE";                     # Set one key/value pair
@$hashref{"KEY1","KEY2"} = ("VAL1","VAL2");   # Set several pairs

&$coderef(1,2,3);

print $handleref "output\n";

--------------

$refrefref = \\\"howdy";
print $$$$refrefref;

######################################################################
8:References/Using Hard References/Using a BLOCK as a Variable Name
######################################################################

$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
@{$arrayref}[4..6] = qw/May June July/;
${$hashref}{"KEY"} = "VALUE";
@{$hashref}{"KEY1","KEY2"} = ("VAL1","VAL2");
&{$coderef}(1,2,3);

--------------

$refrefref = \\\"howdy";
print ${${${$refrefref}}};

--------------

&{ $dispatch{$index} }(1, 2, 3);

###########################################################
8:References/Using Hard References/Using the Arrow Operator
###########################################################

$  $arrayref  [2] = "Dorian";         #1
${ $arrayref }[2] = "Dorian";         #2
   $arrayref->[2] = "Dorian";         #3

$  $hashref  {KEY} = "F#major";       #1
${ $hashref }{KEY} = "F#major";       #2
   $hashref->{KEY} = "F#major";       #3

&  $coderef  (Presto => 192);         #1
&{ $coderef }(Presto => 192);         #2
   $coderef->(Presto => 192);         #3

--------------

print $array[3]->{"English"}->[0];

--------------

$array[3]->{"English"}->[0] = "January";

--------------

$dispatch{$index}(1, 2, 3);
$array[3]{"English"}[0] = "January";

--------------

$answer[$x][$y][$z] += 42;

--------------

 $listref->[2][2] = "hello";    # Pretty clear
$$listref[2][2]   = "hello";    # A bit confusing

--------------

$listref[2]->[$greeting] = "hello";

###############################################
8:References/Using Hard References/Pseudohashes
###############################################

$john = [ {age => 1, eyes => 2, weight => 3}, 47, "brown", 186 ];

--------------

$john->{weight}             # Treats $john as a hashref
$john->[3]                  # Treats $john as an arrayref

--------------

$john->[0]{height} = 4;     # height is to be element 4
$john->{height} = "tall";   # Or $john->[4] = "tall"

--------------

delete $john->[0]{height};  # Deletes from the underlying hash only
$john->{height};            # This now raises an exception
$john->[4];                 # Still prints "tall"

--------------

use fields;
$ph = fields::phash(age => 47, eyes => "brown", weight => 186);
print $ph->{age};

--------------

use fields;
$ph= fields::phash([qw(age eyes brown)], [47]);
$ph->{eyes} = undef;

print exists $ph->{age};     # True, 'age' was set in declaration.
print exists $ph->{weight};  # False, 'weight' has not been used.
print exists $ph->{eyes};    # True, your 'eyes' have been touched.

--------------

print exists $ph->[0]{age};   # True, 'page' is a valid field
print exists $ph->[0]{name};  # False, 'name' can't be used

--------------

print delete $ph->{age};     # Removes and returns $ph->[1], 47
print exists $ph->{age};     # Now false
print exists $ph->[0]{age};  # True, 'age' key still usable
print delete $ph->[0]{age};  # Now 'age' key is gone
print $ph->{age};            # Run-time exception

###############################################################################
8:References/Using Hard References/Other Tricks You Can Do with Hard References
###############################################################################

@reflist = (\$s, \@a, \%h, \&f);     # List of four references
@reflist = \($s,  @a   %h,  &f);     # Same thing

--------------

@reflist = \(@x);                    # Interpolate array, then get refs
@reflist = map { \$_ } @x;           # Same thing

--------------

@reflist = \(@x, (@y));            # But only single aggregates expand
@reflist = (\@x, map { \$_ } @y);    # Same thing

--------------

@envrefs = \@ENV{'HOME', 'TERM'};         # Backslashing a slice
@envrefs = \( $ENV{HOME},  $ENV{TERM} );  # Backslashing a list
@envrefs = ( \$ENV{HOME}, \$ENV{TERM} );  # A list of two references

--------------

@reflist = \fx();
@reflist = map { \$_ } fx();                # Same thing

@reflist = \( fx(), fy(), fz() );
@reflist = ( \fx(), \fy(), \fz() );         # Same thing
@reflist = map { \$_ } fx(), fy(), fz();    # Same thing

--------------

@reflist = \localtime();      # Ref to each of nine time elements
$lastref = \localtime();      # Ref to whether it's daylight savings time

--------------

$dateref = \scalar localtime();    # \"Sat Jul 16 11:42:18 2000"

--------------

sub sum {
    my $arrayref = shift;
    warn "Not an array reference" if ref($arrayref) ne "ARRAY";
    return eval join("+", @$arrayref);
}

--------------

print "My sub returned @{[ mysub(1,2,3) ]} that time.\n";

--------------

print "We need @{ [$n + 5] } widgets!\n";

--------------

print "mysub returns @{ [scalar mysub(1,2,3)] } now.\n";

###########################################
8:References/Using Hard References/Closures
###########################################

{
    my $critter = "camel";
    $critterref = \$critter;
}

--------------

{
    my $critter = "camel";
    $critterref = sub { return $critter };
}

--------------

sub make_saying  {
    my $salute = shift;
    my $newfunc = sub {
        my $target = shift;
        print "$salute, $target!\n";
    };
    return $newfunc;            # Return a closure
}

$f = make_saying("Howdy");      # Create a closure
$g = make_saying("Greetings");  # Create another closure

# Time passes...

$f->("world");
$g->("earthlings");

--------------

sub get_method_ref {
    my ($self, $methodname) = @_;
    my $methref = sub {
        # the @_ below is not the same as the one above!
        return $self->$methodname(@_);
    };
    return $methref;
}

my $dog = new Doggie::
            Name => "Lucky",
            Legs => 3,
            Tail => "clipped";

our $wagger = get_method_ref($dog, 'wag');
$wagger->("tail");        # Calls $dog->wag('tail').

##########################################################################
8:References/Using Hard References/Closures/Closures as function templates
##########################################################################

print "Be ", red("careful"), "with that ", green("light"), "!!!";

--------------

@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
    no strict 'refs';       # Allow symbolic references
    *$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" };
}

--------------

*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };

##############################################################
8:References/Using Hard References/Closures/Nested subroutines
##############################################################

sub outer {
    my $x = $_[0] + 35;
    local *inner = sub { return $x * 19 };
    return $x + inner();
}

--------------

sub outer {
    my $x = $_[0] + 35;
    my $inner = sub { return $x * 19 };
    return $x + $inner->();
}

################################
8:References/Symbolic References
################################

$name = "bam";
$$name = 1;                # Sets $bam
$name->[0] = 4;            # Sets the first element of @bam
$name->{X} = "Y";          # Sets the X element of %bam to Y
@$name = ();               # Clears @bam
keys %$name;               # Yields the keys of %bam
&$name;                    # Calls &bam

--------------

use strict 'refs';

--------------

no strict 'refs';

--------------

${identifier};    # Same as $identifier.
${"identifier"};  # Also $identifier, but a symbolic reference.

--------------

our $value  = "global";
{
    my $value = "private";
    print "Inside, mine is ${value}, ";
    print "but ours is ${'value'}.\n";
}
print "Outside, ${value} is again ${'value'}.\n";

##########################################
8:References/Braces, Brackets, and Quoting
##########################################

$push = "pop on ";
print "${push}over";

--------------

print ${push} . 'over';

--------------

print ${ push } . 'over';

--------------

$hash{ "aaa" }{ "bbb" }{ "ccc" }

--------------

$hash{ aaa }{ bbb }{ ccc }

--------------

$hash{aaa}{bbb}{ccc}

--------------

$hash{ shift }

--------------

$hash{  shift()  }
$hash{ +shift   }
$hash{  shift @_ }

#############################################################################
8:References/Braces, Brackets, and Quoting/References Don't Work as Hash Keys
#############################################################################

$x{ \$a } = $a;
($key, $value) = each %x;
print $$key;                # WRONG

--------------

$r = \@a;
$x{ $r } = $r;

--------------

use Tie::RefHash;
tie my %h, 'Tie::RefHash';
%h = (
    ["this", "here"]   => "at home",
    ["that", "there"]  => "elsewhere",
);
while ( my($keyref, $value) = each %h ) {
    print "@$keyref is $value\n";
}

#######################################################################################################
8:References/Braces, Brackets, and Quoting/Garbage Collection, Circular References, and Weak References
#######################################################################################################

{               # make $a and $b point to each other
    my ($a, $b);
    $a = \$b;
    $b = \$a;
}

--------------

{               # make $a point to itself
    my $a;
    $a = \$a;
}

#################################################################################################################
9:Data Structures/A Brief Tutorial:  Manipulating Arrays of Arrays/Creating and Accessing a Two-Dimensional Array
#################################################################################################################

# Assign a list of array references to an array.
@AoA = (
         [ "fred", "barney" ],
         [ "george", "jane", "elroy" ],
         [ "homer", "marge", "bart" ],
);

print $AoA[2][1];   # prints "marge"

--------------

# Create an reference to an array of array references.
$ref_to_AoA = [
    [ "fred", "barney", "pebbles", "bamm bamm", "dino", ],
    [ "homer", "bart", "marge", "maggie", ],
    [ "george", "jane", "elroy", "judy", ],
];

print $ref_to_AoA->[2][3];   # prints "judy"

--------------

$AoA[2][3]
$ref_to_AoA->[2][3]

--------------

$AoA[2]->[3]
$ref_to_AoA->[2]->[3]

--------------

$AoA[0][-2]

###################################################################################
9:Data Structures/A Brief Tutorial:  Manipulating Arrays of Arrays/Growing Your Own
###################################################################################

while (<>) {
    @tmp = split;           # Split elements into an array.
    push @AoA, [ @tmp ];    # Add an anonymous array reference to @AoA.
}

--------------

while (<>) {
    push @AoA, [ split ];
}

--------------

while (<>) {
    push @$ref_to_AoA, [ split ];
}

--------------

for $x (0 .. 9) {                       # For each row...
    for $y (0 .. 9) {                   # For each column...
        $AoA[$x][$y] = func($x, $y);    # ...set that cell
    }
}

for $x ( 0..9 ) {                       # For each row...
    $ref_to_AoA->[$x][3] = func2($x);   # ...set the fourth column
}

--------------

# Append new columns to an existing row.
push @{ $AoA[0] }, "wilma", "betty";

--------------

push $AoA[0], "wilma", "betty";  # WRONG!

######################################################################################
9:Data Structures/A Brief Tutorial:  Manipulating Arrays of Arrays/Access and Printing
######################################################################################

print $AoA[3][2];

--------------

print @AoA;         # WRONG

--------------

for $row ( @AoA ) {
    print "@$row\n";
}

--------------

for $i ( 0 .. $#AoA ) {
    print "row $i is: @{$AoA[$i]}\n";
}

--------------

for $i ( 0 .. $#AoA ) {
    for $j ( 0 .. $#{$AoA[$i]} ) {
        print "element $i $j is $AoA[$i][$j]\n";
    }
}

--------------

for $i ( 0 .. $#AoA ) {
    $row = $AoA[$i];
    for $j ( 0 .. $#{$row} ) {
        print "element $i $j is $row->[$j]\n";
    }
}

#########################################################################
9:Data Structures/A Brief Tutorial:  Manipulating Arrays of Arrays/Slices
#########################################################################

@part = ();
for ($y = 7; $y < 13; $y++) {
    push @part, $AoA[4][$y];
}

--------------

@part = @{ $AoA[4] } [ 7..12 ];

--------------

@newAoA = ();
for ($startx = $x = 4; $x <= 8; $x++) {
    for ($starty = $y = 7; $y <= 12; $y++) {
        $newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y];
    }
}

--------------

for ($x = 4; $x <= 8; $x++) {
    push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ];
}

##################################################################################
9:Data Structures/A Brief Tutorial:  Manipulating Arrays of Arrays/Common Mistakes
##################################################################################

@AoA = ( [2, 3], [4, 5, 7], [0] );
print "@AoA";

--------------

print $AoA[1][2];

--------------

for $i (1..10) {
    @array = somefunc($i);
    $AoA[$i] = @array;       # WRONG!
}

--------------

for $i (1..10) {
    @array = somefunc($i);
    $AoA[$i] = \@array;      # WRONG AGAIN!
}

--------------

for $i (1..10) {
    @array = somefunc($i);
    $AoA[$i] = [ @array ];   # RIGHT!
}

--------------

for $i (1..10) {
    @array = somefunc($i);
    @{$AoA[$i]} = @array;
}

--------------

$AoA[3] = \@original_array;

--------------

@{$AoA[3]} = @array;

--------------

for $i (1..10) {
    my @array = somefunc($i);
    $AoA[$i] = \@array;
}

--------------

for $i (1..10) {
    $AoA[$i] = [ somefunc($i) ];
}

--------------

$AoA[$i] = [ @array ];   # Safest, sometimes fastest
$AoA[$i] = \@array;      # Fast but risky, depends on my-ness of array
@{ $AoA[$i] } = @array;  # Too tricky for most uses

##################################################################
9:Data Structures/Hashes of Arrays/Composition of a Hash of Arrays
##################################################################

# We customarily omit quotes when the keys are identifiers.
%HoA = (
    flintstones    => [ "fred", "barney" ],
    jetsons        => [ "george", "jane", "elroy" ],
    simpsons       => [ "homer", "marge", "bart" ],
);

--------------

$HoA{teletubbies} = [ "tinky winky", "dipsy", "laa-laa", "po" ];

#################################################################
9:Data Structures/Hashes of Arrays/Generation of a Hash of Arrays
#################################################################

while ( <> ) {
    next unless s/^(.*?):\s*//;
    $HoA{$1} = [ split ];
}

while ( $line = <> ) {
    ($who, $rest) = split /:\s*/, $line, 2;
    @fields = split ' ', $rest;
    $HoA{$who} = [ @fields ];
}

--------------

for $group ( "simpsons", "jetsons", "flintstones" ) {
    $HoA{$group} = [ get_family($group) ];
}

for $group ( "simpsons", "jetsons", "flintstones" ) {
    @members = get_family($group);
    $HoA{$group} = [ @members ];
}

--------------

push @{ $HoA{flintstones} }, "wilma", "pebbles";

##########################################################################
9:Data Structures/Hashes of Arrays/Access and Printing of a Hash of Arrays
##########################################################################

$HoA{flintstones}[0] = "Fred";

--------------

$HoA{simpsons}[1] =~ s/(\w)/\u$1/;

--------------

for $family ( keys %HoA ) {
    print "$family: @{ $HoA{$family} }\n";
}

--------------

for $family ( keys %HoA ) {
    print "$family: ";
    for $i ( 0 .. $#{ $HoA{$family} } ) {
        print " $i = $HoA{$family}[$i]";
    }
    print "\n";
}

--------------

for $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
    print "$family: @{ $HoA{$family} }\n"
}

--------------

# Print the whole thing sorted by number of members and name.
for $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
    print "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
}

####################################################################
9:Data Structures/Arrays of Hashes/Composition of an Array of Hashes
####################################################################

@AoH = (
    {
       husband  => "barney",
       wife     => "betty",
       son      => "bamm bamm",
    },
    {
       husband => "george",
       wife    => "jane",
       son     => "elroy",
    },
    {
       husband => "homer",
       wife    => "marge",
       son     => "bart",
    },
  );

--------------

push @AoH, { husband => "fred", wife => "wilma", son => "junior" };

###################################################################
9:Data Structures/Arrays of Hashes/Generation of an Array of Hashes
###################################################################

while ( <> ) {
    $rec = {};
    for $field ( split ) {
        ($key, $value) = split /=/, $field;
        $rec->{$key} = $value;
    }
    push @AoH, $rec;
}

while ( <> ) {
    push @AoH, { split /[\s=]+/ };
}

--------------

while ( @fields = get_next_pair() ) {
    push @AoH, { @fields };
}

while (<>) {
    push @AoH, { get_next_pair($_) };
}

--------------

$AoH[0]{pet} = "dino";
$AoH[2]{pet} = "santa's little helper";

############################################################################
9:Data Structures/Arrays of Hashes/Access and Printing of an Array of Hashes
############################################################################

$AoH[0]{husband} = "fred";

--------------

$AoH[1]{husband} =~ s/(\w)/\u$1/;

--------------

for $href ( @AoH ) {
    print "{ ";
    for $role ( keys %$href ) {
         print "$role=$href->{$role} ";
    }
    print "}\n";
}

--------------

for $i ( 0 .. $#AoH ) {
    print "$i is { ";
    for $role ( keys %{ $AoH[$i] } ) {
         print "$role=$AoH[$i]{$role} ";
    }
    print "}\n";
}

##################################################################
9:Data Structures/Hashes of Hashes/Composition of a Hash of Hashes
##################################################################

%HoH = (
    flintstones => {
        husband   => "fred",
        pal       => "barney",
    },
    jetsons => {
        husband   => "george",
        wife      => "jane",
        "his boy" => "elroy",  # Key quotes needed.
    },
    simpsons => {
        husband   => "homer",
        wife      => "marge",
        kid       => "bart",
    },
);

--------------

$HoH{ mash } = {
    captain  => "pierce",
    major    => "burns",
    corporal => "radar",
};

#################################################################
9:Data Structures/Hashes of Hashes/Generation of a Hash of Hashes
#################################################################

while ( <> ) {
    next unless s/^(.*?):\s*//;
    $who = $1;
    for $field ( split ) {
        ($key, $value) = split /=/, $field;
        $HoH{$who}{$key} = $value;
    }
}

while ( <> ) {
    next unless s/^(.*?):\s*//;
    $who = $1;
    $rec = {};
    $HoH{$who} = $rec;
    for $field ( split ) {
        ($key, $value) = split /=/, $field;
        $rec->{$key} = $value;
    }
}

--------------

for $group ( "simpsons", "jetsons", "flintstones" ) {
    $HoH{$group} = { get_family($group) };
}

for $group ( "simpsons", "jetsons", "flintstones" ) {
    @members = get_family($group);
    $HoH{$group} = { @members };
}

sub hash_families {
    my @ret;
    for $group ( @_ ) {
        push @ret, $group, { get_family($group) };
    }
    @ret;
}
%HoH = hash_families( "simpsons", "jetsons", "flintstones" );

--------------

%new_folks = (
    wife => "wilma",
    pet  => "dino";
);
for $what (keys %new_folks) {
    $HoH{flintstones}{$what} = $new_folks{$what};
}

##########################################################################
9:Data Structures/Hashes of Hashes/Access and Printing of a Hash of Hashes
##########################################################################

$HoH{flintstones}{wife} = "wilma";

--------------

$HoH{jetsons}{'his boy'} =~ s/(\w)/\u$1/;

--------------

for $family ( keys %HoH ) {
    print "$family: ";
    for $role ( keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
    }
    print "\n";
}

--------------

while ( ($family, $roles) = each %HoH ) {
    print "$family: ";
    while ( ($role, $person) = each %$roles ) {
        print "$role=$person ";
    }
    print "\n";
}

--------------

for $family ( sort keys %HoH ) {
    print "$family: ";
    for $role ( sort keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
    }
    print "\n";
}

--------------

for $family ( sort { keys %{$HoH{$a}} <=> keys %{$HoH{$b}} } keys %HoH ) {
    print "$family: ";
    for $role ( sort keys %{ $HoH{$family} } ) {
         print "$role=$HoH{$family}{$role} ";
    }
    print "\n";
}

--------------

$i = 0;
for ( qw(husband wife son daughter pal pet) ) { $rank{$_} = ++$i }

for $family ( sort { keys %{$HoH{$a}} <=> keys %{$HoH{$b}} } keys %HoH ) {
    print "$family: ";
    for $role ( sort { $rank{$a} <=> $rank{$b} } keys %{ $HoH{$family} } ) {
        print "$role=$HoH{$family}{$role} ";
    }
    print "\n";
}

#####################################
9:Data Structures/Hashes of Functions
#####################################

if    ($cmd =~ /^exit$/i)     { exit }
elsif ($cmd =~ /^help$/i)     { show_help() }
elsif ($cmd =~ /^watch$/i)    { $watch = 1 }
elsif ($cmd =~ /^mail$/i)     { mail_msg($msg) }
elsif ($cmd =~ /^edit$/i)     { $edited++; editmsg($msg); }
elsif ($cmd =~ /^delete$/i)   { confirm_kill() }
else {
    warn "Unknown command: `$cmd'; Try `help' next time\n";
}

--------------

%HoF = (                           # Compose a hash of functions
    exit    =>  sub { exit },
    help    =>  \&show_help,
    watch   =>  sub { $watch = 1 },
    mail    =>  sub { mail_msg($msg) },
    edit    =>  sub { $edited++; editmsg($msg); },
    delete  =>  \&confirm_kill,
);

if   ($HoF{lc $cmd}) { $HoF{lc $cmd}->() }   # Call function
else { warn "Unknown command: `$cmd'; Try `help' next time\n" }

####################################################################################################
9:Data Structures/More Elaborate Records/Composition, Access, and Printing of More Elaborate Records
####################################################################################################

$rec = {
    TEXT      => $string,
    SEQUENCE  => [ @old_values ],
    LOOKUP    => { %some_table },
    THATCODE  => \&some_function,
    THISCODE  => sub { $_[0] ** $_[1] },
    HANDLE    => \*STDOUT,
};

--------------

print $rec->{TEXT};

--------------

print $rec->{SEQUENCE}[0];
$last = pop @{ $rec->{SEQUENCE} };

print $rec->{LOOKUP}{"key"};
($first_k, $first_v) = each %{ $rec->{LOOKUP} };

--------------

$that_answer = $rec->{THATCODE}->($arg1, $arg2);
$this_answer = $rec->{THISCODE}->($arg1, $arg2);

--------------

print { $rec->{HANDLE} } "a string\n";

--------------

use FileHandle;
$rec->{HANDLE}->autoflush(1);
$rec->{HANDLE}->print("a string\n");

#########################################################################################################
9:Data Structures/More Elaborate Records/Composition, Access, and Printing of Even More Elaborate Records
#########################################################################################################

%TV = (
    flintstones => {
        series   => "flintstones",
        nights   => [ "monday", "thursday", "friday" ],
        members  => [
            { name => "fred",    role => "husband", age  => 36, },
            { name => "wilma",   role => "wife",    age  => 31, },
            { name => "pebbles", role => "kid",     age  =>  4, },
        ],
    },

    jetsons     => {
        series   => "jetsons",
        nights   => [ "wednesday", "saturday" ],
        members  => [
            { name => "george",  role => "husband", age  => 41, },
            { name => "jane",    role => "wife",    age  => 39, },
            { name => "elroy",   role => "kid",     age  =>  9, },
        ],
     },

    simpsons    => {
        series   => "simpsons",
        nights   => [ "monday" ],
        members  => [
            { name => "homer", role => "husband", age => 34, },
            { name => "marge", role => "wife",    age => 37, },
            { name => "bart",  role => "kid",     age => 11, },
        ],
     },
  );

################################################################################
9:Data Structures/More Elaborate Records/Generation of a Hash of Complex Records
################################################################################

$rec = {};
$rec->{series} = "flintstones";
$rec->{nights} = [ find_days() ];

--------------

@members = ();
while (<>) {
     %fields = split /[\s=]+/;
     push @members, { %fields };
}
$rec->{members} = [ @members ];

--------------

$TV{ $rec->{series} } = $rec;

--------------

for $family (keys %TV) {
    my $rec = $TV{$family};   # temporary pointer
    @kids = ();
    for $person ( @{$rec->{members}} ) {
        if ($person->{role} =~ /kid|son|daughter/) {
            push @kids, $person;
        }
    }
    # $rec and $TV{$family} point to same data!
    $rec->{kids} = [ @kids ];
}

--------------

$TV{simpsons}{kids}[0]{age}++;

--------------

print $TV{simpsons}{members}[2]{age};

--------------

for $family ( keys %TV ) {
    print "the $family";
    print " is on ", join (" and ", @{ $TV{$family}{nights} }), "\n";
    print "its members are:\n";
    for $who ( @{ $TV{$family}{members} } ) {
        print " $who->{name} ($who->{role}), age $who->{age}\n";
    }
    print "children: ";
    print join (", ", map { $_->{name} } @{ $TV{$family}{kids} } );
    print "\n\n";
}

########################################
9:Data Structures/Saving Data Structures
########################################

use Data::Dumper;
$Data::Dumper::Purity = 1;       # since %TV is self-referential
open (FILE, "> tvinfo.perldata") or die "can't open tvinfo: $!";
print FILE Data::Dumper->Dump([\%TV], ['*TV']);
close FILE                       or die "can't close tvinfo: $!";

--------------

open (FILE, "< tvinfo.perldata") or die "can't open tvinfo: $!";
undef $/;                        # read in file all at once
eval <FILE>;                     # recreate %TV
die "can't recreate tv data from tvinfo.perldata: $@" if $@;
close FILE                       or die "can't close tvinfo: $!";
print $TV{simpsons}{members}[2]{age};

--------------

do "tvinfo.perldata"            or die "can't recreate tvinfo: $! $@";
print $TV{simpsons}{members}[2]{age};

###########
10:Packages
###########

$SIG{QUIT} = "Pkg::quit_catcher"; # fully qualified handler name
$SIG{QUIT} = "quit_catcher";      # implies "main::quit_catcher"
$SIG{QUIT} = *quit_catcher;       # forces current package's sub
$SIG{QUIT} = \&quit_catcher;      # forces current package's sub
$SIG{QUIT} = sub { print "Caught SIGQUIT\n" };   # anonymous sub

#########################
10:Packages/Symbol Tables
#########################

*sym = *main::variable;
*sym = $main::{"variable"};

--------------

foreach $symname (sort keys %main::) {
    local *sym = $main::{$symname};
    print "\$$symname is defined\n" if defined $sym;
    print "\@$symname is nonnull\n" if         @sym;
    print "\%$symname is nonnull\n" if         %sym;
}

--------------

$!@#$%           = 0;         # WRONG, syntax error.
${'!@#$%'}       = 1;         # Ok, though unqualified.

${'main::!@#$%'} = 2;         # Can qualify within the string.
print ${ $main::{'!@#$%'} }   # Ok, prints 2!

--------------

*dick = *richard;

--------------

*dick = \$richard;

--------------

*SomePack::dick = \&OtherPack::richard;

--------------

*units = populate() ;         # Assign \%newhash to the typeglob
print $units{kg};             # Prints 70; no dereferencing needed!

sub populate {
    my %newhash = (km => 10, kg => 70);
    return \%newhash;
}

--------------

%units = (miles => 6, stones => 11);  
fillerup( \%units );          # Pass in a reference
print $units{quarts};         # Prints 4

sub fillerup {
    local *hashsym = shift;   # Assign \%units to the typeglob
    $hashsym{quarts} = 4;     # Affects %units; no dereferencing needed!
}

--------------

*PI = \3.14159265358979;

--------------

use constant PI => 3.14159;

--------------

*PI = sub () { 3.14159 };

--------------

*sym =   *oldvar;
*sym =  \*oldvar;       # auto-dereference
*sym = *{"oldvar"};     # explicit symbol table lookup
*sym =   "oldvar";      # implicit symbol table lookup

--------------

*sym = \$frodo;
*sym = \@sam;
*sym = \%merry;
*sym = \&pippin;

--------------

*pkg::sym{SCALAR}      # same as \$pkg::sym
*pkg::sym{ARRAY}       # same as \@pkg::sym
*pkg::sym{HASH}        # same as \%pkg::sym
*pkg::sym{CODE}        # same as \&pkg::sym
*pkg::sym{GLOB}        # same as \*pkg::sym
*pkg::sym{IO}          # internal file/dir handle, no direct equivalent
*pkg::sym{NAME}        # "sym" (not a reference)
*pkg::sym{PACKAGE}     # "pkg" (not a reference)

--------------

sub identify_typeglob {
    my $glob = shift;
    print 'You gave me ', *{$glob}{PACKAGE}, '::', *{$glob}{NAME}, "\n";
}

identify_typeglob(*foo);
identify_typeglob(*bar::glarch);

#######################
10:Packages/Autoloading
#######################

sub AUTOLOAD {
    our $AUTOLOAD;
    warn "Attempt to call $AUTOLOAD failed.\n";
}

blarg(10);              # our $AUTOLOAD will be set to main::blarg
print "Still alive!\n";

--------------

sub AUTOLOAD {
    our $AUTOLOAD;
    return "I see $AUTOLOAD(@_)\n";
}

print blarg(20);        # prints: I see main::blarg(20)

--------------

sub AUTOLOAD {
    my $name = our $AUTOLOAD;
    *$AUTOLOAD = sub { print "I see $name(@_)\n" };
    goto &$AUTOLOAD;    # Restart the new routine.
}

blarg(30);              # prints: I see main::blarg(30)
glarb(40);              # prints: I see main::glarb(40)
blarg(50);              # prints: I see main::blarg(50)

--------------

sub AUTOLOAD {
    my $program = our $AUTOLOAD;
    $program =~ s/.*:://;  # trim package name
    system($program, @_);
}

--------------

date();
who('am', 'i');
ls('-l');
echo("Abadugabudabuda...");

--------------

sub date (;$$);       # Allow zero to two arguments.
sub who (;$$$$);      # Allow zero to four args.
sub ls;               # Allow any number of args.
sub echo ($@);        # Allow at least one arg.

date;
who "am", "i";
ls "-l";
echo "That's all, folks!";
########################
11:Modules/Using Modules
########################

use Fred;       # If Fred.pm has @EXPORT = qw(flintstone)
flintstone();   # ...this calls Fred::flintstone().

###########################
11:Modules/Creating Modules
###########################

package      Bestiary;
require      Exporter;

our @ISA       = qw(Exporter);
our @EXPORT    = qw(camel);    # Symbols to be exported by default
our @EXPORT_OK = qw($weight);  # Symbols to be exported on request
our $VERSION   = 1.00;         # Version number

### Include your variables and functions here

sub camel { print "One-hump dromedary" }

$weight = 1024;

1;

###########################################################
11:Modules/Creating Modules/Module Privacy and the Exporter
###########################################################

require Exporter;
our @ISA = ("Exporter");

--------------

our @EXPORT    = qw($camel %wolf ram);              # Export by default
our @EXPORT_OK = qw(leopard @llama $emu);           # Export by request
our %EXPORT_TAGS = (                                # Export as group
                     camelids => [qw($camel @llama)],
                     critters => [qw(ram $camel %wolf)],
                   );

--------------

use Bestiary;                    # Import @EXPORT symbols
use Bestiary ();                 # Import nothing
use Bestiary qw(ram @llama);     # Import the ram function and @llama array
use Bestiary qw(:camelids);      # Import $camel and @llama
use Bestiary qw(:DEFAULT);       # Import @EXPORT symbols
use Bestiary qw(/am/);           # Import $camel, @llama, and ram
use Bestiary qw(/^\$/);          # Import all scalars
use Bestiary qw(:critters !ram); # Import the critters, but exclude ram
use Bestiary qw(:critters !:camelids);
                                 # Import critters, but no camelids

--------------

BEGIN {
    require Bestiary;
    import Bestiary LIST;
}

##########################################################################################################
11:Modules/Creating Modules/Module Privacy and the Exporter/Exporting without using Export's import method
##########################################################################################################

package Bestiary;
@ISA = qw(Exporter);
@EXPORT_OK = qw ($zoo);

sub import {
    $Bestiary::zoo = "menagerie";
}

--------------

sub import {
    $Bestiary::zoo = "menagerie";
    Bestiary->export_to_level(1, @_);
}

############################################################################
11:Modules/Creating Modules/Module Privacy and the Exporter/Version checking
############################################################################

use Bestiary 3.14;   # The Bestiary must be version 3.14 or later
use Bestiary v1.0.4; # The Bestiary must be version 1.0.4 or later

####################################################################################
11:Modules/Creating Modules/Module Privacy and the Exporter/Managing unknown symbols
####################################################################################

sub export_fail {
    my $class = shift;
    carp "Sorry, these symbols are unavailable: @_";
    return @_;
}

##########################################################################################
11:Modules/Creating Modules/Module Privacy and the Exporter/Tag handling utility functions
##########################################################################################

%EXPORT_TAGS = (foo => [qw(aa bb cc)], bar => [qw(aa cc dd)]);

Exporter::export_tags('foo');     # add aa, bb and cc to @EXPORT
Exporter::export_ok_tags('bar');  # add aa, cc and dd to @EXPORT_OK

########################################
11:Modules/Overriding Built-in Functions
########################################

use subs qw(chdir chroot chmod chown);
chdir $somewhere;
sub chdir { ... }

--------------

*CORE::GLOBAL::glob = sub {
    my $pat = shift;
    my @got;
    local *D;
    if (opendir D, '.') {
        @got = grep /$pat/, readdir D;
        closedir D;
    }
    return @got;
}

package Whatever;

print <^[a-z_]+\.pm\$>;       # show all pragmas in the current directory

#######################################################################
12:Objects/Method Invocation/Method Invocation Using the Arrow Operator
#######################################################################

$mage = Wizard->summon("Gandalf");  # class method
$mage->speak("friend");             # instance method

--------------

Wizard->summon("Gandalf")->speak("friend");

--------------

$method = "summon";
$mage = Wizard->$method("Gandalf");  # Invoke Wizard->summon

$travel = $companion eq "Shadowfax" ? "ride" : "walk";
$mage->$travel("seven leagues");     # Invoke $mage->ride or $mage->walk

#####################################################################
12:Objects/Method Invocation/Method Invocation Using Indirect Objects
#####################################################################

$mage = summon Wizard "Gandalf";
$nemesis = summon Balrog home => "Moria", weapon => "whip";
move $nemesis "bridge";
speak $mage "You cannot pass";
break $staff;               # safer to use: break $staff ();

--------------

print STDERR "help!!!\n";

--------------

speak { summon Wizard "Gandalf" } "friend";

###################################################################
12:Objects/Method Invocation/Syntactic Snafus with Indirect Objects
###################################################################

enchant $sword ($pips + 2) * $cost;

--------------

($sword->enchant($pips + 2)) * $cost;

--------------

name $sword $oldname || "Glamdring";   # can't use "or" here!

--------------

$sword->name($oldname || "Glamdring");

--------------

speak $mage "friend" && enter();        # should've been "and" here!

--------------

$mage->speak("friend" && enter());

--------------

enter() if $mage->speak("friend");
$mage->speak("friend") && enter();
speak $mage "friend" and enter();

--------------

move $party->{LEADER};               # probably wrong!
move $riders[$i];                    # probably wrong!

--------------

$party->move->{LEADER};
$riders->move([$i]);

--------------

$party->{LEADER}->move;
$riders[$i]->move;

###################################################
12:Objects/Method Invocation/Package-Quoted Classes
###################################################

$obj = new ElvenRing;               # could be new("ElvenRing")
                                    # or even new(ElvenRing())
$obj = ElvenRing->new;              # could be ElvenRing()->new()

$obj = new ElvenRing::;             # always "ElvenRing"->new()
$obj = ElvenRing::->new;            # always "ElvenRing"->new()

--------------

$obj = new ElvenRing::
            name    => "Narya",
            owner   => "Gandalf",
            domain  => "fire",
            stone   => "ruby";

--------------

use ElvenRing;
require ElvenRing;

##############################
12:Objects/Object Construction
##############################

$obj = { };                 # Get reference to anonymous hash.
bless($obj);                # Bless hash into current package.
bless($obj, "Critter");     # Bless hash into class Critter.

--------------

package Critter;
sub spawn { bless {}; }

--------------

package Critter;
sub spawn {
    my     $self = {};       # Reference to an empty anonymous hash
    bless  $self, "Critter"; # Make that hash a Critter object
    return $self;            # Return the freshly generated Critter
}

--------------

$pet = Critter->spawn;      

#######################################################
12:Objects/Object Construction/Inheritable Constructors
#######################################################

--------------

sub spawn {
    my $class =  shift;       # Store the package name
    my $self  =  { };
    bless($self, $class);     # Bless the reference into that package
    return $self;
}

--------------

$vermin = Critter->spawn;
$shelob = Spider->spawn;

--------------

$type  = "Spider";
$shelob = $type->spawn;         # same as "Spider"->spawn

--------------

sub spawn {
    my $invocant = shift;
    my $class    = ref($invocant) || $invocant;  # Object or class name
    my $self     = { };
    bless($self, $class);
    return $self;
}

###########################################
12:Objects/Object Construction/Initializers
###########################################

$steed = Horse->new(name => "Shadowfax", color => "white");

--------------

sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self = { @_ };          # Remaining args become attributes
    bless($self, $class);       # Bestow objecthood
    return $self;
}

--------------

sub new {
    my $invocant = shift;
    my $class   = ref($invocant) || $invocant;
    my $self = {
        color  => "bay",
        legs   => 4,
        owner  => undef,
        @_,                 # Override previous attributes
    };
    return bless $self, $class;
}

$ed       = Horse->new;                    # A 4-legged bay horse
$stallion = Horse->new(color => "black");  # A 4-legged black horse

--------------

$steed  = Horse->new(color => "dun");
$foal   = $steed->clone(owner => "EquuGen Guild, Ltd.");

sub clone {
    my $model = shift;
    my $self  = $model->new(%$model, @_);
    return $self;     # Previously blessed by ->new
}

############################
12:Objects/Class Inheritance
############################

package Horse;
our @ISA = "Critter";

--------------

$steed->move(10);

########################################################
12:Objects/Class Inheritance/Inheritance through C<@ISA>
########################################################

package Mule;
our @ISA = ("Horse", "Donkey");

--------------

package Mule;
use base ("Horse", "Donkey");   # declare superclasses

--------------

package Mule;
BEGIN {
    our @ISA = ("Horse", "Donkey");
    require Horse;
    require Donkey;
}

#########################################################
12:Objects/Class Inheritance/Accessing Overridden Methods
#########################################################

$stallion = Horse->new(gender => "male");
$molly = Mule->new(gender => "female");
$colt = $molly->breed($stallion);

--------------

$colt = Horse::breed($molly, $stallion);

--------------

$colt = $molly->Horse::breed($stallion);

--------------

package Mule;
our @ISA = qw(Horse Donkey);
sub kick {
    my $self = shift;
    print "The mule kicks!\n";
    $self->SUPER::kick(@_);
}

--------------

sub speak {
    my $self = shift;
    print "The mule speaks!\n";
    $self->Donkey::speak(@_);
}

--------------

package Bird;
use Dragonfly;
sub Dragonfly::divebomb { shift->SUPER::divebomb(@_) }

--------------

package Bird;
use Dragonfly;
{
    package Dragonfly;
    sub divebomb { shift->SUPER::divebomb(@_) }
}

###################################################################
12:Objects/Class Inheritance/UNIVERSAL: The Ultimate Ancestor Class
###################################################################

use FileHandle;
if (FileHandle->isa("Exporter")) {
    print "FileHandle is an Exporter.\n";
}

$fh = FileHandle->new();
if ($fh->isa("IO::Handle")) {
    print "\$fh is some sort of IOish object.\n";
}
if ($fh->isa("GLOB")) {
    print "\$fh is really a GLOB.\n";
}

--------------

if ($invocant->can("copy")) {
    print "Our invocant can copy.\n";
}

--------------

$obj->snarl if $obj->can("snarl");

--------------

sub snarl {
    my $self = shift;
    print "Snarling: @_\n";
    my %seen;
    for my $parent (@ISA) {
        if (my $code = $parent->can("snarl")) {
            $self->$code(@_) unless $seen{$code}++;
        }
    }
}

--------------

use Thread 1.0;   # calls Thread->VERSION(1.0)
print "Running version ", Thread->VERSION, " of Thread.\n";

--------------

use Data::Dumper;   
use Carp;
sub UNIVERSAL::copy {
    my $self = shift;
    if (ref $self) {
        return eval Dumper($self);  # no CODE refs
    } else {
        confess "UNIVERSAL::copy can't copy class $self";
    }
}

###############################################
12:Objects/Class Inheritance/Method Autoloading
###############################################

sub AUTOLOAD {
    return if our $AUTOLOAD =~ /::DESTROY$/;
    ...
}

--------------

if ($obj->can("methname") || $obj->can("AUTOLOAD")) {
    $obj->methname();
}

--------------

package Goblin;
sub kick;
sub bite;
sub scratch;

--------------

package Goblin;
use subs qw(kick bite scratch);

############################################
12:Objects/Class Inheritance/Private Methods
############################################

# declare private method
my $secret_door = sub {
    my $self = shift;
    ....
};

--------------

sub knock {
    my $self = shift;
    if ($self->{knocked}++ > 5) {
        $self->$secret_door();
    }
}

###############################
12:Objects/Instance Destructors
###############################

package MailNotify;
sub DESTROY {
   my $self = shift;
   my $fh   = $self->{mailhandle};
   my $id   = $self->{name};
   print $fh "\n$id is signing off at " . localtime() . "\n";
   close $fh;  # close pipe to mailer
}

--------------

sub DESTROY {
    my $self = shift;
    # check for an overridden destructor...
    $self->SUPER::DESTROY if $self->can("SUPER::DESTROY");
    # now do your own thing before or after
}

#################################
12:Objects/Managing Instance Data
#################################

sub get_name {
    my $self = shift;
    return $self->{name};
}

sub set_name {
    my $self      = shift;
    $self->{name} = shift;
}

--------------

$him = Person->new();
$him->set_name("Frodo");
$him->set_name( ucfirst($him->get_name) );

--------------

sub name {
    my $self = shift;
    if (@_) { $self->{name} = shift }
    return $self->{name};
}

--------------

$him = Person->new();
$him->name("Frodo");
$him->name( ucfirst($him->name) );

--------------

sub name {
    my $self = shift;
    my $field = __PACKAGE__ . "::name";
    if (@_) { $self->{$field} = shift }
    return $self->{$field};
}

#######################################################################
12:Objects/Managing Instance Data/Field Declarations with C<use fields>
#######################################################################

sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    return bless [], $class;
}

--------------

sub name {
    my $self = shift;
    if (@_) { $self->[0] = shift }
    return $self->[0];
}

sub race {
    my $self = shift;
    if (@_) { $self->[1] = shift }
    return $self->[1];
}

sub aliases {
    my $self = shift;
    if (@_) { $self->[2] = shift }
    return $self->[2];
}

--------------

package Person;
use fields qw(name race aliases);

--------------

package Person;
use fields qw(name race aliases);
sub new {
    my $type = shift;
    my Person $self = fields::new(ref $type || $type);
    $self->{name} = "unnamed";
    $self->{race}  = "unknown;
    $self->{aliases} = [];
    return $self;
}
sub name {
    my Person $self = shift;
    $self->{name} = shift if @_;
    return $self->{name};
}
sub race {
    my Person $self = shift;
    $self->{race} = shift if @_;
    return $self->{race};
}
sub aliases {
    my Person $self = shift;
    $self->{aliases} = shift if @_;
    return $self->{aliases};
}
1;

--------------

package Wizard;
use base "Person";
use fields qw(staff color sphere);

--------------

my Wizard $mage = fields::new("Wizard");

--------------

$mage->name("Gandalf");
$mage->color("Grey");

--------------

$mage->{name} = "Gandalf";
$mage->{color} = "Grey";

##########################################################################
12:Objects/Managing Instance Data/Generating Classes with C<Class::Struct>
##########################################################################

package Person;
use Class::Struct;
struct Person => {    # create a definition for a "Person"
    name    => '$',   #    name field is a scalar
    race    => '$',   #    race field is also a scalar
    aliases => '@',   #    but aliases field is an array ref
};
1;

--------------

use Person;
my $mage = Person->new();
$mage->name("Gandalf");
$mage->race("Istar");
$mage->aliases( ["Mithrandir", "Olorin", "Incanus"] );

#######################################################################
12:Objects/Managing Instance Data/Generating Accessors with Autoloading
#######################################################################

use Person;
$him = Person->new;
$him->name("Aragorn");
$him->race("Man");
$him->aliases( ["Strider", "Estel", "Elessar"] );
printf "%s is of the race of %s.\n", $him->name, $him->race;
print "His aliases are: ", join(", ", @{$him->aliases}), ".\n";

--------------

package Person;
use Carp;

my %fields = (
    "Person::name"  => "unnamed",
    "Person::race"   => "unknown",
    "Person::aliases"  => [],
);

# The next declaration guarantees we get our own autoloader.
use subs qw(name race aliases);

sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self  = { %fields, @_ };    # clone like Class::Struct
    bless $self, $class;
    return $self;
}

sub AUTOLOAD {
    my $self = shift;
    # only handle instance methods, not class methods
    croak "$self not an object" unless ref($invocant);
    my $name = our $AUTOLOAD;
    return if $name =~ /::DESTROY$/;
    unless (exists $self->{$name}) {
        croak "Can't access `$name' field in $self";
    }
    if (@_) { return $self->{$name} = shift }
    else    { return $self->{$name} }
}

####################################################################
12:Objects/Managing Instance Data/Generating Accessors with Closures
####################################################################

package Person;

sub new {
    my $invocant = shift;
    my $self = bless({}, ref $invocant || $invocant);
    $self->init();
    return $self;
}

sub init {
    my $self = shift;
    $self->name("unnamed");
    $self->race("unknown");
    $self->aliases([]);
}

for my $field (qw(name race aliases)) {
    my $slot = __PACKAGE__ . "::$field";
    no strict "refs";          # So symbolic ref to typeglob works.
    *$field = sub {
        my $self = shift;
        $self->{$slot} = shift if @_;
        return $self->{$slot};
    };
}

####################################################################
12:Objects/Managing Instance Data/Using Closures for Private Objects
####################################################################

package Person;
sub new {
    my $invocant  = shift;
    my $class = ref($invocant) || $invocant;
    my $data = {
       NAME     => "unnamed",
       RACE     => "unknown",
       ALIASES  => [],
    };
    my $self = sub {
       my $field = shift;
       #############################
       ### ACCESS CHECKS GO HERE ###
       #############################
       if (@_) { $data->{$field} = shift }
       return    $data->{$field};
    };
    bless($self, $class);
    return $self;
}
# generate method names
for my $field (qw(name race aliases)) {
    no strict "refs";  # for access to the symbol table
    *$field = sub {
        my $self = shift;
        return $self->(uc $field, @_);
    };
}

--------------

use Carp;
local $Carp::CarpLevel = 1;  # Keeps croak messages short
my ($cpack, $cfile) = caller();

--------------

croak "No valid field `$field' in object"
    unless exists $data->{$field};

--------------

carp "Unmediated access denied to foreign file"
    unless $cfile eq __FILE__;

--------------

carp "Unmediated access denied to foreign package ${cpack}::"
    unless $cpack eq __PACKAGE__;

--------------

carp "Unmediated access denied to unfriendly class ${cpack}::"
    unless $cpack->isa(__PACKAGE__);

############################################
12:Objects/Managing Instance Data/New Tricks
############################################

package Critter;

sub new {
    my $class = shift;
    my $self = { pups => 0, @_ };    # Override default.
    bless $self, $class;
}

sub pups : lvalue {                  # We'll assign to pups() later.
    my $self = shift;
    $self->{pups};
}

package main;
$varmint = Critter->new(pups => 4);
$varmint->pups *= 2;                 # Assign to $varmint->pups!
$varmint->pups =~ s/(.)/$1$1/;       # Modify $varmint->pups in place!
print $varmint->pups;                # Now we have 88 pups.

--------------

sub pups : locked method {
    ...
}

##############################
12:Objects/Managing Class Data
##############################

Critter->population()       # Access via class name
$gollum->population()       # Access via instance

--------------

package Critter;
our $Population = 0;
sub population { return $Population; }
sub DESTROY { $Population-- }
sub spawn {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    $Population++;
    return bless { name => shift || "anon" }, $class;
}
sub name {
    my $self = shift;
    $self->{name} = shift if @_;
    return $self->{name};
}

--------------

our $Debugging = 0;     # class datum
sub debug {
    shift;              # intentionally ignore invocant
    $Debugging = shift if @_;
    return $Debugging;
}

--------------

{
    my $Debugging = 0;          # lexically scoped class datum
    sub debug {
        shift;       # intentionally ignore invocant
        $Debugging = shift if @_;
        return $Debugging;
    }
}

--------------

sub debug {
    my $invocant = shift;
    my $class   = ref($invocant) || $invocant;
    my $varname = $class . "::Debugging";
    no strict "refs";       # to access package data symbolically
    $$varname = shift if @_;
    return $$varname;
}

##############
13:Overloading
##############

print $object->as_string;
$new_object = $subject->add($object);

--------------

print $object;
$new_object = $subject + $object;

#####################################
13:Overloading/The C<overload> Pragma
#####################################

package MyClass;

use overload   '+' => \&myadd,            # coderef
               '<' => "less_than";        # named method
             'abs' => sub { return @_ },  # anonymous subroutine

################################
13:Overloading/Overload Handlers
################################

package ClipByte;

use overload '+' => \&clip_add,
             '-' => \&clip_sub;

sub new {
    my $class = shift;
    my $value = shift;
    return bless \$value => $class;
}

sub clip_add {
    my ($x, $y) = @_;
    my ($value) = ref($x) ? $$x : $x;
    $value     += ref($y) ? $$y : $y;
    $value = 255 if $value > 255;
    $value =   0 if $value < 0;
    return bless \$value => ref($x);
}

sub clip_sub {
    my ($x, $y, $swap) = @_;
    my ($value) = (ref $x) ? $$x : $x;
    $value     -= (ref $y) ? $$y : $y;
    if ($swap) { $value = -$value }
    $value = 255 if $value > 255;
    $value =   0 if $value < 0;
    return bless \$value => ref($x);
}

package main;

$byte1 = ClipByte->new(200);
$byte2 = ClipByte->new(100);

$byte3 = $byte1 + $byte2;    # 255
$byte4 = $byte1 - $byte2;    # 100
$byte5 = 150 - $byte2;       # 50

#####################################
13:Overloading/Overloadable Operators
#####################################

package Person;

use overload q("") => \&as_string;

sub new {
    my $class = shift;
    return bless { @_ } => $class;
}

sub as_string {
    my $self = shift;
    my ($key, $value, $result);
    while (($key, $value) = each %$self) {
        $result .= "$key => $value\n";
    }
    return $result;
}

$obj = Person->new(height => 72, weight => 165, eyes => "brown");

print $obj;

--------------

package ShiftString;

use overload
    '>>' => \&right_shift,
    '""' => sub { ${ $_[0] } };

sub new {
    my $class = shift;
    my $value = shift;
    return bless \$value => $class;
}

sub right_shift {
    my ($x, $y) = @_;
    my $value = $$x;
    substr($value, -$y) = "";
    return bless \$value => ref($x);
}

$camel = ShiftString->new("Camel");
$ram = $camel >> 2;
print $ram;            # Cam

--------------

package MagicDec;

use overload
    q(--) => \&decrement,
    q("") => sub { ${ $_[0] } };

sub new {
    my $class = shift;
    my $value = shift;
    bless \$value => $class;
}

sub decrement {
    my @string = reverse split(//, ${ $_[0] } );
    my $i;
    for ($i = 0; $i < @string; $i++ ) {
        last unless $string[$i] =~ /a/i;
        $string[$i] = chr( ord($string[$i]) + 25 );
    }
    $string[$i] = chr( ord($string[$i]) - 1 );
    my $result = join('', reverse @string);
    $_[0] = bless \$result => ref($_[0]);
}

package main;

for $normal (qw/perl NZ Pa/) {
    $magic = MagicDec->new($normal);
    $magic--;
    print "$normal goes to $magic\n";
}

--------------

package LuckyDraw;
use overload
    '<>' => sub {
                my $self = shift;
                return splice @$self, rand @$self, 1;
            };
sub new {
    my $class = shift;
    return bless [@_] => $class;
}
package main;
$lotto = new LuckyDraw 1 .. 51;
for (qw(1st 2nd 3rd 4th 5th 6th)) {
    $lucky_number = <$lotto>;
    print "The $_ lucky number is: $lucky_number.\n";
}
$lucky_number = <$lotto>;
print "\nAnd the bonus number is: $lucky_number.\n";

--------------

package PsychoHash;

use overload '%{}' => \&as_hash;

sub as_hash {
    my ($x) = shift;
    return { @$x };
}

sub new {
    my $class = shift;
    return bless [ @_ ] => $class;
}

$critter = new PsychoHash( height => 72, weight => 365, type => "camel" );

print $critter->{weight};   # prints 365

--------------

use overload '+' => sub { bless [ \$_[0], \$_[1] ] };

##########################################
13:Overloading/The Copy Constructor (C<=>)
##########################################

$copy = $scalar_ref;
++$$copy;             # changes $$scalar_ref

--------------

$copy = $scalar_ref;
$copy = $copy + 1;

--------------

$copy = $scalar_ref;
...
++$copy;

--------------

$copy = $scalar_ref;
...
$copy = $copy->clone(undef, "");
$copy->incr(undef, "");

####################################
13:Overloading/Overloading Constants
####################################

sub import { overload::constant ( integer => \&integer_handler,
                                  float   => \&float_handler,
                                  binary  => \&oct_and_hex_handler,
                                  q       => \&string_handler,
                                  qr      => \&regex_handler ) }

--------------

$year = cube(12) + 1;
$pi   = 3.14159265358979;

--------------

package DigitDoubler;    # A module to be placed in DigitDoubler.pm
use overload;

sub import { overload::constant ( integer => \&integer_handler,
                                  float   => \&float_handler ) }

sub integer_handler {
    my ($orig, $interp, $context) = @_;
    return $interp * 2;          # double all constant integers
}

1;

--------------

use DigitDoubler;

$trouble = 123;      # trouble is now 246

###################################
13:Overloading/Run-time Overloading
###################################

eval " use overload '+' => \&my_add ";

--------------

eval " no overload '+', '--', '<=' ";

###############################
14:Tied Variables/Tying Scalars
###############################

#!/usr/bin/perl
package Centsible;
sub TIESCALAR { bless \my $self, shift }
sub STORE { ${ $_[0] } = $_[1] }  # do the default thing
sub FETCH { sprintf "%.02f", ${ my $self = shift } } # round value

package main;
tie $bucks, "Centsible";
$bucks = 45.00;
$bucks *= 1.0715; # tax
$bucks *= 1.0715; # and double tax!
print "That will be $bucks, please.\n";

####################################################
14:Tied Variables/Tying Scalars/Scalar Tying Methods
####################################################

use ScalarFile;       # load ScalarFile.pm
tie $camel, "ScalarFile", "/tmp/camel.lot";

--------------

$dromedary = $camel;

--------------

$dromedary = (tied $camel)->FETCH():

--------------

$clot = tie $camel, "ScalarFile", "/tmp/camel.lot";
$dromedary = $camel;          # through the implicit interface
$dromedary = $clot->FETCH();  # same thing, but explicitly

--------------

package ScalarFile;
use Carp;                # Propagates error messages nicely.
use strict;              # Enforce some discipline on ourselves.
use warnings;            # Turn on lexically scoped warnings.
use warnings::register;  # Allow user to say "use warnings 'ScalarFile'".
my $count = 0;           # Internal count of tied ScalarFiles.

--------------

sub TIESCALAR {           # in ScalarFile.pm
    my $class    = shift;
    my $filename = shift;
    $count++;             # A file-scoped lexical, private to class.
    return bless \$filename, $class;
}

--------------

sub TIESCALAR { bless \$_[1], $_[0] }    # WRONG, could refer to global.

--------------

sub TIESCALAR {           # in ScalarFile.pm
    my $class    = shift;
    my $filename = shift;
    my $fh;
    if (open $fh, "<", $filename or
        open $fh, ">", $filename)
    {
        close $fh;
        $count++;
        return bless \$filename, $class;
    }
    carp "Can't tie $filename: $!" if warnings::enabled();
    return;
}

--------------

tie ($string, "ScalarFile", "camel.lot") or die;

--------------

sub FETCH {
    my $self  = shift;
    confess "I am not a class method" unless ref $self;
    return unless open my $fh, $$self;
    read($fh, my $value, -s $fh);  # NB: don't use -s on pipes!
    return $value;
}

--------------

tie($string, "ScalarFile", "camel.lot");
print $string;

--------------

sub STORE {
    my($self,$value) = @_;
    ref $self                   or confess "not a class method";
    open my $fh, ">", $$self    or croak "can't clobber $$self: $!";
    syswrite($fh, $value) == length $value
                                or croak "can't write to $$self: $!";
    close $fh                   or croak "can't close $$self: $!";
    return $value;
}

--------------

tie($string, "ScalarFile", "camel.lot");
$string  = "Here is the first line of camel.lot\n";
$string .= "And here is another line, automatically appended.\n";

--------------

sub DESTROY {
    my $self = shift;
    confess "wrong type" unless ref $self;
    $count--;
}

--------------

sub count {
    # my $invocant = shift;
    $count;
}

--------------

if (ScalarFile->count) {
    warn "Still some tied ScalarFiles sitting around somewhere...\n";
}


1 comment: