Ratnendra Pandey
Ratnendra Pandey

Reputation: 83

Why does invoking print in a subroutine append 1 to the string?

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

Upvotes: 1

Views: 818

Answers (4)

Ratnendra Pandey
Ratnendra Pandey

Reputation: 83

Thank You everyone for helping me out with this. I found a way to make a chart I wanted. Here is how I finally did it;

print   "GENDER    NAME   AGE   HEIGHT  WEIGHT \n";
foreach (1..10) {                       ## Starting foreach loop
$age    = int(rand( 50))+10;
$height = int (rand(40)) + 50;
$weight = int (rand (100)) + 100;
sub randchar4bit {(chr int rand(25)+65).(chr int rand(25)+65). (chr int rand(25)+65).(chr int rand(25)+65)};
sub gender { return (int rand(100)>50)? "MALE    " : "FEMALE  ";} ;

print gender(), "  ", &randchar4bit,   "    $age     $height      $weight   style 1\n";
}; ## closing foreach loop 

It generates a nice output:

GENDER    NAME   AGE   HEIGHT  WEIGHT
FEMALE    HHRN    41     67      165   style 1
MALE      HNMF    27     63      187   style 1
MALE      NLDB    26     54      165   style 1
FEMALE    REMB    33     71      118   style 1
MALE      TWEW    10     57      122   style 1
MALE      OCSC    35     80      168   style 1
FEMALE    TKTR    25     64      179   style 1
MALE      GMYN    47     73      123   style 1
MALE      YKUG    50     79      148   style 1
FEMALE    HDFW    47     73      159   style 1

Upvotes: 0

Andy Lester
Andy Lester

Reputation: 93715

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.

Upvotes: 2

ysth
ysth

Reputation: 98398

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.

Upvotes: 14

daotoad
daotoad

Reputation: 27183

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.

Upvotes: 8

Related Questions