Useless use of array element in void context

M

Marek

Hello all,


as a beginner, I am not sure, why I get this message "Useless use of
array element in void context". To illustrate I made a concentrated
excerpt of my larger perl project.

I need to compare two strings, which start with a different date, than
are coming numbers, which may or may not be different, and one line
finishes with a name. Whether the numbers are different in the string,
I made a split on white space, to compare them. Is there probably a
better way to compare two strings? In any case perl syntax check is
complaining about line 41 but is running fine. Really no idea why?

Thank you in advance


marek


#! /usr/bin/perl/

use warnings;
use strict;

my @out_lines = split(
/\n/,
'Mit, 16.05.2007 992.50 44284.40 2688 69.50
10110.90 Name1
Don, 17.05.2007 1148.90 44364.80 2707 69.50
10307.60 Name2
Fre, 18.05.2007 1408.50 44496.50 2714 69.50
10512.90 Name1
Sam, 19.05.2007 1736.60 44665.00 2738 69.50
10864.50 Name2
Son, 20.05.2007 2003.41 44769.00 2746 69.50
11037.70 Name1
Mon, 21.05.2007 2294.10 44907.40 2754 70.50
11256.30 Name1
Die, 22.05.2007 2683.10 45151.10 2762 70.50
11607.90 Name1
Mit, 23.05.2007 2994.30 45284.10 2765 70.50
11808.00 Name1
Don, 24.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name1
Fre, 25.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Sam, 26.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Son, 27.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Mon, 28.05.2007 3494.70 45498.30 2816 70.50
12358.30 Name2
Die, 29.05.2007 3896.20 45617.80 2821 70.50
12535.40 Name1
Mit, 30.05.2007 4226.30 45851.00 2829 70.50
12875.60 Name1
Don, 31.05.2007 4465.80 45920.90 2834 70.50
12974.20 Name1'
);

my $old_line;
foreach my $out_line (@out_lines) {
if ($old_line) {
$old_line =~ s/\s+[-a-z\d]+$//i;
my @old_line = split( /\s+/, $old_line );
my @out_line = split( /\s+/, $out_line );
if (
(
$old_line[2], $old_line[3], $old_line[4],
$old_line[5], $old_line[6]
) == (
$out_line[2], $out_line[3], $out_line[4],
$out_line[5], $out_line[6]
)
)
{
print "same numbers:\n";
print join("\t",@old_line) . "\n";
print join("\t",@out_line) . "\n";
print "TOTAL\t\t0.00\t0.00\t0.00\t0.00\t0.00\n";
}
else {
my $result2 = $out_line[2] - $old_line[2];
my $result3 = $out_line[3] - $old_line[3];
my $result4 = $out_line[4] - $old_line[4];
my $result5 = $out_line[5] - $old_line[5];
my $result6 = $out_line[6] - $old_line[6];
print "different numbers\n";
print join("\t",@old_line) . "\n";
print join("\t",@out_line) . "\n";
printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
$result2, $result3, $result4, $result5, $result6 );
}
$old_line = $out_line;
}
else {
$old_line = $out_line;
}
}
 
P

Peter J. Holzer

as a beginner, I am not sure, why I get this message "Useless use of
array element in void context". To illustrate I made a concentrated
excerpt of my larger perl project.

I need to compare two strings, which start with a different date, than
are coming numbers, which may or may not be different, and one line
finishes with a name. Whether the numbers are different in the string,
I made a split on white space, to compare them.

You cannot compare lists with "==". If you write something like
if (
(
$old_line[2], $old_line[3], $old_line[4],
$old_line[5], $old_line[6]
) == (
$out_line[2], $out_line[3], $out_line[4],
$out_line[5], $out_line[6]
)
)

the commas are interpreted as the comma operator, so it means something
like:

evaluate $old_line[2] and throw the value away, then
evaluate $old_line[3] and throw the value away, then
evaluate $old_line[4] and throw the value away, then
evaluate $old_line[5] and throw the value away, then
evaluate $old_line[6] and keep it,
evaluate $out_line[2] and throw the value away, then
evaluate $out_line[3] and throw the value away, then
evaluate $out_line[4] and throw the value away, then
evaluate $out_line[5] and throw the value away, then
evaluate $out_line[6] and keep it
if the two values we kept ($old_line[6] and $out_line[6] are
numerically equal ...

Or in other words, since there are no side effects, its exactly the same
as if you had written

if ($old_line[6] == $out_line[6])

Perl is warning you that this is probably not what you wanted.

What you probably wanted is to compare each field individually:

if ($old_line[2] == $out_line[2]
&& $old_line[3] == $out_line[3]
&& $old_line[4] == $out_line[4]
&& $old_line[5] == $out_line[5]
&& $old_line[6] == $out_line[6])

or in a loop:

my $same = 1;
$same = $same && $old_line[$_] == $out_line[$_] for (2 .. 6);
if ($same)

Is there probably a better way to compare two strings? In any case
perl syntax check is

This is not a syntax check. The syntax is fine. It is the semantic of
the code which is probably wrong.
complaining about line 41 but is running fine.

Define "running fine". Are the results correct?

hp
 
D

Dr.Ruud

Marek schreef:
if (
(
$old_line[2], $old_line[3], $old_line[4],
$old_line[5], $old_line[6]
) == (
$out_line[2], $out_line[3], $out_line[4],
$out_line[5], $out_line[6]
)
)

This doesn't do what you expect it to. Test with

perl -wle 'print( (1,2,0) == (5,4,0) ? "A" : "B")'


Maybe use:

if ( join ( "\n", @old_line[2..6] ) eq
join ( "\n", @out_line[2..6] )
) {
...
}

but consider List::Util.

my $result2 = $out_line[2] - $old_line[2];
my $result3 = $out_line[3] - $old_line[3];
my $result4 = $out_line[4] - $old_line[4];
my $result5 = $out_line[5] - $old_line[5];
my $result6 = $out_line[6] - $old_line[6];

Alternative-1:
$result[$_] = $out_line[$_] - $old_line[$_] for 2..6;

Alternative-2:
@result = map $out_line[$_] - $old_line[$_], 2..6;
printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
$result2, $result3, $result4, $result5, $result6 );

printf ( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
@result [2..6] );

or even:
my $s;
$s .= sprintf ( "\t%.2f", $out_line[$_] - $old_line[$_] )
for 2..6;
print "TOTAL\t", $s, "\n";

or evener:
my @s = '', map sprintf ( "%.2f", $out_line[$_] - $old_line[$_] ),
2..6;
print do { local $" = "\t"; "TOTAL$\"@s\n" };
 
P

Paul Lalli

as a beginner, I am not sure, why I get this message "Useless use
of array element in void context". To illustrate I made a
concentrated excerpt of my larger perl project.

Excellent! Well done! Seriously, that's one of the best things I've
read in this newsgroup in a long time.
#! /usr/bin/perl/

use warnings;
use strict;

More excellencence!!
if (
(
$old_line[2], $old_line[3], $old_line[4],
$old_line[5], $old_line[6]
) == (
$out_line[2], $out_line[3], $out_line[4],
$out_line[5], $out_line[6]
)
)
{

This is the problem. This block does not do what you think it does.
The == operator in Perl does not compare lists. It only compares
scalars. Therefore, both of the "lists" are being evaluated in scalar
context[1], and so it's only comparing the last element of each list.
That is, the above block is identical to:
if ($old_line[6] == $out_line[6])
None of the other elements are being looked at by the == operator.
That's why Perl is telling you all those array elements are being
uselessly used in a void context.

If you want to compare two arrays for equality of their elements, you
have a few possibilities. You could modify the example in the FAQ to
suit your needs. (From a command line, run `perldoc -q equal`), or you
could use either of the modules Array::Compare or List::Compare from
the CPAN (head to http://search.cpan.org and search for those
modules).

Note there are also less robust solutions involving comparing the two
arrays as strings - for example:
if ("@a1" eq "@a2")
or
use Data::Dumper;
if (Dumper(\@a1) eq Dumper(\@a2) )
But these are generally dangerous because they could return false
positives. For example, the first one would report that these two
arrays are indeed equal:
my @a1 = (1, 2, 3);
my @a2 = ("1 2", 3);
The Dumper one is more unlikely to give false positives, but still
possible.

My overall suggestion would be to modify the example given in the FAQ
to suit your needs.

Good luck!
Paul Lalli
 
P

Paul Lalli

The == operator in Perl does not compare lists. It only compares
scalars. Therefore, both of the "lists" are being evaluated in
scalar context[1]

Whoops, forgot my footnote. I meant to include the point that when
code looks as though a list is being used in a scalar context, what's
actually happening is that the comma operator is being used in scalar
context. The comma operator in scalar context does not create a list
- instead it evaluates its left argument, throws it away, and then
evaluates its right argument, and returns it.

my $x = (f1(), f2());
That statement calls the subroutine f1(), ignores its return value,
then calls f2() and puts f2's return value in $x.

So when you get a bunch of comma operators used in sequence like:
my $x = (1, 2, 3, 4, 5);
each comma operator evaluates its left argument, and then throws the
value away. The last element in the sequence (in this case, 5) is
returned from the expression. That's why
if ( (1, 2, 3) == (4, 5, 6) )
is only checking if 3 is equal to 6, and not any of the others.

Paul Lalli
 
P

Paul Lalli

Marek schreef:
if (
(
$old_line[2], $old_line[3], $old_line[4],
$old_line[5], $old_line[6]
) == (
$out_line[2], $out_line[3], $out_line[4],
$out_line[5], $out_line[6]
)
)

This doesn't do what you expect it to. Test with

perl -wle 'print( (1,2,0) == (5,4,0) ? "A" : "B")'

Maybe use:

if ( join ( "\n", @old_line[2..6] ) eq
join ( "\n", @out_line[2..6] )
) {
...
}

So long as you're aware of the possibility of false positives, like:
my @a1 = (1, 2, 3);
my @a2 = ("1\n2", 3);

If that's not possible for your data, then by all means, go for it.
but consider List::Util.

Can you explain which of List::Util's subroutines you believe will be
useful for comparing two lists for equality? Maybe reduce(), but if
so, I can't figure it out...


Thanks,
Paul Lalli
 
D

Dr.Ruud

Paul Lalli schreef:
Dr.Ruud:
if ( join ( "\n", @old_line[2..6] ) eq
join ( "\n", @out_line[2..6] ) ) {

So long as you're aware of the possibility of false positives, like:
my @a1 = (1, 2, 3);
my @a2 = ("1\n2", 3);
If that's not possible for your data, then by all means, go for it.

Because the data was split on whitespace, as it often is, the
"technique" is possible.
It might remind some people of hoops they needed to go through when
using sed.

I don't tolerate such code in production, for the reason that you
mentioned, and for the data conversion (list values concatenated in a
string).

Can you explain which of List::Util's subroutines you believe will be
useful for comparing two lists for equality? Maybe reduce(), but if
so, I can't figure it out...

I now checked List::MoreUtils as well, and even that doesn't have the
compare functionality that I expected. I must have seen it someweher
else, Bit::Vector maybe.
Though List::MoreUtils::pairwise can be used. But no list_is() or
list_eq() in either. Even a list_diff() would do.

Some other approaches:

if (5 == grep $_, map $old_line[$_] == $out_line[$_], 2..6) {
(but that needs two changes if the length of the range is changed)

my $same = 1; $same &&= $old_line[$_] == $out_line[$_] for 2..6;
(almost as hp showed)

unless (grep $_, map $old_line[$_] != $out_line[$_], 2..6) {
(ok, but not shortcutting)

my $diff; $diff ||= $old_line[$_] != $out_line[$_] and last for 2..6;
(shortcuts)



An (alpha) firstidx_diff for List::MoreUtils:
(beware, this one does numeric equation only)

#!/usr/bin/perl
use strict;
use warnings;

sub firstidx_diff (\@\@;@) {
$_[0]->[$_] != $_[1]->[$_] and return $_
for $_[2] ? @_[2..$#_] : $[..$#{$_[0]};
return '==';
}

my @x = (0..6, 70..99);
my @y = (0..6, 80..99);
print firstidx_diff( @x, @y ), "\n"; # 7

my @x1 = @x[2..6];
my @y1 = @y[2..6];
print firstidx_diff( @x1, @y1 ), "\n"; # ==
print firstidx_diff( @x, @y, 2..6 ), "\n"; # ==

my @z = (9, 9, 2..6, 90..99);
print firstidx_diff( @x, @z ), "\n"; # 0
print firstidx_diff( @x, @z, 2..10 ), "\n"; # 7
__END__


P.S. Perl6::Junction?
 
P

Peter J. Holzer

Paul Lalli schreef:

I now checked List::MoreUtils as well, and even that doesn't have the
compare functionality that I expected. I must have seen it someweher
else, Bit::Vector maybe.

Test::More has is_deeply(). Maybe you were thinking of that.

hp
 
D

Dr.Ruud

Mark Clements schreef:
Peter J. Holzer:

I've used Set::Array in the past for such comparisons, but YMMV :)

I assume that one ignores order, which wasn't OP's goal.
 
M

Marek

Thank you all, for this great help! Finally my question turned out to
be a FAQ, but I was looking for perldoc -q void and not, as Paul Lalli
suggested for perldoc -q equal ... And the example here (perlfaq4)
looks really complicate. I would prefere a monster like this, than the
example of the faq:

if ( $old_line[2] == $out_line[2]
&& $old_line[3] == $out_line[3]
&& $old_line[4] == $out_line[4]
&& $old_line[5] == $out_line[5]
&& $old_line[6] == $out_line[6])


Finally I understood the sense of the comma operator and why this code
is not working:

( $old_line[2], $old_line[3], $old_line[4], $old_line[5],
$old_line[6] )
==
( $out_line[2], $out_line[3], $out_line[4], $out_line[5],
$out_line[6] )

This night I dreamt the following "solution": make a slice of the
array-elements to compare, and compare as follows:

my @old_line_num_to_comp = @old_line[2..6];
my @out_line_num_to_comp = @out_line[2..6];
if (@old_line_num_to_comp == @out_line_num_to_comp) { ... }

But apparently here is the same problem, the comma operator is working
silently over the two arrays. This is tricky and I am wondering, why
Larry Wall or somebody else from you gurus is not inventing an
operator, just to compare two arrays, one element after the other :)

Thank you all for your genius suggestions! So many elegant
suggestions! It is always an aesthetic revelation for me, to see
elegant code from professionals like you. I am glad as a child under
the Christmas tree seeing code like:

my $result[$_] = $out_line[$_] - $old_line[$_] for 2..6;

Affijn: This is looking really genius! But this gives me a syntax
error! I probably misunderstood something?

I took your second suggestion:

my @result = map $out_line[$_] - $old_line[$_], 2..6;


but Perl is complaining about an uninitialized value in:

printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
@result[2..6] );


hmmmm, have to learn a lot again!


Thank you all again


marek
 
P

Peter J. Holzer

Thank you all, for this great help! Finally my question turned out to
be a FAQ, but I was looking for perldoc -q void and not, as Paul Lalli
suggested for perldoc -q equal ... And the example here (perlfaq4)
looks really complicate. I would prefere a monster like this, than the
example of the faq:

if ( $old_line[2] == $out_line[2]
&& $old_line[3] == $out_line[3]
&& $old_line[4] == $out_line[4]
&& $old_line[5] == $out_line[5]
&& $old_line[6] == $out_line[6])

The example in the FAQ works for arrays of arbitrary length.

[...]
This night I dreamt the following "solution": make a slice of the
array-elements to compare, and compare as follows:

my @old_line_num_to_comp = @old_line[2..6];
my @out_line_num_to_comp = @out_line[2..6];
if (@old_line_num_to_comp == @out_line_num_to_comp) { ... }

But apparently here is the same problem, the comma operator is working
silently over the two arrays.

I see no comma operatore here.

Instead there are two arrays, and if you use an array in a scalar
context, you get its length. So this code checks if
@old_line_num_to_comp and @out_line_num_to_comp have the same length.

hp
 
M

Marek

Thank you Peter!


correction to my previous posting:


printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
@result[2..6] );

=> @result[0..4]

of course! Little question to you more: you suggested:

my $same = 1;
$same = $same && $old_line[$_] == $out_line[$_] for (2 .. 6);
if ($same) { ... }

I changed to:

my $same = 1;
$same = $old_line[$_] == $out_line[$_] for (2 .. 6);
if ($same) { ... }

because I did not understand the use of the second "$same" after "="
Is it really needed?


Thank you again


marek
 
P

Paul Lalli

Thank you Peter!

correction to my previous posting:

printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
@result[2..6] );

=> @result[0..4]

of course! Little question to you more: you suggested:

my $same = 1;
$same = $same && $old_line[$_] == $out_line[$_] for (2 .. 6);
if ($same) { ... }

I changed to:

my $same = 1;
$same = $old_line[$_] == $out_line[$_] for (2 .. 6);
if ($same) { ... }

because I did not understand the use of the second "$same" after "="
Is it really needed?

Yes it is. Your example is only giving the result of the final
comparison. It is completely ignoring indices two through five.

In Peter's original, $same is being set to the result of the
conjunction of $same and the current comparison. As an example:
$same starts as 1
The arrays at index 2 are equal, so the comparison returns 1.
1 && 1 = 1, so $same stays one.
The arrays at index 3 are not equal, so the comparison returns 0.
1 && 0 = 0, so $same becomes 0.
The arrays at index 4 are equal, so the comparison returns 1.
0 && 1 = 0, so $same stays 0. In your example, however, $same becomes
1, completely ignoring the fact that at index 3, the arrays were not
equal.

Paul Lalli
 
M

Marek

Thank you Paul, your explanations are very clear. Great to have such
help in this group!



greetings


marek
 
D

Dr.Ruud

Marek schreef:

Heheh, "Affijn" is a Dutch variant of the French "enfin".
This is looking really genius! But this gives me a syntax
error! I probably misunderstood something?

What did you try, and what was the syntax error?
 
P

Paul Lalli

Marek schreef:

What did you try,

Er. Presumably the line of quote directly above the text that you
just quoted...
my $result[$_] = $out_line[$_] - $old_line[$_] for 2..6;
and what was the syntax error?

Looking at it, I'd say the syntax error would be to the effect that
you can't declare an array element with 'my'.

Paul Lalli
 
M

Marek

Marek schreef:
What did you try,

Er. Presumably the line of quote directly above the text that you
just quoted...
my $result[$_] = $out_line[$_] - $old_line[$_] for 2..6;
and what was the syntax error?

Looking at it, I'd say the syntax error would be to the effect that
you can't declare an array element with 'my'.

Paul Lalli



Probably I am doing a terrible beginner's error, but I get this
message:


syntax error at excel_uebung.pl line .., near "$result["


Here the script with the line my $result[$_] = $out_line[$_] -
$old_line[$_] for 2..6;:


#! /usr/bin/perl/

use warnings;
use strict;

my @out_lines = split(
/\n/,
'Mit, 16.05.2007 992.50 44284.40 2688 69.50
10110.90 Name1
Don, 17.05.2007 1148.90 44364.80 2707 69.50
10307.60 Name2
Fre, 18.05.2007 1408.50 44496.50 2714 69.50
10512.90 Name1
Sam, 19.05.2007 1736.60 44665.00 2738 69.50
10864.50 Name2
Son, 20.05.2007 2003.41 44769.00 2746 69.50
11037.70 Name1
Mon, 21.05.2007 2294.10 44907.40 2754 70.50
11256.30 Name1
Die, 22.05.2007 2683.10 45151.10 2762 70.50
11607.90 Name1
Mit, 23.05.2007 2994.30 45284.10 2765 70.50
11808.00 Name1
Don, 24.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name1
Fre, 25.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Sam, 26.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Son, 27.05.2007 3092.10 45336.50 2777 70.50
11953.40 Name2
Mon, 28.05.2007 3494.70 45498.30 2816 70.50
12358.30 Name2
Die, 29.05.2007 3896.20 45617.80 2821 70.50
12535.40 Name1
Mit, 30.05.2007 4226.30 45851.00 2829 70.50
12875.60 Name1
Don, 31.05.2007 4465.80 45920.90 2834 70.50
12974.20 Name1'
);

my $old_line;
foreach my $out_line (@out_lines) {
if ($old_line) {
$old_line =~ s/\s+[-a-z\d]+$//i;
my @old_line = split( /\s+/, $old_line );
my @out_line = split( /\s+/, $out_line );
my $same = 1;
$same = $same && $old_line[$_] == $out_line[$_] for ( 2 ..
6 );
if ($same) {
print "same numbers:\n";
print join( "\t", @old_line ) . "\n";
print join( "\t", @out_line ) . "\n";
print "TOTAL\t\t0.00\t0.00\t0.00\t0.00\t0.00\n";
}
else {
print "different numbers\n";

# my @result = map $out_line[$_] -
$old_line[$_], 2 .. 6;

my $result[$_] = $out_line[$_] - $old_line[$_] for 2 .. 6;
print join( "\t", @old_line ) . "\n";
print join( "\t", @out_line ) . "\n";
printf( "TOTAL\t\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
@result[ 2 .. 6 ] );
}
$old_line = $out_line;
}
else {
$old_line = $out_line;
}
}
 
J

Joe Smith

Marek said:
Marek schreef:
This is looking really genius! But this gives me a syntax
error! I probably misunderstood something?
What did you try,
Er. Presumably the line of quote directly above the text that you
just quoted...
my $result[$_] = $out_line[$_] - $old_line[$_] for 2..6;
and what was the syntax error?
Looking at it, I'd say the syntax error would be to the effect that
you can't declare an array element with 'my'.

Paul Lalli



Probably I am doing a terrible beginner's error, but I get this
message:


syntax error at excel_uebung.pl line .., near "$result["


Here the script with the line my $result[$_] = $out_line[$_] -
$old_line[$_] for 2..6;:

How many times do we have to repeat it?
"That is a syntax error; you CANNOT declare an array element with 'my'".
Do you know what an "array element" is?????

You can declare an entire array with 'my', you cannot declare
individual elements with 'my'.

1) Put "my @result;" on a line by itself.
2) Use "$result[$_]" without 'my'.

-Joe
 
M

Marek

Sorry, Joe, I have had the feeling, when posting the last message,
that there is something terrible wrong!

I am blushing!



Thanx to all, having so much patience with me

marek
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top