Re: use strict; use warnings;

Discussion in 'Perl Misc' started by Jens Thoms Toerring, Feb 24, 2014.

  1. Marek Novotny <> wrote:
    > Hi group,


    > Tonight I finished up another obj for my class. The class has not yet
    > started to learn about use strict; and use warnings;


    > I was given the following to solve:


    > # Objective 1:
    > # The pop function "pops" off the last element of an array
    > # and returns it. It works like this:
    > #
    > # $element = pop(@array);
    > #
    > # The shift function works the same way, but removes an element
    > # from the front of an array.
    > #
    > # Using pop, shift, push, and the starting code below, write a script
    > # that sufficiently "shuffles" a simulated deck of cards before printing
    > # the top five cards. Save this script in your home directory as
    > # obj10.pl.
    > #
    > # The goal of this objective is to familiarize yourself
    > # with these three functions while using loops and to use them
    > # together to rearrange the array but not a randomly shuffled array.


    > @startingdeck = ("A H","2 H","3 H","4 H","5 H","6 H","7 H","8 H",
    > "9 H","10 H","J H","Q H","K H",
    > "A D","2 D","3 D","4 D","5 D","6 D","7 D","8 D",
    > "9 D","10 D","J D","Q D","K D",
    > "A C","2 C","3 C","4 C","5 C","6 C","7 C","8 C",
    > "9 C","10 C","J C","Q C","K C",
    > "A S","2 S","3 S","4 S","5 S","6 S","7 S","8 S",
    > "9 S","10 S","J S","Q S","K S");


    > And here is my solution, which does not include the use of use strict;
    > and use warnings;


    This isn't the solution asked for - you're supposed to shuffle
    the array, not sort it. What's the most simple way to shuffle
    a deck of cards? Just remove a random card from the deck and
    put it on a new heap and repeat this until there are no more
    cards in the deck you started with. And you can shuffle your
    array in exactly the same way when you think about it.

    The first question is, of course, how to pick a card at random.
    Perl has a nice function called 'rand' for this (if you haven't
    yet please read the documentation for it, all you've got to do
    is type "perldoc -f rand" in your terminal and you get the do-
    cumentation.

    rand() returns a floating point number. If called without an
    argument (or with 0 or 1) it's a floating point number between
    0 and less than 1. If you give it any other argument it returns
    a random number between 0 and less than the argument. But what
    we need is an integer number we can use as an index into our
    array. That's were the int() function comes handy, which strips
    of the fractional part of a floating point number.

    Now, since you start with 52 cards, you would use

    my $rnd_index = int( rand( 52 ) );

    rand() will return a number between 0 and less than 52, and
    int() will make sure that it's an integer bewteen 0 and 51.
    Thus the result is suitable for accessing one of the elements
    of your array '@startingdeck' and we can put the value of
    that element into a new array named '@shuffled'. But we also
    have to remove the "card" from the "deck", i.e. the element
    from the array. In a very pedestrian way we could do it like
    this

    my @shuffled;

    my $rnd_index = int( rand( 52 ) );

    my @temp;

    for my $i ( 0 .. $rnd_index - 1 ) {
    push @temp, shift @startingdeck;
    }

    push @shuffled, shift @startingdeck;

    for my $i ( 0 .. $rnd_index - 1 ) {
    unshift @startingdeck, pop @temp;
    }

    As you can see we first move all cards before the one we
    randomly selected to a "temporary deck". Then we move the
    card we're interested in to our deck of shuffled cards. An
    then we put the cards we had stored on the tem,proray deck
    back onto the deck we pick cards from.

    By the way, if you haven't seen this before: the

    for my $i ( 0 .. $rnd_index - 1 ) {

    is a for look running from 0 up to '$rnd_index - 1', that's
    what the two dots are good for.

    Of course, the above code, deals only with the very first card
    we remove from the '@startingdeck' array. And it's a bit longish.
    Let's first see how we can make this a llop that deals with
    all the cards.

    When we use

    my $rnd_index = int( rand( 52 ) );

    we obviously assume that there are 52 cards in the '@startingdeck'
    array. But after we've removed on card only 51 one are left. It
    probably would make sense to get rid of the '52' and put some-
    thing there that is the actual length of '@startingdeck'. And
    this is quite simple, just use

    my $rnd_index = int( rand( @startingdeck ) );

    The trick here is that, when in a number is expected in a certain
    place, writing '@arrayname' gives you the number of elements in the
    array. And the rand() function expects a number, so we're on the
    safe side here. Shouldn't you be certain if a number is expected
    than you can always make sure by putting 'scalar' in front of it:

    my $rnd_index = int( rand( scalar @startingdeck ) );

    With this we now can create a look that deals with all cards:

    my @shuffled;

    while ( scalar @startingdeck ) {
    my $rnd_index = int( rand( scalar @startingdeck) );
    my @temp;

    for my $i ( 0 .. $rnd_index - 1 ) {
    push @temp, shift @startingdeck;
    }

    push @shuffled, shift @startingdeck;

    for my $i ( 0 .. $rnd_index - 1 ) {
    unshift @startingdeck, pop @temp;
    }
    }

    Well, that works but is rather long and a bit convoluted.
    One way to condense it a bit is to use array slices. When
    you write e.g.

    @startingdeck[ 6 .. 19 ]

    you get a new array containg the elements of '@startingdeck'
    starting with index 6 and ending with index 19 (note again the
    use of the '..' operator). Another nice feature is that you can
    combine two arrays rather easily:

    my @a = ( 1, 2, 3 );
    my @b = ( 4, 5 );
    my @c = ( @a, @b );

    After this '@c' will contain 5 elements, first the ones from '@a',
    then those from '@b'.

    Using that we can write

    my @shuffled;

    while ( @startingdeck ) {
    my $rnd_index = int( rand( @startingdeck) );

    push @shuffled, $startingdeck[ $rnd_index ];
    @startingdeck = ( @startingdeck[ 0 .. $rnd_index - 1 ],
    @startingdeck[ $rnd_index + 1 .. @startingdeck - 1 ] );
    }

    Now, that's already quite a bit shorter and not really much harder
    to understand, isn't it?

    But we can make it even shorter. There's another function for
    arrays, calles splice(), which allows you to remove (or insert)
    elements somewehere within an array. E.g.

    splice( @startingdeck, 5, 7 )

    removes 7 elements from '@startindeck', starting at index 5, and
    it returns the removed elements. We can use this to remove the
    random card from '@startingdeck' and, at the same time, put it
    onto the '@shuffled' array:

    my @shuffled;

    while ( @startingdeck ) {
    my $rnd_index = int( rand( @startingdeck) );
    push @shuffled, splice( @startingdeck, $rnd_index, 1 );
    }

    And, if you feel like it, you can condense the whole loop down
    to a single line of code:

    push @shuffled, splice @startingdeck, int rand @startingdeck, 1
    while @startingdeck;

    (You may notice that a lot of parentheses are optional.)

    About printing the first 5 element of '@shuffled': there's
    nothing wrong with using

    my $i = 0;
    while ( $i < 5 ) {
    print $shuffled[ i ], " ";
    }
    print "\n\n";

    But there are also ways to shorthen this a bit, e.g.

    print join( " ", @shuffled[ 0 .. 4 ] ), "\n\n";

    So, taking it all together, your whole assigment can done in
    just three lines of code;-)

    my @shuffled;
    push @shuffled, splice @startingdeck, int rand @startingdeck, 1
    while @startingdeck;
    print join( "\n", @shuffled[ 0 .. 4 ] ), "\n\n";

    But, of course, it doesn't fit the requirement to use just
    push, pop and shift...

    > # separate the hearts and organize them in order


    > my $i = 0;
    > while ($i < 13){
    > @hearts[$i] = shift(@startingdeck);
    > $i++;
    > }
    > $ace = shift(@hearts);
    > push(@hearts, $ace);


    > # separate the diamonds and organize them in order


    > my $i = 0;
    > while ($i < 13){
    > @diamonds[$i] = shift(@startingdeck);
    > $i++;
    > }
    > $ace = shift(@diamonds);
    > push(@diamonds, $ace);


    > # separate the clubs and organize them in order


    > my $i = 0;
    > while ($i < 13){
    > @clubs[$i] = shift(@startingdeck);
    > $i++;
    > }
    > $ace = shift(@clubs);
    > push(@clubs, $ace);


    > # separate the spades and organize them in order


    > my $i = 0;
    > while ($i < 13){
    > @spades[$i] = shift(@startingdeck);
    > $i++;
    > }
    > $ace = shift(@spades);
    > push(@spades, $ace);


    You probably have noticed that you do the same thing again
    and again. These are the things were it's a good idea to
    stop and consider if there's no way to avoid that...

    > # put the organized cards back in the starting deck


    > push (@startingdeck, @hearts, @diamonds, @clubs, @spades);


    > # sort the suits in reverse high order stating with ace, kings, etc.


    > my $i = 0;
    > while ($i < 51){
    > @sorted[$i] = pop(@hearts); $i++;
    > @sorted[$i] = pop(@diamonds); $i++;
    > @sorted[$i] = pop(@clubs); $i++;
    > @sorted[$i] = pop(@spades); $i++;
    > }


    > # print the top 5 highest cards


    > print "\nThe top five cards are: \n\n";


    > my $i = 0;
    > while ($i < 5){
    > print "@sorted[$i], ";
    > $i++;
    > }
    > print "\n\n";


    As said there's a lot of redundancy in your code and you
    actually don't seem to need the @hearts, @diamonds etc.
    arrays except for tempory storage. And there's a clear
    sign when a new suite starts, the last character from
    the elment of @startingdeck changes from 'H' to 'D' to
    'C' and finally 'S'. This can be used:

    my @sorted, @temp;

    while ( @startingdeck ) {
    my @array;
    my $char = substr( $startingdeck[ 0 ], -1 );

    # Push all elements from @startingdeck that have the same
    # character at the end onto an array

    push @array, shift @startingdeck
    while @startingdeck && substr( $startingdeck[ 0 ] eq $char;

    # Move the first element to the end

    push @array, shift @array;

    # Append the result to @temp and the reversed result to @sorted

    push @temp, @array;
    push @sorted, reverse @array;
    }

    # @startingdeck (now empty) is supposed to be just a copy of @temp

    @startingdeck = @temp;

    print join( " ", @sorted[ 0 .. 4 ] ), "\n\n";

    Regards, Jens
    --
    \ Jens Thoms Toerring ___
    \__________________________ http://toerring.de
    Jens Thoms Toerring, Feb 24, 2014
    #1
    1. Advertising

  2. Jens Thoms Toerring

    John Bokma Guest

    Marek Novotny <> writes:

    > # Using pop, shift, push, and the starting code below, write a script
    > # that sufficiently "shuffles" a simulated deck of cards before printing
    > # the top five cards. Save this script in your home directory as
    > # obj10.pl.


    You might to read:
    http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
    on how to shuffle.

    Or have a look at how List::Util does this. I normally do

    use List::Util 'shuffle';

    If I need to shuffle something.

    --
    John Bokma j3b

    Blog: http://johnbokma.com/ Perl Consultancy: http://castleamber.com/
    Perl for books: http://johnbokma.com/perl/help-in-exchange-for-books.html
    John Bokma, Feb 24, 2014
    #2
    1. Advertising

  3. Marek Novotny <> wrote:
    > Turns out I didn't read the objective correctly. They did indeed what the
    > deck more shuffled, and they did not want the high value cards, just the
    > top five elements from the array.


    > I did this on the fly, it doesn't contain all the suggestions given here
    > cause I haven't yet really internalized those yet.


    > #!/usr/bin/perl
    > # file: obj10-1.pl
    > # Objective 1:
    > #
    > # The pop function "pops" off the last element of an array
    > # and returns it. It works like this:
    > #
    > # $element = pop(@array);
    > #
    > # The shift function works the same way, but removes an element
    > # from the front of an array.
    > #
    > # Using pop, shift, push, and the starting code below, write a script
    > # that sufficiently "shuffles" a simulated deck of cards before printing
    > # the top five cards. Save this script in your home directory as
    > # obj10.pl.
    > #
    > # The goal of this objective is to familiarize yourself
    > # with these three functions while using loops and to use them
    > # together to rearrange the array but not a randomly shuffled array.


    > use strict;
    > use warnings;


    > my @startingdeck = ("A H","2 H","3 H","4 H","5 H","6 H","7 H","8 H",
    > "9 H","10 H","J H","Q H","K H",
    > "A D","2 D","3 D","4 D","5 D","6 D","7 D","8 D",
    > "9 D","10 D","J D","Q D","K D",
    > "A C","2 C","3 C","4 C","5 C","6 C","7 C","8 C",
    > "9 C","10 C","J C","Q C","K C",
    > "A S","2 S","3 S","4 S","5 S","6 S","7 S","8 S",
    > "9 S","10 S","J S","Q S","K S");


    > my $i = 0; my @randomdeck;
    > while ($i < 51){
    > $randomdeck[$i] = shift(@startingdeck); $i++;
    > $randomdeck[$i] = pop(@startingdeck); $i++;
    > }


    You don't necessarily need that '$i' variable and you can also
    use push here (as required):

    while ( @startingdeck ) {
    push @randomdeck, shift @startingdeck;
    push @randomdeck, pop @startingdeck;
    }

    But the whole exercise looks rather strange: at first they
    write something about 'sufficiently "shuffles"' and a bit
    later you have "but not a randomly shuffled". That makes no
    sense to me - I've never seem a definition of "sufficiently
    but not randomly shuffled" - for me "shuffled" implies some
    elememt of at least pseudo-randomness...

    What you do, i.e. taking the card from the top and then
    the one from the bottom, and repeating the exact same pro-
    cedure until the deck is empty, might fit the bill but
    there's no way to know for certain.

    Perhaps the most you can learn from this isn't something
    specific to Perl but that very often the requirements you
    get for a programming task are badly inconsistent (or some-
    times even violate the principle of causality;-). So now you
    can start on the second, hard part of the task: discuss with
    the customer what the requirements actually are supposed to
    mean without pissing him off by making him feel stupid;-)

    Regards, Jens
    --
    \ Jens Thoms Toerring ___
    \__________________________ http://toerring.de
    Jens Thoms Toerring, Feb 25, 2014
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Marius Gavrilescu

    Re: use strict; use warnings;

    Marius Gavrilescu, Feb 24, 2014, in forum: Perl Misc
    Replies:
    152
    Views:
    496
    $Bill
    Mar 11, 2014
  2. Kaz Kylheku

    Re: use strict; use warnings;

    Kaz Kylheku, Feb 24, 2014, in forum: Perl Misc
    Replies:
    0
    Views:
    67
    Kaz Kylheku
    Feb 24, 2014
  3. George Mpouras

    Re: use strict; use warnings;

    George Mpouras, Feb 24, 2014, in forum: Perl Misc
    Replies:
    0
    Views:
    56
    George Mpouras
    Feb 24, 2014
  4. Mart van de Wege

    Re: use strict; use warnings;

    Mart van de Wege, Feb 24, 2014, in forum: Perl Misc
    Replies:
    1
    Views:
    54
    Mart van de Wege
    Feb 25, 2014
  5. Justin C

    Re: use strict; use warnings;

    Justin C, Feb 24, 2014, in forum: Perl Misc
    Replies:
    2
    Views:
    77
    Justin C
    Feb 25, 2014
Loading...

Share This Page