Kamilė Vainiūtė
Kamilė Vainiūtė

Reputation: 105

The difference between `$/` and `local $/`

For my program, I need to use FASTA files and do some calculations with them. In order to do that, I used local $/ = "^>, to chomp up my file in to header line and sequence lines. While my program does what I want it to do, why can't I just simply use $/ = "^>"? When I tried it, my results were not what I needed, I'm interested why is that so. Here's my simplified code:

my @array;
while(<>){
    local $/ = "^>";
    chomp;
    push (@array, $_);
    if(eof){
        for(@array){
            ...
        }
    ...
    }
    if(eof){
        @array = ();
    }

Upvotes: 2

Views: 1666

Answers (3)

zdim
zdim

Reputation: 66964

The local

modifies the listed variables to be local to the enclosing block, file, or eval.

It saves away the value of an (already existing) variable, which can then be changed as desired and still once the scope is exited its original value is restored.

As such it is used precisely with the global variables like $/ -- by local-izing them we can alter their value within the scope where we need it and not have it change for the whole program.

More is provided in perlsub.

What is shown raises questions though. The $/ variable takes a string, not a regex; from what I recall those "fasta" files have lines starting with >, not with ^>. Also, $/ need be set before reading lines (with <>) and I don't see how the shown code does what its intent is.

Upvotes: 5

ikegami
ikegami

Reputation: 386541

local $var saves the value of $var, and adds a directive to the stack that will cause the value of $var to be restored when the scope is exited (even by exception). It's the closest thing to my that's available to package variables.

$_ = 123;
{
   local $_ = 456;
   # $_ is 456 here.
}
# $_ is back to being 123 here.

This is useful to avoid causing problems in surrounding code or in the caller (in the case of subs).

Note that value of $/ is matched character-for-character. It's not treated as a regular expression.

Note that $/ appears to be set for no reason in the code you posted (unless you left something out).


why can't I just simply use $/ = "^>"

Then, the change wouldn't be undone at the end of the block, so it would affect the <> in the while condition as well as any code after your loop that performs reads.


How I'd handle a FASTA file:

my ($header, $seq);
while (1) {
   my $line = <>;
   if (!defined($line) || $line =~ /^>/) {
      work($header, $seq) if defined($header);

      last if !defined($line);

      chomp($line);
      $header = substr($line, 1);
      $seq = "";
   } else {
      chomp($line);
      $seq .= $line;
   }
}

Upvotes: 5

Stefan Becker
Stefan Becker

Reputation: 5972

Your code relies on undefined behavior:

Remember: the value of $/ is a string, not a regex. awk has to be better for something. :-)

  • you modify the value of $/ inside the while (<>) which is supposed to affect it.

Correct would be:

my @array;
{
    local $/ = ">";
    while (<>) {
        ...
    }
}

then the observed difference simply disappears, i.e. the code behaves the same with or without local.

But you should always use local on global variables to ensure that the original value is restored when leaving the scope of the local definition.

Upvotes: 1

Related Questions