5

Documentation seems sparse regarding when statements outside of given blocks. It is said the when can be used when a 'topic' has been set, but when exactly is a topic considered set? Consider these cases:

for (@arr) {
  when { }
}

Seems this is the default, basic case, but I couldn't get even this working on my perl 5.14.2 - $ perl -Mfeature=switch -e 'foreach (qw(a b c)) { when (/a/) {print 'boom'} }' prints nothing. What am I doing wrong? Turned out this was just another bash quoting issue.

for my $elem (@arr) {
  when { }
}

Would this work? Will $elem automatically become the topic for when to use?

for (@arr) {
  $_ = some_expression($_);
  when { }
}

Would this work? Can the topic be set inside the loop?

Also, is there any difference when each of the above code segments uses foreach instead of for?

Basically, I'm very unclear on the topic of topics, so please enlighten me.

Sundar R
  • 13,776
  • 6
  • 49
  • 76

2 Answers2

7

It's a shell command construction error.

perl -Mfeature=switch -e 'foreach (qw(a b c)) { when (/a/) {print 'boom'} }'

should be

perl -Mfeature=switch -e 'foreach (qw(a b c)) { when (/a/) {print '\''boom'\''} }'

We can switch to double quotes to simplify

perl -Mfeature=switch -le'foreach (qw(a b c)) { when (/a/) {print "boom"} }'

And we can simplify further

perl -E'for (qw(a b c)) { when (/a/) { say "boom" } }'

Keywords for and foreach are one and the same to Perl.

Note that 5.18 marked given and when as experimental. It won't go away, but it will change. I explain what's going on here.


No, when doesn't use $elem; it always uses $_.


Yes, you can change $_ inside of for or given if you so desire. Note that foreach loops aliase their topic to the element being visited, so changing one changes the other.

my @x = qw( abc def );
for (@x) { $_ = uc($_); }
say @x;  # ABCDEF
Community
  • 1
  • 1
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I always go wrong with the quotes, thanks. Thanks for the link to your other answer, that and the links from there helped understand the status better. I have a weird case with this one: `perl -E '@arr = qw(a b c); foreach $x (@arr) { $_=$x; $_++; when (/b/) {print $x} }'` It prints a as expected, but then seems to die with the error "Can't use when() outside a topicalizer at -e line 1.". The same code runs without error (but printing nothing) if I remove the two lines with $_. Could you explain what's going on here? – Sundar R Jul 03 '13 at 23:05
  • @sundar, The problem isn't the choice of quotes. The problem is that you didn't escape them properly. See update. – ikegami Jul 03 '13 at 23:08
  • this syntax for escaping ' is new and useful to me, thanks. regarding the topicalization, that's why I mentioned that "the same code runs without error if I remove the two lines with $_". It seems that any for/foreach loop is treated as a topicalizer, even for SOMETHING (...) ones, but assigning to $_ inside the loop messes that up somehow. – Sundar R Jul 03 '13 at 23:26
  • It's not because you're doing something in the loop. You only get told of the error if the `when` is successful (i.e. `$_ ~~ /b/` is true), and it just so happened to be successful that one time. Try `perl -E'$_="b"; for my $x ("b") { when (/b/) { } }'`. `for SOMETHING (...) { ... when ... }` is not allowed if `SOMETHING` isn't blank, `$_`, `my $_`, `our $_` or `state $_`. – ikegami Jul 04 '13 at 01:10
  • Oh, why is it that `when` works one time but not after? Could you please explain the logic behind that? I'm just trying to understand the whole picture. I appreciate you taking the time to clarify things repeatedly, thanks. – Sundar R Jul 04 '13 at 09:29
2

Ikegami spotted your actual problem. Regarding the other questions:

The two topicalizers are for/foreach and given. Other constructs that set $_ (the topic variable), are not considered topicalizers (e.g. map or grep).

If you run a foreach-loop with an explicit loop variable, the loop isn't considered to be a topicalizer. Therefore,

for my $x (1..3) {
  local $_ = $x;
  say when 2;
}

will work for the first iteration, but then die (Can't "when" outside a topicalizer) once the when is exited.

The given is different in that it doesn't localize $_, but gives a lexical $_. That is, given (@a) { foo; } is similar to { my $_ = \@a; foo; } (I think this might be fixed).

amon
  • 57,091
  • 2
  • 89
  • 149