Siaris
Simple Things
Syndicate: full/short
Siaris
Categories
General0
News2
Programming2
LanguageBits0
Perl50
Ruby10
VersionControl1
Misc0
Article Calendar
<= July, 2014
S M T W T F S
12345
6789101112
13141516171819
20212223242526
2728293031
Search this blog

Key links
External Blogs
Brought to you by ...
Ruby
1and1.com

Named Parameters for Subroutine Calls

Andrew L. Johnson (First published by ItWorld.com 2001-07-26)

Perl doesn’t have typed or named parameters for subroutines, all parameters are passed in the @_ array as a flat list. However, since a list can also be a hash, we can fake it and create subroutines that can be called like:

    my $status = login( -username => $user,
                        -password => $pass,
                        -host     => $host,
                       );

This assumes we have already obtained values for $user and $pass from the user input or the command line (or wherever) and we have a function named login() that is apparently going to log us in to whatever $host is. Without worrying about the specifics of the function itself, let’s look at how it can process the arguments:

    sub login {
        my %args = @_;
        # do stuff
    }

Easy right, so what benefit do we get? Well, we get two benefits, the first of which is that our calling code is rather self-documenting (even if we chose horrible names for the variables themselves, the hash keys give it all away). Another important benefit is that we no longer have to worry about which order we give the arguments, and neither does our subroutine! We can call it like above, or like either of these:

    my $status = login( -password => $pass,
                        -host     => $host,
                        -username => $user,
                       );

    my $status = login( -password => $pass,
                        -username => $user,
                        -host     => $host,
                       );

And, as a bonus, if want a subroutine where some or all of the arguments are optional (in any order), we can do that too:

    sub draw_rect3D {
        my %args = shift;
        $args{-length} ||= 1;
        $args{-width}  ||= 1;
        $args{-height} ||= 1;
        $args{-units} = $args{-units} eq 'in'? 'in' : 'cm';
        # get to work
    }

Now we can call this and supply any of the parameters, all of the parameters, or none of the parameters (the default will be a 1 centimeter cube):

    draw_rect3D(-length => 2, -units => 'in'); # 2 x 1 x 1 inches
    draw_rect3D(-length => 5, -wdith => 2);    # 5 x 2 x 1 centimeters
    draw_rect3D(-length => 5,
                -height => 10,
                -units  => 'in',
                -width  => 3,
               );                  # 5 x 3 x 10 inches

This is quite unlike our regular way of just using the argument array as an array — in that case the order you use must always be the same, and optional arguments must be at the end.

You will see this style of function used often for Object constructor methods (and other OO methods) and it is widely used in the various Tk routines and methods (for GUI programming). But you can use this technique with any regular subroutine — all we are doing is using the argument list as a hash instead of a list or array.

*****

What is Perl-6?

Andrew L. Johnson (First published by ItWorld.com 2001-07-19)

If you interact with the Perl community (via usenet or various web-sites) you’ve probably heard about Perl-6 by now. But what exactly is it? Just another step in the versioning scheme or something more? I can’t tell you exactly what Perl-6 will eventually look like, but I can tell you what and why it is.

The change from Perl4 to Perl5 was essentially Larry Wall’s rewrite of Perl, and quite a bit was added to the Perl language at the time (but not much of the Perl4 language was changed). Perl-6 is to be, in Larry’s words: "the community’s rewrite of Perl".

Why rewrite Perl? A primary reason is to rewrite the core of perl (that’s the perl source code, not the Perl language) to make it cleaner, faster, more extensible, and easier for people to understand so that more people can participate in the maintainance and development of perl itself.

Larry also decided that there is no time like the present to investigate changes to the language itself: what sorts of things aren’t working out as well as we hoped, and what sorts of additions would be useful. Thus, the Perl6 RFC process began, and some 361 RFC’s for language changes and/or additions were submitted by the greater Perl community.

Not all of the requests will be integrated into Perl-6 of course, they are there for Larry and the team of Perl-6 developers to sift through and use in the new design. The process will take some time and you shouldn’t expect to see a usable Perl-6 for perhaps another year. However, there are a few changes that are apparent already:

  • Changing the usage of type symbols for variables:

Currently, as I’m sure you well know by now, we have three type symbols for variables: $ for scalars, @ for arrays, and % for hashes. However, you also know that these are symbols not strictly applied to variables, but to the type of thing you want to access. For example, to obtain the scalar value at index 3 of an array you use the $ symbol:

    print $array[3];

In Perl-6 these type symbols will be exclusively used with their variable types, not the data type being accessed — thus, the following will be the correct syntax:

    $scalar = 12;
    @array  = (1,2,3);
    %hash   = (name => 'andrew', beer => 'dark ale');

    @slice  = @array[1,2];
    $scalar = @array[0];

    %hash{'age','children'} = (37, 2);
    print %hash{name};

As you can see, no matter what type of value you access you will use the variable type symbol (no more using @ for slices of hashes, and $ for scalar access of single array or hash elements).

  • A new dereference operator (and concatenation operator):

The -> dereference arrow will be replaced with a dot (.). This makes Perl more similar to other OO languages with respect to calling object methods:

    $foo = SomeObject->new();
    $foo->some_method();

    # becomes:

    $foo = SomeObject.new();
    $foo.some_method();

This means that the dot will no longer be used as the concatenation operator — the new concat op will likely be the ~ character.

  • Typed variables?:

Yes, you will have the ability to declare variables as certain types (such as ‘int’ or ‘constant’), or dimensioning arrays to a certain size. You won’t be required to do this sort of thing, but it allows perl to optimize the code if you do.

  • Arrays and Hashes in scalar context:

When used in a scalar context, hashes and arrays will return a reference to themselves. This means, to assign a reference to a hash or array we can do the following:

    $a_ref = @array;
    $h_ref = %hash;

But, don’t worry about your habit of getting the length (number of elements) of an array by using it in scalar context, because now, an array reference in numerical context will return its length, and will return true if it holds any elements in a boolean context, so we can still perform the same sorts of array testing as we do now.

As you can see even from this short foray, Perl-6 won’t simply be adding new stuff for us to use, but will be changing some of our currently familiar syntax. This is not a bad thing. The changes proposed so far make a good deal of sense and we should not fear them merely because we will have to make some adjustments to they way we currently do things. Also be aware that there will be some sort of compatibility mode so that Perl-5 programs can still be run (not to mention that versions of Perl-5 will not be disappearing anytime soon either).

There is a good more information about Perl-6, and you can stay abreast of its progress by checking out the following two sites:

    http://www.perl.org/perl6/
    http://dev.perl.org/perl6/

*****

Additional OO Concepts and Further Reading

Andrew L. Johnson (First published by ItWorld.com 2001-07-12)

In the past few articles we have only touched the surface of OO programming in Perl by building a relatively simple class. This series has not been intended to teach OO programming per se, but rather to introduce the topic with a working example and provide a starting point for those wishing to delve further.

In particular, out of the following general OO concepts: Abstraction, Encapsulation, Inheritance, and Polymorphism, we have only really addressed encapsulation — hiding the internal workings and data inside the object and providing an external interface to the programmer. Abstraction is just generalization. Our slot class was not very abstract because we hard coded in the wheels and payoff table — a more abstract slot machine class might be defined so that a variety of slot machines and payoff tables could be used.

Inheritance and Polymorphism define relationships among classes — one class might be very general and not really intended to be used directly. Other classes can then be defined as derived or child classes of the this class and they can inherit all of the properties and methods of their parent class. Often when inheriting from a parent class, the new class needs to change or override one or more methods — this is referred to as polymorphism (child classes need not be identical to their parents, nor to their siblings).

There is a great deal of further information that can be found in the standard Perl documentation:

    perldoc perltoot     # an OO primer
    perldoc perlboot     # another OO primer
    perldoc perltootc    # a tutorial on class data
    perldoc perlobj      # the manpage for Perl objects
    perldoc perlbot      # OO tips and tricks

As well, Damian Conway has written and excellent book on the subject of OO programming with Perl:

    Object Oriented Perl. By Damian Conway (Manning Publications)

This book is widely recognized by many in the Perl community to be the bible of OO programming in Perl. It is very readable, highly informative, and even funny. I definitely recommend it for anyone who wants to learn how to do OO programming in Perl.

*****

The Finished Object (soot part VI)

Andrew L. Johnson (First published by ItWorld.com 2001-07-05)

The spin() method is the most complex as it does all the real work of simulating the slot machine (the object itself just stores the current state of the machine and ancillary data). The first thing we will need to do is figure out how much is being bet on this spin:

    sub spin {
        my $self = shift;
        my $bet  = $self->bet(shift);
        $bet = < $bet < 3? $bet : 3;
        $bet = $bet < $self->credits()? $bet : $self->credits();

We obtain the $bet value by calling the bet() method with the second argument (if any). We then check that the bet doesn’t exceed 3, and finally check that it doesn’t exceed our remaining credits. Next we get the spin result itself:

        my $spin = join('', map{$symbols[rand @symbols]}1..3);
        $self->{spin} = $spin;

Here we have used map() to create a list of three random elements from the @symbols array and joined them into a single string, and we set the ‘spin’ attribute of the object. Next we need to calculate the payoff for this spin:

        my $payoff = 0;
        if($spin =~ /^(\d)\1\1$/){
            $payoff = $payoff{$spin}->($bet);
        } elsif ($spin =~ /^[123][123][123]$/) {
            $payoff = $payoff{any}->($bet);
        }

Here we first check if the spin contains three matching digits (using a regex), and if so we call the subroutine from the %payoff hash to obtain the payoff for that spin. The special case of ‘any’ is taken care of by the elsif clause. Lastly, we need to adjust the attributes:

        $self->{credits} -= $bet;
        $self->{credits} += $payoff;
        $self->{paid}     = $payoff;
        $self->{win}      = $payoff;
    }

We first subtract the $bet amount from the credits, then add any payoff (we could have done this in one step). Then we set the ‘paid’ attribute to equal the current payoff, and finally, we set the ‘win’ attribute to the payoff as well. Why? if the payoff is zero, then the win will be false, if there is a payoff, then the value will be true. So, rather than setting it to 0 or 1, we simply set it to the payoff itself.

Now we have a complete object oriented slot module. The complete module is:

    package Slot;
    use strict;
    my %payoff = (
        "777"  => sub {$_[0] * 500},
        "333"  => sub {$_[0] * 80},
        "222"  => sub {$_[0] * 20},
        "111"  => sub {$_[0] * 5},
        "any"  => sub {$_[0] * 2},
        "000"  => sub {$_[0] * 1},
        );

    my @symbols = (0,7,0,3,0,2,0,1);

    sub new {
        my $class   = shift;
        my $credits = shift || 100;
        my $self  = {credits => $credits,
                     bet     => 1,
                     win     => 0,
                     paid    => 0,
                     spin    => undef,
                    };
        bless $self, $class;
    }

    sub credits {
        my $self = shift;
        $self->{credits};
    }
    sub bet {
        my $self = shift;
        $self->{bet} = shift||$self->{bet};
    }

    sub spin {
        my $self = shift;
        my $bet  = $self->bet(shift);
        $bet = $bet < 3? $bet : 3;
        $bet = $bet < $self->credits()? $bet : $self->credits();
        my $spin = join('', map{$symbols[rand @symbols]}1..3);
        $self->{spin} = $spin;
        my $payoff = 0;
        if($spin =~ /^(\d)\1\1$/){
            $payoff = $payoff{$spin}->($bet);
        } elsif ($spin =~ /^[123][123][123]$/) {
            $payoff = $payoff{any}->($bet);
        }
        $self->{credits} -= $bet;
        $self->{credits} += $payoff;
        $self->{paid}     = $payoff;
        $self->{win}      = $payoff;
    }

    sub display_results {
        my $self = shift;
        print join(" ", split //, $self->{spin}),"\n";
        if ($self->{win}){
            print "Winner paid: $self->{paid} credits!!\n\n";
        } else {
            print "Better luck next time\n\n";
        }

    }
    1;
    __END__

And the interactive test script is:

    #!/usr/bin/perl -w
    use strict;
    use Slot;
    my $slot = Slot->new(100);
    while(1){
        my $credits  = $slot->credits();
        my $curr_bet = $slot->bet();
        last if $credits < 1;

        print "You have $credits remaining: max bet is 3\n";
        print "Enter your bet [$curr_bet]: ";

        chomp(my $bet = <STDIN>);
        last if $bet eq 'q';
        $bet ||= $curr_bet;

        $slot->spin($bet);
        $slot->display_results();
    }

    print "Game Over\n";
    print "You have ", $slot->credits(), " remaining\n";
    print "Thank you for playing\n";
    __END__

I have played a few times and always seem to lose my credits in a short time — so I guess it’s just like the real thing :-)

Class data and more methods (soot part V)

Andrew L. Johnson (First published by ItWorld.com 2001-06-28)

At this point in the object tutorial we have built a Slot class that builds a simple object with a few attributes and provides a couple of accessor routines for those attributes the user might need. But we don’t have working slot machine by any means.

Our slot machine will be a variation on the lucky-7 variety — it will have 3 spinning wheels each containing the following digits: 0,1,2,3,7. Getting three 7’s will pay the most, followed by three 3’s, three 2’s, three 1’s, three of any 1,2 or 3, and three 0’s.

The payoff scheme will be as follows (I don’t have much experience slot machines and odds, so I’m making this up as I go along — feel free to change things in your machine) [$bet indicates amount being bet]:

    777   pays  $bet * 500
    333   pays  $bet * 80
    222   pays  $bet * 20
    111   pays  $bet * 5
    any   pays  $bet * 2
    000   pays  $bet * 1

Here, "any" refers to spins with all wheels showing any of 1,2, or 3 (for example: 123, 332, 231). We can set this up as a hash of payoffs where the keys will be the spin result and the values will be anonymous subroutines that return the winnings:

    my %payoff = (
        "777"  => sub {$_[0] * 500},
        "333"  => sub {$_[0] * 80},
        "222"  => sub {$_[0] * 20},
        "111"  => sub {$_[0] * 5},
        "any"  => sub {$_[0] * 2},
        "000"  => sub {$_[0] * 1},
        );

This will only be accessed within our module, not by the user. We will call it like:

    $payoff = $payoff{$spin}->($bet);

But we’ll get to that shortly. So we have payoffs, but we still don’t have a machine. One very simple way to simulate this machine is with a single array containing all the digits. We can then just choose three random elements from that array and we’ll have our spin result:

    my @symbols = (0,7,0,3,0,2,0,1);

I’ve thrown in extra 0’s to make it more likely to appear (you can adjust the symbols list to better approximate a real slot machine’s probabilities if you know them).

Both the %payoff hash and the @symbols array are called class data. They are not stored with each object but only in the class, and all object methods can access them.

We still have two methods left to define: spin() and results(), according to our original specification and test program. The spin() method is the most complex and we will have to leave it to next week. But we can make an assumption about it: after spin() is called, all of the attributes will be set appropriately. That is, the credits attribute will be adjusted, the ‘win’ attribute will be set to 1 if it is a winning spin, and the ‘paid’ attribute will contain the payoff for that spin. The ‘spin’ attribute will contain a string of three digits representing the spin itself. Knowing this, we can build our results() method quite easily. However, I have decided to change the spec a little: now we will call the method display_results() and have it print the results rather than having it return the results to be printed:

    sub display_results {
        my $self = shift;
        print join(" ", split //, $self->{spin}),"\n";
        if ($self->{win}){
            print "Winner paid: $self->{paid} credits!!\n\n";
        } else {
            print "Better luck next time\n\n";
        }

    }

All we do here is grab the object and print out the spin itself by splitting it into separate digits and joining it with a space in between. We then test if the spin was a winning spin and if so we print a congratulatory message stating how much was paid, otherwise we try to encourage the loser to bet some more money.

*****