views:

121

answers:

5

I have created the following subroutine gender to randomly print string MALE or FEMALE. When subroutine is invoked, the print command suffixes a "1" at the end of the string. See the sample code and output below:

sub gender { 
    if ( (int rand(100)) >50) {
        print "MALE  ";
    }
    else {
        print "FEMALE";
    }
}   

foreach (1..5) {
    print &gender, "\n"; 
} 

Notice a "1" is suffixed to "MALE" OR "FEMALE"

OUTPUT:

FEMALE1
FEMALE1
MALE  1
MALE  1
FEMALE1
MALE  1

I am using perl v5.8.9 v5.8.9 built for MSWin32-x86-multi-thread

Binary build 826 [290470] provided by ActiveState http://www.ActiveState.com Built May 24 2009 09:21:05

+14  A: 
print &gender

calls the gender function and prints what it returns. gender itself, as the last thing it does in either branch, prints a string. Implicitly, it returns the result of the last expression in it (the print "MALE" or print "FEMALE"), and print, when it succeeds, returns 1.

So either do this:

sub gender { if ( rand(100) >= 50 ) {print "MALE  ";}  else {print "FEMALE";}}
foreach (1..5) { &gender();  print "\n"; } 

or this:

sub gender { if ( rand(100) >= 50 ) {return "MALE  ";}  else {return "FEMALE";}}
foreach (1..5) { print &gender(), "\n"; }

Also, note that &gender, with & but without parentheses, is a special form of function invocation that isn't usually what people mean to use; either drop the & or add empty parentheses to your call.

I've also corrected the if test to return male 50% of the time and female 50% of the time, instead of 49% and 51% respectively.

ysth
or just drop the parentheses since the sub has been declared prior to the call. Best practice though is usually to use the empty parentheses anyway
mfontani
Porculus
+8  A: 

Let's get idiomatic with your code:

print gender(), "\n" 
    for 1..5;

sub gender {
    return  int rand(100) > 50 ? 'MALE' : 'FEMALE';
}

So, what did I do?

First:

  1. The gender sub should not be called with the & and no parens. This invokes the subroutine on the arguments passed to its caller. This is handy when you have a bunch of common argument sanitizing code. But it is not desirable or needed here.
  2. I put the sub after the other code because I like to read my code from high level to specific--the opposite of how C forces you to organize things. I don't like reading my code from the bottom up, so I did it this way. This is purely a personal preference. Do whatever makes you happy. Or if you have to work with others, follow the standard you've agreed upon.
  3. I shortened foreach to for. They do exactly the same thing, one takes fewer characters.
  4. I used for as a statement modifier. In other words I took a simple statement print $_, "\n"; and tacked the for onto the end. For simple tasks it is nicer than using a block. Again, this is my opinion. Some people decry statement modifiers as evil and unwelcome. If you decide to use them, keep it simple. YMMV.
  5. I got rid of the extra unneeded print ysth mentioned.
  6. Instead of using a big if/else block, I used the ternary operator (OK, it's really just a ternary operator, but people call it the ternary operator). It computes a test value and depending on the boolean value of the test, returns the result one of two expressions. It is handy when you want if/else logic in an assignment.
daotoad
7. .... profit!
ysth
What's the `int()` for?
tchrist
The int doesn't really do anything practical. It does force a small shift in the probabilities--`int(50.9)` is 50, and therefore is yields `FEMALE`. Without the `int`, 50.9 yields `MALE`. I guess this is an unintended feature, but I didn't want to change the probabilities. Maybe that's what number 7 is for.
daotoad
True, int() is just carried over from the work I was trying to resolve. It has no real purpose in the question under discussion.
Ratnendra Pandey
A: 

There is something about subroutine invocation that is still not clear to me. Notice that prefixing & and/or suffixing () does not seem to make any difference. What does seem to make a difference is whether the print is placed before or after the subroutine invocation. Notice different styles of print command in the following code:

sub gender { if (int rand(100)>50) 
                  {print "MALE  ";}  
             else 
                  {print "FEMALE";}}

foreach (1..4) {  print &gender(), "   :   style1 with & print before &gender()  \n"; }
foreach (1..4) {  &gender(), print "   :   style2 with & &gender() before print \n"; }
foreach (1..4) {  print  gender(), "   :   style3 & dropped print  before gender() \n"; }
foreach (1..4) {  gender(), print  "   :   style4 & dropped gender() before print \n"; }`

This generates the following output:

FEMALE1   :   style1 with & print before &gender()
MALE  1   :   style1 with & print before &gender()
MALE  1   :   style1 with & print before &gender()
MALE  1   :   style1 with & print before &gender()

FEMALE   :   style2 with & &gender() before print
FEMALE   :   style2 with & &gender() before print
FEMALE   :   style2 with & &gender() before print
MALE     :   style2 with & &gender() before print

FEMALE1   :   style3 & dropped print  before gender()
FEMALE1   :   style3 & dropped print  before gender()
MALE  1   :   style3 & dropped print  before gender()
FEMALE1   :   style3 & dropped print  before gender()

FEMALE   :   style4 & dropped gender() before print
MALE     :   style4 & dropped gender() before print
MALE     :   style4 & dropped gender() before print
FEMALE   :   style4 & dropped gender() before print
Ratnendra Pandey
I tried to explain this in my answer, but I'll try again: your gender function prints something and then returns 1 if the print was successful. If you include the call to gender in the arguments to print, that 1 that it returns gets printed.
ysth
+1  A: 

Without an explicit return, the Perl sub will return the last evaluated value. gender returns a 1 because in both execution paths, it calls print which returns a 1.

You should either be having gender return a string, which the caller then prints, or have gender do the printing, and have the caller not do anything with the return value.

Andy Lester