http://www.perlmonks.org?node_id=1009773

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

Hello,

I was trying to assing a value to $hash{'AirIATA'} but was failing and then used @hash{'AirIATA'} and voila, it worked but I got a warning as "Scalar value @hash{'AirIATA'} better written as $hash{'AirIATA'} at makeAnnounce.pl line 31."

So, what is wrong or what is right ?

Here is the problematic part of my code;

file1;
@hash{'AirIATA'}=get_IATA($airlineLoc,$hash{'Airline'}); my $letter1 = substr($hash{'AirIATA'},0,1); my $letter2 = substr($hash{'AirIATA'},1,2); print $letter1,"\n",$letter2,"\n";

------------

file2;
sub get_IATA { my $file = shift; my $airline = shift; my @data; my $count=0; open(my $fh, '<', $file) or die "Can't read file '$file' [$!]\ +n"; while (my $line = <$fh>) { my @fields = split(/;/, $line); push @data, @fields; } foreach my $data ( @data) { if ($data eq $airline) { return $data[$count-1],"\n"; } $count++; } }

Replies are listed 'Best First'.
Re: Warning is right or not ?
by davido (Cardinal) on Dec 20, 2012 at 19:02 UTC

    Yes, the warning is right; if you're assigning a single scalar value to a hash you probably don't want to be using hash slice semantics. If your code was failing when you did it "the right way", there's a bug elsewhere in the code. I couldn't run the code you provided because I don't know what your initial data set looks like, nor do I know what $hash{Airline}'s initial state is before the code you showed.

    Perhaps you could follow up with a post in this same thread that demonstrates the problem in a self-contained script that we can actually run to see the behavior. Chances are good that as you attempt to boil it all down to a small test script of under 20 lines, you'll discover the bug yourself and be able to fix it.

    But yes... the warning is accurate. You shouldn't have to assign a single value to a hash slice like that.


    Dave

Re: Warning is right or not ?
by RichardK (Parson) on Dec 20, 2012 at 19:47 UTC

    Did you really intent to put a comma in this, or is it a typo?

    return $data[$count-1],"\n";

    As it is, this will only return "\n"

      That is typo, because there was print before return to test that line, thank you for pointing that out. And when I removed that, the warning disappeared too, thank you very much :)
Re: Warning is right or not ?
by tobyink (Canon) on Dec 20, 2012 at 18:59 UTC

    Given that get_IATA seems to to return the same thing in list and scalar context, in this case I'd expect them to be equivalent, but $hash{'AirIATA'} is less obscure.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: Warning is right or not ?
by AnomalousMonk (Archbishop) on Dec 20, 2012 at 20:14 UTC

    The function  get_IATA() defined in the OP returns either a two element list if the condition  $data eq $airline is ever true, or the pre-post-incremented value of  $countfalse otherwise. (BTW: Is the latter behavior really intended?) The effects of assigning a multi-element list in scalar versus list context are shown in the code example below. Is the behavior in the scalar assignment below similar to the unspecified failure mentioned in the OP? If so, the proper cure is to return only a single scalar (perhaps the hash value with a concatenated newline) and assign in scalar context, i.e., to  $hash{'AirIATA'} and not to a slice.

    >perl -wMstrict -MData::Dump -le "my %hash; ;; $hash{'AirIATA'} = get_IATA(); dd \%hash; ;; @hash{'AirIATA'} = get_IATA(); dd \%hash; ;; sub get_IATA { return 'useful', 'unexpected'; } " Scalar value @hash{'AirIATA'} better written as $hash{'AirIATA'} at .. +. { AirIATA => "unexpected" } { AirIATA => "useful" }

      You are right, but the cause of returning more than single scalar was a typo, so its fixed now.

      (BTW: Is the latter behavior really intended?) Do you mean $count++ ?

      If so, I need the previous value of @data, so if you can tell me a better solution I'll use it.

      Because if I try the code as

      if ( grep( /^$airline/, @data) ) { return $data[-2]; }
      it returns wrong value if there are additional notes in the definition file

        ... I need the previous value of @data ...

        If you mean you need to return the element in the  @data array immediately prior to the first element in the array that matches the string passed into the function, I think I would use something like this (Update: an empty string is returned if no match):

        >perl -wMstrict -le "print get_IATA('bar'); ;; sub get_IATA { my ($airline) = @_; ;; my @data = qw(fee fie foe foo barbell boff); ;; for my $i (1 .. $#data) { return $data[$i - 1] if $data[$i] =~ m{ \A \Q$airline\E }xms; } return ''; } " foo

        Update: Here's a variation that returns every element in the  @data array immediately prior to an element in the array that matches the string passed into the function. An empty list is returned if no match.

        >perl -wMstrict -le "printf qq{'$_' } for get_IATA('bar'); ;; sub get_IATA { my ($airline) = @_; ;; my @data = qw(fee fie yyy barfly foe fum zzz barbell foo); ;; return map { $data[$_] =~ m{ \A \Q$airline\E }xms ? $data[$_ - 1] : () } 1 .. $#data ; } " 'yyy' 'zzz'
Re: Warning is right or not ?
by frozenwithjoy (Priest) on Dec 20, 2012 at 19:01 UTC
    I was trying to assing a value to $hash{'AirIATA'} but was failing

    How was it failing?

Re: Warning is right or not ?
by Anonymous Monk on Dec 20, 2012 at 19:50 UTC

    Here is the result ( and then both running versions);

    Output;

    [root@testrhel AFAStest]# perl test2.pl #Right now $hash [root@testrhel AFAStest]# vi test2.pl #I change $hash to @hash [root@testrhel AFAStest]# perl test2.pl # Running as @hash Scalar value @hash{'AirIATA'} better written as $hash{'AirIATA'} at te +st2.pl line 12. P C

    pl files

    test1.pl

    #!/usr/bin/perl use warnings; use strict; use XML::Simple; my $programNAME= 'This line is required to work.'; sub get_IATA { my $file = shift; my $airline = shift; my @data; my $count=0; open(my $fh, '<', $file) or die "Can't read file '$file' [$!]\ +n"; while (my $line = <$fh>) { my @fields = split(/;/, $line); push @data, @fields; } foreach my $data ( @data) { if ($data eq $airline) { return $data[$count-1],"\n"; } $count++; } }

    test2.pl

    #!/usr/bin/perl use warnings; use strict; use XML::Simple; require 'test1.pl'; my $airlineLoc = "definitions/airline.csv"; my %hash; @hash{'AirIATA'}=get_IATA($airlineLoc,'PGT'); my $letter1 = substr($hash{'AirIATA'},0,1); my $letter2 = substr($hash{'AirIATA'},1,2); print $letter1,"\n",$letter2,"\n";
Re: Warning is right or not ?
by Anonymous Monk on Dec 20, 2012 at 19:59 UTC

    Thanks to RichardK's comment, I fixed my typo and now the code is running without any warning.

    return $data[$count-1],"\n";

    I removed comma and newline that I used in print to test the data and the warning disappeared too. Thanks for your all helps.