1 of 57

DC-Baltimore Perl Workshop 2013

Structures, Context, Functions and Flow

Hello, Perl

~ Joshua Eric Turcotte

please don't spook the speaker!

(...he's never done this before...)

2 of 57

For seriously, though...

  • This is my first talk ~ever~�
  • Thrown together rather quickly upon request;�
  • Borrowing from YAPC'11 Data Structures talk by John SJ Anderson talk and 'Modern Perl' by chromatic
  • I have no idea how long this'll last, so don't be afraid to ask questions or correct me.

3 of 57

There may be a few topics:

  • Some good practices
  • Sigils and Variable Types
  • Nested Data Types
  • Operators and Flow Control
  • Scope and References
  • Functions
  • Context
  • and any questions !

4 of 57

Before we start...

5 of 57

Deimos,

because I forgot how to funny

6 of 57

A show of hands!

How many here have some working knowledge of perl?

7 of 57

Well, crap...

=head2

dangit!

=cut��sub where_to {

my $slides = shift;

my %crowd = @_;

return knowledge(\%crowd) <= $me->{$comfort_level} ?

shift @$slides : skip_a_bunch_of(@$slides);

}

8 of 57

too soon?

I spent way too much time on that slide...

... and Deimos was harassing me�the entire time. (go chase a rubber band!)

NO DON'T!

9 of 57

Save yourself a little pain...

  • use 5.010;
  • say "SO glad I don't have to type in my own $explicative newlines";
  • use strict;
  • use warnings;
  • use Data::Dumper;
  • use $Data::Dumper::Useqq = 1;
  • use $Data::Dumper::Sortkeys = 1;
  • $| = 1;

10 of 57

These things will help you learn

... or, possibly, never learn how to debug!

  • Anything before perl 5.10 shouldn't be considered 'contemporary' ... hell, go for 5.14
  • Strict and Warnings will slap your knuckles with a ruler and lecture you mercilessly.�
  • Data::Dumper will show your the unfurled complexity of your data structures.

11 of 57

Are we there yet?

12 of 57

$gotta->start(%somewhere);

First up; Sigils...

13 of 57

$%@&!

No, really... $%@&�

  • $I am a scalar;
  • There's a whole hash of %others here;
  • We'gonna join the @conga_line array;
  • And do a little &dance() routine;

  • $dance != %dance != @dance != &dance;
  • $badger[$badger{&badger($badger)}}];

14 of 57

say "$me, $myself, and $I";

  • The $ sigil denotes a scalar...� ...a singular jack of all trades;�
    • my $pi = 3.14159; # a number
    • my $pi = "ooh, $food π"; # a string of characters
    • my $pi = \&tau_divided_by_two;# even a reference to some other�# variable or even a subroutine�
  • Scalars are near-inevitably the most used variable types in any piece of code.

15 of 57

%They are a @bunch of $people;

  • Plural data structures such as %hashes, @arrays, (lists) are made up of $scalars;�
    • my %family = { � dad => $father,� mum => $mother,� cat => $demonspawn�};�
    • my @pecking_order = ($mother, $father);

16 of 57

Among these %people, who am $I;

  • the $ sigil is also used to isolate, in scalar CONTEXT, one member of the group.�
    • my $deimos = $family{cat};
    • my $first = $pecking_order[0];
    • my $last = $pecking_order[-1];
  • singular %hash elements are called by their key while ...
  • singular @array elements are found via their index (starts at 0, negatives from the end)

17 of 57

why can't I find my $line[$place];

  • $ scalars are frequently used to vary the key or index being sought:�
    • my $good_friend = $acquaintances[$index_next_to_mine];# this presumes an integer!
    • my $dinner = $food{$strongest_desire};# and this presumes a pre-existing key string!
  • Since a $ could be just about anything, even a reference to the location of a subroutine or plurality, take care.

18 of 57

meet the %band;

  • Hashes are structures of other variables... even other NESTED structures;�
    • my %coworkers = {� myself => $the_man,� tech => %tech_team,� media => {� manager => $some_dude,� writer => $some_lady,� },� chain_of_command => @managers�};
    • $coworkers{tech}{new_guy} = $drupal_dev; # :(

19 of 57

Calling attendance!

  • The % hash is really quite wonderful for testing or aggregating against existing membership;�
    • if defined($shelf{car_keys}){ ...; } else { ...; }�# decide course of action on existence of 'car_keys'�# in the $shelf hash collection
    • $quantity{$this_box} += 1;�# initialize and/or increment the numerical value�# stored in whichever $this_box is.

20 of 57

Is it a %Bag or a %Book?

  • How the elements of a hash are treated can lend to a couple of definitions;�
    • say $bag{marbles}; # 12;�# called a container
    • say $car{make}; # Chevrolet�# called a record because the key is strict
    • say $family{$pet}; # Thor�# called a dictionary because the key itself�# varies while the hash
    • say $family{$car}{newest}{model}; # Volt�# and now you've got a complex document

21 of 57

Am I at the back of the @line?

  • @arrays are linear populations which, again, can consist of scalars or other populations (both arrays and hashes.)�
  • @arrays are indexed by integer in a predictable way�
    • my @array = (3, 2, 1);
    • say $array[1]; #2
    • my $i = -1;
    • say $array[$i]; #1

22 of 57

In and Out of the @line;

  • @array elements can be added a few ways;�
    • $array[1] = $something; # in 2nd slot
    • push @array, $something; # at the end
    • unshift @array, $something; # at the beginning
  • ... or removed in other ways;�
    • my $thing = pop @array; # from the end
    • my $thing = shift @array; # from the beginning
    • @array = (); # hell, just empty the whole thing!

23 of 57

First in or Last in?

  • How you choose to push and pop, shift and unshift defines whether you're using a QUEUE or a STACK
  • A STACK would be LIFO (Last in, First out) where something put on the top must then be taken off the top again.�
  • A QUEUE, however, is FIFO (First in, First out), and would be akin to the DMV.

24 of 57

Take a $number and &wait(@line);

25 of 57

You can even do a @grid!

26 of 57

*In exchange for your very soul...

  • It has 8 rows of 16 columns, or 128 scalars of identity to store... or you could try...�
    • $this_map{rocks} = (� [3,3], [4,3], [5,3], [6,3], [3,5], [4,5], ...�);
    • say scalar(@{$this_map{rocks}}); # 18# more on CONTEXT and REFERENCES soon*
  • If your overall background already existed, storing the rocks this way takes just 36 scalars across 18 anonymous arrays.

27 of 57

As stolen from the YAPC'11 talk...

  • Don't see a need for complex structures?
  • A hospital
    • has multiple numbered floors
      • with multiple wards/units per floor
        • with multiple nurses per ward
          • with a list of patients, in visitation order�
  • say $hospital->[3]{'MedSurg'}{'Sarah'}[4]; # DEIMOS!�# I guess it was a veterinary hospital...

# that ->, btw, is reference operator (later...)

28 of 57

Okay, but what do we do with this?

  • Variables are acted upon by operators, and most of them you can probably predict;�
    • $z = $x + $y; $a = ($b - $c) * $e; # think algebra�
  • While others may need some 'splainin';�
    • say !$spider ? 'yep' : 'nope' x 3; # ehhh?
    • if ($awake && scalar(@puppies)) { ...; } # what?
    • $grocery_list .= &think_about(%food); # okay
    • push @playlist, @{music->{$recent_faves}};

29 of 57

Characteristics of Operators

  • Keep in mind precedence;
    • 8 + 8 * 10; # 88
    • (8 + 8) * 10; # 160
  • And associativity;
    • 2 + 3 + 4; # (left) 2 + 3, then 5 + 5, vs.
    • 2 ** 3 ** 4; # (right) 3 to the power of 4, and 2 to the power of 81
  • And arity;
    • (nullary) operates on zero items
    • (unary) operates on one
    • (binary) on two, (trinary) on three
    • (listary) on a whole list of items

30 of 57

And then there's Fixity...

  • Perl has several places in which operators reside;�
    • my $area = $length * $depth; # infix operator
    • say 'whew' if !$spider; # prefix operator (not, here)
    • $nopes++ if $spider; # postfix operator (increment)
    • { nopeworthy => %spiders }; # circumfix
    • say $pecking_order[0]; # Deimos! postcircumfix

31 of 57

Numerical Operators

  • Perl has +, +=, -, -=, *, *=, /, /=, **, **=, %, %=, as well as postfix ++ and -- numerical operators.�
    • 1 + 2; # 3
    • 2 += 2; # 4, could be read as incrementing by 2
    • 2++; # 3, incrementation�
  • It should be noticed that ++ and -- have interest effects on string scalars as well; a -> b, z -> aa, a9 -> b0, etc.

32 of 57

Numerical Comparation

  • When used, these COERCE the interpretation of a scalar as a number;�
    • $a == $b; # are they the same?
    • $b != $c; # are they not the same?
    • $c < $d; $d <= $e; # less than? less than or equal?
    • $e > $f; $f >= $g; # greater than (or equal to?)?
  • There is also the numeric <=> sort operator;�
    • sort {$a <=> $b} (3, 2, 8); #2, 3, 8
    • @eldest = sort {$age{$b} <=> $age{$a}} keys %age;

33 of 57

String Operators

  • While strings can be affected by ++ and --, their typical operator is concatenation;�
    • say 'OMG ' . $family{cat} . '!, don't eat that!';
    • while (shift @paragraphs){ $document .= "$_\n\n"; }# more on $_ and @_ eventually...�
  • Strings can be compared, as well, through eq (like ==), ne (!=), gt (>) and ge (>=), lt (<) and le (<=), or sorted with cmp;
    • @class = sort($kid{$a} cmp $kid{$b}) keys %name;

34 of 57

Logical Operators

  • To be able to write a responsible program, you're going to have to engage in some logic;�
    • || and or check that any one conditions is met.
    • && or and check that each condition is met
    • // tests whether a thing is defined (even if 0 or '')
    • ? and : together are TERNARY operators where:� say $is_it_true ? 'yes' : 'no';�depending on the truth of the test (before the ?), the middle or last clause are returned

35 of 57

Need LESS input!

  • When checking multiple conditions in�a logic test, put the most likely to�prove true at the BEGINNING...
    • &do_it if (&probable() || &unlikely());
  • This is called SHORT CIRCUITING as perl will stop running any further checks after the first truth is found that satisfies the clause.
  • If both had say/print statements within, you'd only have seen the one from the &probable() routine.

*yes, johnny 5 runs on perl 5.10+

*

36 of 57

You want me to do what, now?

  • The ternary operator is a bit like a quick and dirty IF (){ } ELSE {} statement... but there's more than this;�
    • if ($deimos){ &run(); } # highest priority!�elsif ($pizza){ &eat($pizza); } # good alternative�else { &play($minecraft); } # if all else fails
    • unless ($pizza){ &make(pizza); } # opposite of if�else { &eat(pizza); } # can be confusing at times

37 of 57

All the while, until otherwise...

  • There is also the while(){} statement which operates for so long as its conditions are true;
    • while ($where{$deimos} ne 'here'){ &relax(); }
    • while (my $coin = pop @coins){ $total += $coin{value}; }�
  • Then again, there's until(){} which loops UNTIL a condition is met;
    • until ($where{$deimos} eq 'here'){ &relax(); }

38 of 57

Do this at least once...

  • A do {} until () block operates very much like an until() {} clause, but ensures that even if our condition starts as false, the block executes at least one time.�
    • do { &relax() } until ($loc{$deimos}} eq 'here');�# in other words, my cat here has a chance to�# surprise me while relaxing...

39 of 57

For Tom, for Dick, and for Harry;

  • If you intend to iterate over a sequence that should not be changed in the process, try foreach(){};�
    • foreach my $dancer (@conga_line){ &dance($dancer); }
    • foreach (keys %dancer) { say $dancer{$_}{name}; }
    • foreach my $i (0..99) { say "$i monkeys!"; }
    • for (my $i = 0; $i <= 99; $++) { say "$i cats!"; }# technically, this c-style method is similar to�# the one before it...

40 of 57

Not while you live under my roof!

  • Keep track of the SCOPE of your variables!��my @outside = ('deimos', 'thor');�while (!$inside){� my @inside = ('josh', 'kath');� say "$inside[1] watches $outside[0]";�}�say $inside[0]; # this will not work�
  • those @outside the { enclosure } remain in the wider scope, while those @inside are not defined once we leave that scope.

41 of 57

Let's count�some rocks!

my $rocks = 0;

foreach my $x(0..$maxx){

foreach my $y(0..$maxy){

$rocks++ if $grid[$x][$y] eq 'rock';

}

}

say "$rocks rocks found!";

  • You can have loops within conditions within enclosures within loops if you like, but the clock cycles may add up quickly.

42 of 57

So, about that whole reference thing

  • It turns out, you can't actually have @arrays of %hashes of @arrays of %hashes of $scalars... hashes and arrays only contain $scalars.�
  • But $scalars can be a singular REFERENCE; �they can refer to anything... an array, a hash, a subroutine, another scalar, etc.�

%hash = { array => (1, 2, 3) };# the inner scalar 'array' contains a reference to # anonymous array in memory�# => allows unquoted key names, btw

43 of 57

This is why we like Data::Dumper;

my $hashref = { # note the $� name => 'josh',� age => $obfuscated_age,� pet_plants => [� { name => 'president', type => 'sago' },� { name => 'thing', type => 'cactus' }

]

};��say $hashref; # HASH(0x30ec2c0)

  • It can only report a reference; DD would have printed the whole thing for us.

44 of 57

To whom are you referring?

  • References to existing variables can be made deliberately using \;�
    • my $arrayref = \@array;
    • my $hashref = \%hash;
    • my $scalarref = \$scalar;
  • ... or instant assignment to a $scalar;�
    • my $arrayref = [ 1, "cat", undef, $hashref ];
    • my $hashref2 = { APR => 12, AUG => 19 };

45 of 57

Are you referring to $$me?

  • But do you need to collect the value(s) of a reference?�
    • say $_ for @{$arrayref};
    • say "$_: $hashref->{$_}" for keys %{$hashref};
  • the {} can be considered optional except when referencing a complex structure;�
    • say $_ for @$arrayref; # no danger of confusion ...
    • say $_ for @{$hospital->[$floor]{$ward}{$nurse}};

46 of 57

By why even bother with refs?

my $pets = \%hash_of_cat_and_plants;�my $condo_pets = { josh => $pets, kath => $kathpets };�my $vetqueue = [ $kathpets, $pets, $billpets ];

  • Right... so?�
    • $pets->{deimos}->{age} += 1;
    • $pets->{sagopalm}->{watered} = 'Apr 19';
  • All of a sudden, the details within $pets is updated for ALL references everywhere

47 of 57

Let's do a little routine;

  • Now, there's another good reason for references... but first, the SUBROUTINE!�sub obfuscate_age {� my ($me, $age) = @_; # array of ALL incoming� $me->{age} = (int(rand(5)) - 2) + $age;�}��say $josh->{age}; # 35;�obfuscate_age($josh, 35);�sat $josh->{age}; # 34; (or 33, 35, 36, or 37)

48 of 57

Self documenting routines...

  • Alternatively, you might consider...

�sub obfuscate_age {� my $me = shift; # @_ can be implied silently� my $age = shift || 35; # you can 'or' a default

$me->{age} = (int(rand(5)) - 2) + $age;

}

  • By yanking args individually from the implied @_, you can make it easier for others to read what arguments this routine expects or requires

49 of 57

Wait a minute... what's going on?

  • What if your sub returns separate arrays?

sub twoarrays {

my @a1 = (1, 2, 3);� my @a2 = (4, 5, 6);� return (@a1, @a2);

}

my (@one, @two) = twoarrays();�say join(", ",@one); # 1, 2, 3, 4, 5, 6

say join(", ",@two); # ... huh ...

  • Perl can't do it! They all end up in the first array.

50 of 57

Oh wait... REFERENCES!

  • Try this instead;�sub twoarrays {� my $a1 = [1, 2, 3]; # instant array ref!� my @a2 = (4, 5, 6]); # alternatively� return ($a1, \@a2);�}�my ($one, $two) = twoarray();�say join(", ", @$one); # 1, 2, 3�say join(", ", @$two); # 4, 5, 6
  • We passed a list of references back, and dereferenced each in time for pretty concatenations... wooo, join()!

51 of 57

For a little bit of context;

  • Along with variables, subroutines also respond to a thing called CONTEXT;�
    • my @all_changed = a_to_e(\%people) # list
    • my $num_changed = a_to_e(\%people); # scalar
    • a_to_e(\%people) # void
  • the subroutine is being told by the assignment's context if it wants many, one, or no results.
  • Whether the routine obeys is up to you.

52 of 57

Umm... if you really want me to;

sub a_to_e {

$people = shift;

my @changed;

foreach my $p (keys %$people){

if ($p =~ /a/){

$people->{$p} =~ s/a/e/g;

push @changed, $people->{$p};

}

}

...

53 of 57

... just a little bit more;

if (wantarray){ # list context!

return @changed; # give us all changed!

}

elsif (defined wantarray){ # scalar context!

return scalar @changed; # return a count

} # ^ that's also a forced context!

else { # void context#

... # we're already done!

}

}

54 of 57

Forcing contexts...

  • Essentially every operator imposes a context on it's parts, and not always the one you want.�
    • my ($alter_ego) = a_to_e(%people);
    • return scalar @changed; # compresses to a count
  • In the above case, I wanted to get just the FIRST result from among the people processed... not a count of those altered.
  • () imposed a list context on this scenario.

55 of 57

Finally, Coercion; (get off the stage!)

  • As there are also VALUE contexts (string and number), operators will give perl hints as to what you want done with a scalar.�
    • 124 eq 124; # coerces both into strings
    • say split(//, 123456); # '123456', string again
    • say int('5 pigmy marmosets') # 5, numerical
    • my %users;�$users{Kath}{hair} = 'red'; # reference coercion
  • That last is called autovivification, having automatically made the 'Kath' reference.

56 of 57

References... no, no... SOURCES

  • http://modernperlbooks.com/books/modern_perl/� 'Modern Perl' by chromatic
  • http://perldoc.perl.org/perlreftut.html� References Tutorial
  • https://github.com/genehack/perl-beginner-talks/tree/master/intro-to-perl-data-structures� 'Intro to Perl: Data Structures' by John SJ Anderson

joshua.eric.turcotte@gmail.com

@je_turcotte

How'd I do?

57 of 57

I'm sorry... you were talking

about something?