3

I need to modify a variable inside a routine, so it keeps the changes after leaving the routine. Here's an example:

$text = "hello";
&convert_to_uppercase($text);
print $text;

I want to see "HELLO" on the screen, not "hello".

The routine would be:

sub convert_to_uppercase($text){
  <something like $text = uc($text);>
}

I know how to do it in PHP, but it seems that the parameters are not changed the same way. And, I've been searching everywhere and I couldn't find a concrete answer.

Richlv
  • 3,954
  • 1
  • 17
  • 21
gonfer
  • 33
  • 1
  • 5
  • This question might help: http://stackoverflow.com/questions/24063638/if-perl-is-call-by-reference-why-does-this-happen – AntonH Oct 16 '14 at 22:21
  • Thanks a lot! My new question is: why ($x1, $y1) = @_;? Would it be the same if i did: **$x1 = $_[0]; $y1 = $_[1];**? Please consider I'm really new to Perl, and thanks for your patience! – gonfer Oct 16 '14 at 23:14
  • Yes it is. `($x1, $y1) = @_;` is a shortcut. – AntonH Oct 16 '14 at 23:27

2 Answers2

6

You really shouldn't use an ampersand & when calling a Perl subroutine. It is necessary only when treating the code as a data item, for instance when taking a reference, like \&convert_to_uppercase. Using it in a call hasn't been necessary since version 4 of Perl 5, and it does some arcane things that you probably don't want.

It is unusual for subroutines to modify their parameters, but the elements of @_ are aliases of the actual parameters so you can do what you ask by modifying that array.

If you write your subroutine like this

sub convert_to_uppercase {
    $_[0] = uc $_[0];
}

then it will do what you ask. But it is generally best to return the modified value so that the decision on whether to overwrite the original value can be taken by the calling code. For instance, if I have

sub upper_case {
    uc shift;
}

then it can be called either as

my $text = "hello"; 
$text = upper_case($text);
print $text;

which does as you require, and modifies $text; or as

my $text = "hello";
print upper_case($text);

which leaves $text unchanged, but returns the altered value.

Richlv
  • 3,954
  • 1
  • 17
  • 21
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 1
    Great! One more thing: isn't it necessary to declare the routine signature? I see you do **sub convert_to_uppercase{}** instead of **sub convert_to_uppercase($text){}** but when you call it, you do **convert_to_uppercase($text)**. Another thing, I used the **&** because I didn't know I shouldn't, I'm really new to Perl. What I'm doing is a validation from user input, so I want to uppercase the input from the user (I'll check it later too), and return a 1 or a 0 if valid or not, that's why I don't use the the return to do this. You were really helpful, thanks a lot! – gonfer Oct 16 '14 at 23:22
  • @gonfer, no. Sub signatures have only been added as a core Perl feature since Perl 5.20 (which came out a few months ago), and only experimentally. (Though there are several implementations of signatures available as extension modules of varying levels of stability.) – tobyink Oct 17 '14 at 09:37
6

Passing a reference and modifying the original variable inside the subroutine would be done like this:

$text = 'hello';
convert_to_uppercase(\$text);  #notice the \ before $text
print $text;

sub convert_to_uppercase {       #perl doesn't specify arguments here

    ### arguments will be in @_, so @_ is now a list like ('hello') 
    my $ref = shift;             #$ref is NOT 'hello'. it's '$text'

    ### add some output so you can see what's going on:
    print 'Variable $ref is: ', $ref, " \n";  #will print some hex number like SCALAR(0xad1d2)
    print 'Variable ${$ref} is: ', ${$ref}, " \n"; #will print 'hello'

    # Now do what this function is supposed to do:
    ${$ref} = uc ${$ref};  #it's modifying the original variable, not a copy of it
}

The other way is to create a return value inside the subroutine and modify the variable outside of the subroutine:

$text = 'hello';
$text = convert_to_uppercase($text);  #there's no \ this time
print $text;

sub convert_to_uppercase {
    # @_ contains 'hello'
    my $input = shift;    #$input is 'hello'
    return uc $input;    #returns 'HELLO'
}

But the convert_to_uppercase routine seems redundant because that's what uc does. Skip all of that and just do this:

$text = 'hello';
$text = uc $text;
Shawn Darichuk
  • 380
  • 4
  • 9
  • Hello, you cleared out all my doubts! As I replied the other guy, I'm validating a key entered by the user. I want to uppercase it AND validate it, and leave it in upper case for future checks. If it's valid, I'll return 1, otherwise 0. A simple **uc** would be easier, but I wanted to do all this inside a single routine. Thanks a lot to you too. I'm amused by the quality and speed of the replies here. – gonfer Oct 16 '14 at 23:28
  • One moe thing now I read you reply again: modifying the @_, the variable is changed outside the routine, but is it necessary to pass it with the \ when calling the subroutine? This way, the $ref is an actual reference to the variable, and you de-reference it with the {}. The use of {} is only when you pass the parameter with the \? Thanks! – gonfer Oct 16 '14 at 23:33
  • It's worth telling that you can use `convert_to_uppercase($text);` (without "\" before the subroutine parameter) instead of `convert_to_uppercase(\$text);` by adding "\" before `shift`: `my $ref = \shift;` – Omar Jan 25 '17 at 14:35