kiat has asked for the wisdom of the Perl Monks concerning the following question:

Hi there, In the sample code below, if I want to declare the array @entries wity 'my', where is the best spot to do it? Should I declare it before the first 'if' statement? Or is it necessary perhaps to declare it twice, once in each 'if' statement.
sub openFile { # Should I declare my @entries here??? if($file1) { open FH, "$file1" or die $!; @entries = <FH>; close FH; } if($file2) { open FH, "$file2" or die $!; @entries = <FH>; close FH; } }
Kindly advise :) kiat

Replies are listed 'Best First'.
Re: where to declare a variable...
by dvergin (Monsignor) on Dec 09, 2001 at 08:28 UTC
    Before the "if" blocks.

    A common rule of thumb is that you want to declare the variable in as narrow a scope as you can and still be able to access it where you need to.

    So you could try declaring the array inside the if blocks for a very narrow scope. But then you would discover that you were not able to access it outside the scope of the if blocks -- which I presume you will want to do when you flesh out the subroutine (otherwise why load the contents of the file into the array in the first place).

    ------------------------------------------------------------
    "Perl is a mess and that's good because the
    problem space is also a mess.
    " - Larry Wall

      ... you want to declare the variable in as narrow a scope as you can and still be able to access it where you need to.

      ++dvergin!

      Of course, this assumes that the fragment presented was whittled down to some minimum amount needed to ask the question.

      In other words, it's probably safe to presume that the if block would be followed by code which does something with the @entries array, such as return a reference to it or its contents, in which case perhaps something like this is more appropriate:

      sub openFile { my ($file1, $file2) = @_; # assume these are passed as parameters my $fn = $file1 || $file2; # replaces the 'if' in original open FH, $fn or die "$fn: $!"; my @entries = <FH>; close FH; # in list context, return the entire array, else (scalar context) a +ref return wantarray ? @entries : \@entries; }

      dmm

      
      You can give a man a fish and feed him for a day ...
      Or, you can teach him to fish and feed him for a lifetime
      
      Hi All, A big thank you for taking the time to respond to my question on 'my'. This is definitely a place I'll keep coming back.
Re (tilly) 1: where to declare a variable...
by tilly (Archbishop) on Dec 09, 2001 at 09:54 UTC
    How are $file1 and $file2 getting in there? Are they poorly named globals? Furthermore your error message should include the filename, and if possible a stack backtrace (which is easily done by using Carp to confess). And while we are at it, you have blocks with almost entirely duplicated code, and the function does something entirely different from what its name promises. It claims to open a file. Instead it reads it into an array.

    I would write that something like this:

    use Carp; # Time passes... # takes a filename, and returns the contents of # the file as an array in array context, string in scalar. sub read_file { my $file = shift; local *IN; open(IN, "< $file") or confess("Cannot read '$file': $!"); return wantarray ? <IN> : join '', <IN>; }
    More accurate name. Better error reporting. Less code duplication...
Re: where to declare a variable...
by jryan (Vicar) on Dec 09, 2001 at 09:11 UTC

    Dvergin is indeed correct when he says that you should declare @entries right before the if blocks; assuming, that is, that you are going to be using it outside of the if blocks. However, if the usage is going to be exclusive to inside of the blocks, you may want to consider declaring it twice, once for each block. Garbage collection is done at the end of the block the variable is declared in, so you will want your variables garbage collected as soon as possible if you are no longer using it. Here is an example:

    my $entry = 1; my $item; while($entry) { $item = "foobar"; print $item; $entry--; }

    In this example, $item is declared out of the loop, even though it is not used outside of the loop. Since it wasn't going to used outside of the loop, it should have been declared inside of the loop so that it was destroyed (and the memory it uses freed) when the block exits.

    From looking at the context of your code, it appears that you might want to use @entries outside of the if statements, so you should follow dvergin's suggestion. Otherwise, always make sure that you declare your variables in the inner-most possible block that you can so that you don't unnecessarily tie up memory

      In this example, $item ... wasn't going to used outside of the loop, it should have been declared inside of the loop so that it was destroyed (and the memory it uses freed) when the block exits.

      You raise a valid point, but perhaps chose an inappropriate example. Particularly when a block is the body of a loop, you should weight the cost of creating and destroying a loop-scoped lexical variable against the need to keep the memory from being tied up. In this particular (obviously contrived) example, I might leave $item scoped outside the loop if I knew the loop would execute many times, just to avoid the create/destroy cost in terms of execution time and memory fragmentation (which itself might cost execution time later in the program, by slowing down subsequent allocations).

      dmm

      
      You can give a man a fish and feed him for a day ...
      Or, you can teach him to fish and feed him for a lifetime
      
        In perl the lexical creation of my is a compile time action, The only run time effect is that it is asigned to or set to undef. local on the other hand is a run time directive and will have a speed penalty.
        There is still a cost of entering and leaving lexical scopes in perl, but it is incured reguardless of variable declarations.
Re: where to declare a variable...
by atcroft (Abbot) on Dec 09, 2001 at 12:47 UTC
    Assuming there are reasons for the way you coded that routine (and you may wish to check for a better way to code it, although my guess is that is a striped-down version to focus on just the problem you present here), I think what I would do (although there are many more knowledgable here than I, and I hope they will correct me if I am in error) would be thus:
    sub openFile { my (@entries, $filename); $filename = $file1 if ($file1); $filename = $file2 if ($file2); open(FH, $filename) or die($!); @entries = <FH>; close(FH) # Other processing here }
Re: where to declare a variable...
by Dylan (Monk) on Dec 10, 2001 at 13:58 UTC

    Well, depends on what you want to do.
    I quote, perldoc -f my:

    A "my" declares the listed variables to be local (lexically) to the enclosing block, file, or "eval".

    Let the code speak:
    Run this code, study this code... :)

    #!/usr/bin/perl #Is you is, or is you ain't, my foobar? #The dope on scope. baz(); #call baz. sub baz { my $foobar="I feel Baz!\n"; print $foobar; #$foobar is now lexically scoped for #the whole subroutine block. if (1) { #This is another block, it belongs to if. my $foobar="I am the FooMan!\n"; print $foobar; #$foobar is now lexically scoped #for this if block; } print $foobar; if ("I own several pairs of pants") { #That ^^^^ is another if, and this is its block my $foobar="I am the BarMan!\n"; print $foobar; #$foobar is now lexically scoped for this if block; } print $foobar; }

    Now, what I would do for your little file opener is

    sub openFile { my @entries; #is now lexically scoped for the entire subroutine; if ($file1) { open FH, "$file1" or die $!; @entries = <FH>; close FH; } if ($file2) { open FH, "$file2" or die $!; @entries = <FH>; #Replace all the entries? what? close FH; } }

    Get it? Also, there are many flaws with the openFile code, but I digress
    Have fun!

      Hi Dylan, Thanks very much for explaining so clearly with that code of yours. I now understand 'my' and the meaning of scoping better. cheers, kiat
Re: where to declare a variable...
by rje (Deacon) on Dec 10, 2001 at 20:38 UTC
    Redeclaration
    Bit bucket claims old data
    Never to return

    Only declare once
    Vanquish data entropy;
    Your comment is right

    However,

    There must be another option.
    Could you do this?

    open FH, $file1 or $file2 or die $!; my @entries = <FH>; close FH;

    Rob

      Mea culpa.

      You can't say
      open FH, $file1 or $file2 or die "Rats!\n";
      Stick with the earlier poster's suggestion:

      $fh = $file1 || $file2; open FH, $fh or die $!; etc

      Sorry 'bout that.

      Rob

A Simple Explanation of scoping
by tradez (Pilgrim) on Dec 11, 2001 at 03:38 UTC
    Scoping is probably the most important part of good code. It helps allow for all aspects of good coding including polymorphism, inheritance, and even eventual OO'ification. A simple rule of thumb is that of the curly brakets. a "my"ed variable is only "visible" to the code in the same set of outermost curly brackets. Given this example:
    sub main { my $blah; if ($blah =~/(\d+)\s{2}/) { my $test = $1; } }
    anything outside of the if {} will not see or be affected by the assignment of $test. but anything within sub main {} will be able to see $blah because it has been globally instantiated. Hope that helps.