lundi 24 février 2014

Re: use strict; use warnings; topic




Marek Novotny <(E-Mail Removed)> 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 ___ (E-Mail Removed)
\__________________________ http://toerring.de





Aucun commentaire:

Enregistrer un commentaire