Question

#!/usr/bin/perl

use feature "switch";
use strict;
use warnings;

while (<DATA>) {
  given ($_) {
    when ("abc" or "def") {
      # ...
      next;
    }
    when (/\w{3}/) {
      print $_;
      next;  
    }

}

__DATA__
abc
def
ghi
lmn
opq
rst
uvz

I'm having this output:

abc
def
ghi
lmn
opq
rst
uvz

but I want this one:

ghi
lmn
opq
rst
uvz

I don't know what I'm missing, please help.

Was it helpful?

Solution

First of all, don't use given/when. The smartmatch feature which it builds upon is a failure, and should be avoided. The correct way to write this is:

use strict;
use warnings;
use feature 'say';

while (<DATA>) {
    chomp;
    next if $_ eq 'abc' or $_ eq 'def';
    say $_;
}

But back to given/when. The when ("abc" or "def") condition is – for some reason – parsed as when ("abc") (verify this by running a script under perl -MO=Deparse). If you want to smartmatch against a set of values, use an array: when (["abc", "def"]).

That condition will still not match because $_ is a string with a trailing newline. You need to chomp it first.

Of course, the next inside the when blocks is entirely unnecessary. Unlike in C's switch/case, there is no implicit fallthrough. To explicitly continue matching, one can use the continue command. So your code could be rewritten to:

while (<DATA>) {
  chomp;
  given ($_) {
    when (["abc", "def"]) {
       # do nothing
    }
    when (/\w{3}/) {
      print "$_\n";
    }
  }
}

But let's assume it had been written

while (<DATA>) {
  chomp;
  given ($_) {
    when (["abc", "def"]) {
       # do nothing
    }
    when (/\w{3}/) {
      print $_;
    }
  }
  print "\n";
}

So we would have to skip the print "\n". In that case, we can use loop labels:

LINE:
while (<DATA>) {
  chomp;
  given ($_) {
    when (["abc", "def"]) {
       next LINE;
    }
    when (/\w{3}/) {
      print $_;
    }
  }
  print "\n";
}

Any loop control command like next, redo, last, continue can take a label to refer to some outer loop.

OTHER TIPS

Unfortunately, given/when is a bit troublesome and marked as experimental. There is some controversy around it. You can find out more about the experimental details here.

In your context, you can use break to break out of the enclosing given block

#!/usr/bin/perl
use strict;
use warnings;
use feature "switch";

while (<DATA>) {
    given ($_) {
        break when (["abc\n","def\n"]);
        when(/\w{3}/) {
            print $_;
            break;
        }
    }
};

__DATA__
abc
def
ghi
lmn
opq
rst
uvz
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top