You are on page 1of 29

A Toronto Perl Mongers Presentation

Tom Legrady
24 October, 2013

When Is it Useful?

For a big project, youre using Moose, et. al. For a quick little script, probably just plain-old-Perl. perl5i turns Perl into a modern language, with modern conveniences. Lots of modules required, but you have them already. When people go looking for examples of Perl code online, they nd short snippets without documentation, poor style. I decided to write samples code using perl5i, showing good style.

Compatability; Upgrade Paths


If Perl5i changes in the future, but adding or dropping modules, it will NEVER affect your existing scripts! You dont actually use Perl5i .... What you use is Perl5i:: $version use perl5i::2;

Whatdja Get?

Which would you rather type? use perl5i::2; or use warnings; use strict; use feature say state switch; use English; use autodie; use Try::Tiny;

But Wait! Theres More!


The Meta Object Subroutine and Method Signature Introspection Autoboxing alias Scalar Autoboxing center round, round_up, round_down ceil, oor is_number is_positive, is_negative is_even, is_odd is_integer, is_int, is_decimal require wrap ltrim, rtrim, trim, title_case path2module, module2path is_module_name group_digits, commify, reverse Array Autoboxing foreach as_hash pick, pick_one diff popn, shiftn intersect ltrim, rtrim, trim Hash Autoboxing each, ip merge, diff, intersect Code autoboxing signature caller, die, list utf8::all capture Carp Child English Modern::Perl CLASS File::chdir File::stat DateTime Time::y2038 IO::Handle autodie autovivication No indirect object syntax want Try::Tiny true Better load errors

A Slight Change in Plans

100 Doors

use perl5i::2; package doors { ... } # -------------------------------------------# Main Thread # my $doors = doors->new(100); $doors->toggle_all(); $doors->print_open();

Package doors:

package doors { use perl5i::2; use Const::Fast; const my $OPEN => 1; const my $CLOSED => 0; # -----------------------# Constructor: door->new(@args); # input: N - how many doors? # returns: door object # method new($class: @args ) { my $self = bless {}, $class; $self->_init( @args ); return $self; }

Package doors::_init()
# ----------------------# class initializer. # input: how many doors? # set N, create N+1 doors # ( door zero is unused ). # method _init( $N ) { $self->{N} = $N; $self->{doors} = [($CLOSED) x ($N+1)]; }

Package doors::toggle()

# ---------------------------------------# $self->toggle_all(); # Toggle every door, then every other door, # every third door, ... # method toggle_all() { $self->toggle_n( $_ ) for ( 1 .. $self->{N} ); }

Package doors::toggle()

# ---------------------------------------# $self->toggle_n( $cycle ); # input: number. # Toggle doors 0, $cycle, 2*$cycle, 3*$cycle,..$self->{N} # method toggle_n( $n ) { $self->toggle($_) for map { $n * $_ } ( 1 .. int( $self->{N} / $n) ); }

Package doors::toggle()

# # # # #

---------------------------------------$self->toggle( $door_number ); input: number of door to toggle. OPEN a CLOSED door; CLOSE an OPEN door.

method toggle( $which ) { $self->{doors}[$which] = ( $self->{doors}[$which] == $OPEN ? $CLOSED : $OPEN ); }

Package doors::print_open()
# ---------------------------------------# $self->print_open(); # Print list of which doors are open. # method print_open() { say join ', ', grep { $self->{doors}[$_] == $OPEN } ( 1 ... $self->{N} ); }

The Less Verbose Way

my @doors; for my $pass (1 .. 100) { for (1 .. 100) { if (0 == $_ % $pass) { $doors[$_] = not $doors[$_]; }; }; };

print "Door $_ is ", $doors[$_] ? "open" : "closed", "\n" for 1 .. 100;

I got distracted by the Software Engineering


use perl5i::2; use Getopt::Long; use Pod::Usage; # # # # # # # # # # # # # # # # ---------------------------------------Generate combinations of length $len consisting of characters from the sorted set @chars, using each character once in a combination,with sorted strings in sorted order. We will use a recursive solution: * If we run out of eligable characters, we've gone too far, and won't find a solution along this path. * If we are looking for a single character, each character in @set is eligible, so return each as the single element of an array. * We have not yet reached the last character, so two possibilities: 1) push the first element onto the front of an N-1 length combination from the remainder of the set. 2) skip the current element, and generate an N-length combination from the remainder

Combinations
func combine($len, @chars) { return unless @chars; return map { [ $_ ] } @chars if $len == 1; my ($head) = shift @chars; my @result = combine( $len-1, @chars ); for my $subarray ( @result ) { $subarray->unshift( $head ); } return ( @result, combine( $len, @chars ) );

Combinations - the UI
func parse_options() { my %options = ( length => 3, set => 5, ); GetOptions( \%options, 'length=s', 'set=s', 'man', 'help' ) or pod2usage(2); pod2usage(1) if $options{help}; pod2usage(-verbose => 2) if $options{man}; $options{alphabet} = [ ('a'..'z')[0..$options{set}-1] ]; return \%options;

The Mainline

my $options = parse_options(); say @$_ for combine( $options->{length}, @{$options->{alphabet}} );

POD
__END__ =pod =head1 NAME combinations.pli - determine possible combinations of M elements from a set of N =head1 VERSION This documentation applies to combinations.pli version 0.0.1

PODer
=head1 SYNOPSYS combinations.pli [--length M] [--set N] \ [-man] [-help] =head1 OPTIONS =over 4 =item length M Create strings of this length =item set N Use an alphabet of this many characters =item help Display a brief summary of information about the program. =item man Display the full documentation. =back

PODest

=head1 DESCRIPTION Generate combinations of length C<length> consisting of characters from the sorted set C<alphabet>, using each character once in a combination, with sorted strings in sorted order. =cut

Is That the Best Direction?


I had my doubts about whether that was a good example. POD is verbose, detached from code, liable to be out of sync. Subroutine description comments are repetitious boilerplate which may not discuss most important situations / exceptions. Testing gives examples of use, covers edge cases as well as expected behaviour, far more likely to match the code.

Take two ... core


use perl5i::2; package Combine; # ---------------------------------------# generate combinations of length $n consisting of # characters from the sorted set @set, using each # character once in a combination, with sorted strings # in sorted order. # # Returns a list of array references, each containing one # combination. # func combine($n, @set) { return unless @set; return map { [ $_ ] } @set if $n == 1; my ($head) = shift @set; my @result = combine( $n-1, @set ); for my $subarray ( @result ) { $subarray->unshift( $head ); } return ( @result, combine( $n, @set ) );

Test Wrapper
# ------------------------------------# Tests # func runtests() { eval { use Test::More }; # only include if testing test_no_set(); test_length_zero(); test_length_one(); test_longer(); } done_testing();

Test the Limiting Cases


func test_no_set() { is( combine( 1, () ), undef, 'set == empty list returns empty array' ); is( combine( 1 ), undef, 'set not provided returns empty array' );

func test_length_zero() { is( combine( 0, 'a' ), undef, 'zero length returns empty array' ); }

func test_length_one() { my ( @results ) = combine( 1, 'a' ); is_deeply( \@results, [['a']], 'length 1 returns unary set as array element'); @results = combine( 1, 'a', 'b', 'c' ); is_deeply( \@results, [['a'], ['b'], ['c']], 'length 1 returns larger set as array elements');

The Inductive Test


func test_longer() { my ( @results ) = combine( 2, 'a', 'b', 'c' ); is_deeply( \@results, [['a','b'],['a','c'],['b','c']], 'longer length generates list of combinations');

} # ----------------------------------------------# Run tests if invoked as a program, rather than # included as a module. # runtests() unless caller(); 1; # Package return

Resources
http://which-dwarf-are-you.blogspot.ca/ 2013_03_01_archive.html http://rosettacode.org/wiki/Category:Perl5i https://metacpan.org/module/perl5i Thanks to Ingy for a shout-out to the Rosetta Code perl5i examples, at YAPC::2013

Next Item on the Agenda?

You might also like