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

Hi, Monks- Could someone explain why this code works perfectly in Perl v5.6.1 but not in v5.20.1? It imports a text file of names of bills and their due dates and converts to hash: CITI 5 AMEX 10 CHASE 20 Number on right becomes key, name is value:

open(my $fh, '<', 'c:/003/MYBILLS.TXT'); while(<$fh>) { my %hash = map{ chomp; split/ /; $_[1] => $_[0];} <$fh>; print$hash{20}; #prints CHASE

In v5.20.1 nothing will print here. But why? </p?

Replies are listed 'Best First'.
Re: newer Perl version rejects this
by davido (Cardinal) on Feb 08, 2015 at 05:45 UTC

    In older versions of Perl, in scalar context split would split into @_, and return the number of elements resulting from the split operation. This "feature" was deprecated at least as long ago as 5.8.8, but possibly earlier. And it has since been removed from Perl. I don't know which version of Perl removed it, but clearly it's not present in 5.20.1.

    You have another bug as well. The while loop's first iteration will read the first line of your input file, but you never use it. The "map" line will slurp the remainder of the file, so there should never be a second iteration of the while loop. If the first line of your file is a header line, that's probably ok. But if the first line is real data, you're losing it.

    You can fix the v5.20 issue by changing the map line to this:

    my %hash = map{ chomp; local @_ = split/ /; $_[1] => $_[0]; } <$fh>;

    Or to this:

    my %hash = map { chomp; (split / /)[1,0]; } <$fh>;

    Removing the while loop should clear up the other bug. If you still need to skip past a header line, do it more explicitly.


    Dave

      This "feature" was deprecated at least as long ago as 5.8.8, but possibly earlier. And it has since been removed from Perl. I don't know which version of Perl removed it, but clearly it's not present in 5.20.1.
      Yes. From Perl 5.8.8 perldoc:
      In scalar context, returns the number of fields found and splits into the @_ array. Use of split in scalar context is deprecated, however, because it clobbers your subroutine arguments.
      AFAICT, it was deprecated in Perl 5.8 and removed in 5.12. Unfortunately, I could not find a reference to it in any perldelta. Indeed, bug report #97382 complains that it was removed from Perl 5.12 without appearing in perldelta.

      As a simpler standalone test of this specific deprecation, I ran this program (dent.pl):

      use strict; use warnings; use Data::Dumper; my %hash = map { split; $_[1] => $_[0] } "value key", "meaning-of-life + 42"; print Dumper( \%hash );
      with Perl 5.8.8 and it produced:
      Use of implicit split to @_ is deprecated at dent.pl line 4. $VAR1 = { '42' => 'meaning-of-life', 'key' => 'value' };
      That is, it worked while printing a warning to stderr (note that you need use warnings to see the warning). As you might expect, running it with Perl 5.12+ fails spectactularly, producing:
      Useless use of split in void context at dent.pl line 4. Use of uninitialized value in list assignment at dent.pl line 4. Use of uninitialized value in list assignment at dent.pl line 4. $VAR1 = { '' => undef };

      This version seems to work everywhere:

      use strict; use warnings; use Data::Dumper; my %hash = map { (split)[1,0] } "value key", "meaning-of-life 42"; print Dumper( \%hash );

      Apart from the pragmatic "writing to @_ clobbers your subroutine arguments" reason, another (more theoretical) reason for changing this behavior is that a function that sets a global variable (i.e. @_) is a function with a side-effect and side effects are considered evil in functional programming because they violate referential transparency.

      BTW, I am much more intimate with this specific Perl deprecation that I ever wanted to be owing to this grueling golf game (see "Fun with split" section). Note that side-effects, while anathema to functional purists, are cherished by golfers because they often lead to shorter code ... as they did in this golf game.

      Then, perldoc's pelsub is needing for an update:

      And the split gets called in scalar context so it starts scribbling on your @_ parameter list. Ouch!

        Good catch. I'm not sure if anyone else has already reported it, but your post motivated me to submit a perlbug report: https://rt.perl.org/Public/Bug/Display.html?id=129297.

        Didn't notice till afterward that I had perlbrew set to a perl-5.22 installation, but the report should be adequate either way.


        Dave

      I'm not sure where the "while(<fh>)" came from -it wasn't part of the original; I must've mixmashed different programs. Your map line suggestion fixed the problem. Thank you, all you Monks, for saving this newbie's a** once again.

Re: newer Perl version rejects this
by BrowserUk (Patriarch) on Feb 08, 2015 at 05:50 UTC

    As posted that won't work on any build!

    1. The while block is never closed: while(<$fh>) { ie. Missing '}'.
    2. And if you closed the block like this:
      while(<$fh>) { my %hash = map{ chomp; split/ /; $_[1] => $_[0];} <$fh>; } print$hash{20}; #prints CHASE

      It wouldn't work because you read one line at the top of the loop throw it away; then read the rest of the lines into the map.

      But even if you don't care about the first line it still won't work because the hash is defined inside the loop and will disappear as soon as you exit it.

    Bottom line is: your code is broken wherever you try to run it.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked