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

In a previous reply (Re: Use of BEGIN block) , pfaut suggested using BEGIN and END blocks with the Command Switch n . Here is a complete example.

#!perl -n use strict; use warnings; # USAGE: perl np.pl <np.dat BEGIN{ our $total = 0; } our $total; $total += $_; END{ our $total; print $total }

Note that the package variable $total is in scope in all three blocks because it is declared in each with our. Is there a preferred way to do this? I understand that without strict, no declaration is necessary and with the use vars syntax, there is no restriction on scope.

Bill

Replies are listed 'Best First'.
Re: Initialize variable in BEGIN
by hippo (Archbishop) on Apr 30, 2025 at 14:50 UTC

    I would declare it only once, like this:

    #!/usr/bin/env -S perl -n use strict; use warnings; # USAGE: perl np.pl <np.dat our $total; BEGIN{ $total = 0; } $total += $_; END{ print $total }

    This seems less verbose while retaining the clarity but it is all subjective, of course.


    🦛

      [Deleted incorrect comment.]

Re: Initialize variable in BEGIN
by Fletch (Bishop) on Apr 30, 2025 at 16:43 UTC

    For stuff like this I kind of cheat and use -M'5;my $total = 0;' instead (although I wouldn't just for zero since undef does the right thing anyhoo). That along with the eskimo kiss trick (closing the implicit -n or -p while block with }{ ; using -MO=Deparse can show what you're really getting) lets you handle most things.

    perl -F, -M'5;my $t = 0' -lanE '$t += $F[2];}{ say $t' some_file.csv

    Edit: And I'd never use -n in a shebang line, just when trying for a (too) clever / short one-liner. If you're not golfing or sticking this in the middle of a pipeline it's much better to be explicit in your script and not need to bother with tricks to get reasonable scoping.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Initialize variable in BEGIN
by LanX (Saint) on Apr 30, 2025 at 15:37 UTC
    our was introduced¹ into Perl to explicitly have the same lexical scoping rules like my but now for package variables.

    Hence you are declaring 3 different² variables in your code.

    Since declarations happen at compile time it's safe to have only one in the surrounding scope (block or file) This will cover all nested scopes.

    Like demonstrated by hippo in the other reply on the file scope.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    Updates

    ¹) See https://perldoc.perl.org/5.6.0/perldelta#%22our%22-declarations

    ²) That's probably confusing, three different lexical aliases are declared, but all store inside the same package variable. It's just that the values are switched whenever the scope is changing, giving the appearance of different variables. Similar to local

Re: Initialize variable in BEGIN
by ikegami (Patriarch) on Apr 30, 2025 at 16:47 UTC

    Using -n just complicates things for nothing.

    With -n:

    #!/usr/bin/perl -n use strict; use warnings; our $total; BEGIN { $total = 0; } $total += $_; END { print $total, "\n"; }

    Without -n:

    #!/usr/bin/perl use strict; use warnings; my $total = 0; while ( <> ) { $total += $_; } print $total, "\n";

    One is clearly a million times better.

Re: Initialize variable in BEGIN
by ikegami (Patriarch) on Apr 30, 2025 at 16:49 UTC

    Using -n just complicates things for nothing.

    With -n:

    #!/usr/bin/perl -n use strict; use warnings; our $total; BEGIN { $total = 0; } $total += $_; END { print $total, "\n"; }

    Without -n:

    #!/usr/bin/perl use strict; use warnings; my $total = 0; while ( <> ) { $total += $_; } print $total, "\n";

    One is clearly a million times better.

      It might make more sense if expressed as a one-liner.

      $ for i in {1..10}; do echo $i; done | perl -ne 'BEGIN {our $prod = 1; +} $prod *= $_; END { print $prod,$/; }' 3628800
      90% of every Perl application is already written.
      dragonchild

        I wasn't saying that BEGIN is never useful. I was talking about in the situation at hand.

        But even in your entirely different scenario, does it really make sense?

        perl -nle'BEGIN { $prod = 1; } $prod *= $_; END { print $prod; }'

        vs

        perl -le'$prod = 1; $prod *= $_ while <>; print $prod;'