Re: split problem
by haukex (Archbishop) on Nov 30, 2016 at 05:51 UTC
|
Hi rmarkman,
Some more info for your bug report: I reduced the test case down to perl -e '$_="Boo";@x=split/o/;$#x=1;$x[1]=0', and a git bisect shows the first bad commit is 4ecee209 (the first release that contains this commit appears to be v5.21.5).
Regards, -- Hauke D
| [reply] [d/l] |
|
commit 71ca73e5fa9639ac33e9f2e74cd0c32288a5040d
Author: David Mitchell <davem@iabyn.com>
AuthorDate: Wed Nov 30 08:59:01 2016 +0000
Commit: David Mitchell <davem@iabyn.com>
CommitDate: Wed Nov 30 09:11:25 2016 +0000
split was leaving PL_sv_undef in unused ary slots
This:
@a = split(/-/,"-");
$a[1] = undef;
$a[0] = 0;
was giving
Modification of a read-only value attempted at foo line 3.
This is because:
1) unused slots in AvARRAY between AvFILL and AvMAX should always
+be
null; av_clear(), av_extend() etc do this; while av_store(), if st
+oring
to a slot N somewhere between AvFILL and AvMAX, doesn't bother to
+clear
between (AvFILL+1)..(N-1) on the assumption that everyone else pla
+ys
nicely.
2) pp_split() when splitting directly to an array, sometimes over-
+splits
and has to null out the excess elements;
3) Since perl 5.19.4, unused AV slots are now marked with NULL rat
+her than
&PL_sv_undef;
4) pp_split was still using &PL_sv_undef;
The fault was with (4), and is easily fixed.
Dave. | [reply] [d/l] |
|
Hi Dave,
The real issue was introduced in perl 5.19.4, and I've just fixed in it in blead with this commit
Thank you very much!
Regards, -- Hauke D
| [reply] |
|
I just came back to this issue this morning to follow up to see if a bug ticket was opened, and if not, was going to open one.
Thanks Dave for getting this fix in!
| [reply] |
Re: split problem
by kennethk (Abbot) on Nov 29, 2016 at 22:43 UTC
|
I can confirm the behavior for ActiveState perl 5.22.1 and ActiveState perl 5.24.0, and verify I don't see the problem with ActiveState perl 5.20.1. Can anyone check in Strawberry or under *nix?
#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.
| [reply] |
|
Breaks on 5.24 on Linux Mint (ie. I see the same behaviour as the OP).
update: It works on 5.10.1 and 5.20.3, and breaks on 5.22.2 and 5.24.0 (Linux Mint still).
update 2: It works fine on all versions if the trailing tabs are removed from the string being split...
| [reply] |
|
| [reply] |
Re: split problem
by kcott (Archbishop) on Nov 29, 2016 at 23:12 UTC
|
G'day rmarkman,
Welcome to the Monastery.
Update (strike):
I looked up the error message and made an incorrect inference.
I suspect I read "off the end of the string" as "off the end of the array".
Anyway, regardless of the reason, what I wrote is wrong and is now stricken.
You're getting the error because you're attempting to assign a value to an element past the end of the array.
If you search for "Modification of a read-only value attempted"
in "perldiag -- Perl diagnostic messages"
you'll get a lot of information about this.
However, the opening sentence sums up yor problem:
"You tried, directly or indirectly, to change the value of a constant."
Your split statement, as written,
only produces 6 elements: ('A' .. 'F').
You can print $#flds or scalar(@flds) to confirm this.
When you add a LIMIT of -1, you get 10 elements.
This behaviour is documented in split:
there's examples roughly in the middle of the page.
With the code as posted, both $flds[16] and $flds[8] are undefined.
Attempting to assign undef doesn't cause an issue:
$ perl -we 'undef=undef'
$
Attempting to assign 0 generates the error you're getting:
$ perl -we 'undef=0'
Modification of a read-only value attempted at -e line 1.
$
If you use split like this:
@flds = split(/(\t)/, $value);
You'll get 18 elements.
That's also documented (at the end of the page).
| [reply] [d/l] [select] |
|
Except that if you assign any value past the end of an array, Perl is supposed to automatically lengthen the array to accommodate the value.
use strict;
use 5.10.0;
my @flds;
$flds[16] = undef;
say 0+ @flds;
I can see no rational reason to treat an array holding the results of a split differently other arrays.
#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.
| [reply] [d/l] |
|
| [reply] |
|
|
use warnings;
use strict;
my $value = "A\tB\tC\tD\tE\tF";
my @flds = split(/\t/, $value);
$flds[16] = undef;
$flds[8] = 0;
print scalar @flds;
__END__
17
Broken:
use warnings;
use strict;
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t\t\t";
my @flds = split(/\t/, $value);
$flds[16] = undef;
$flds[8] = 0;
| [reply] [d/l] [select] |
|
#!/usr/bin/perl --
use strict;
use warnings;
use Devel::Peek qw/ Dump /;
eval {
my $value = "A\tB\tC\tD\tE\tF";
my @flds = split(/\t/, $value);
$flds[16] = undef;
$flds[8] = 0;
Dump( \@flds );
print scalar @flds, "\n", '#'x5,"\n";
1;
} or warn $@;
eval {
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t\t\t";
my @flds = split(/\t/, $value);
Dump( \@flds );
$flds[16] = undef;
$flds[8] = 0;
print scalar @flds, "\n", '#'x5,"\n";
1;
} or warn $@;
__END__
| [reply] [d/l] |
|
I've updated my post.
The part about "past the end of the array" was completely wrong and is now stricken.
I didn't originally try without the trailing tabs.
I have now done so and it works fine for me too.
| [reply] |
|
You're getting the error because you're attempting to assign a value to an element past the end of the array. There is no such error in perl, arrays don't have end, this is a bug
| [reply] |
|
| [reply] |
|
perl -wMstrict -E 'my @a; $a[16]=0; say @a;'
| [reply] [d/l] |
Re: split problem
by Marshall (Canon) on Nov 29, 2016 at 22:43 UTC
|
Please explain further what you intend to accomplish as an end result. Your code is actually fine albeit strange.
use strict;
use warnings;
use Data::Dumper;
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t";
my @flds;
@flds = split(/\t/,$value);
$flds[16] = undef;
$flds[8] = 0;
print Dumper \@flds;
__END__
$VAR1 = [
'A',
'B',
'C',
'D',
'E',
'F',
undef,
undef,
0,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef
];
PS: I see other replies, I am using Perl 5.20 Could be a bug. | [reply] [d/l] |
Re: split problem
by stevieb (Canon) on Nov 29, 2016 at 22:44 UTC
|
$flds[16] = undef;
$flds[8] = 0;
...around like this:
$flds[8] = 0;
$flds[16] = undef;
...will work, but then you won't be able to populate the other ones. Perhaps a full blown initialization of any array elements that aren't populated by split first?:
use warnings;
use strict;
use Data::Dumper;
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t";
my @flds = split(/\t/,$value);
for (0..16){
$flds[$_] = undef if ! defined $flds[$_];
}
$flds[8] = 0;
print Dumper \@flds;
Output:
$VAR1 = [
'A',
'B',
'C',
'D',
'E',
'F',
undef,
undef,
0,
undef,
undef,
undef,
undef,
undef,
undef,
undef,
undef
];
| [reply] [d/l] [select] |
|
I believe you have to initialize the array in order. Switching these two lines: :) nope, this is perl, the order doesn't matter
Looks like some recent optimization has backfired, again
| [reply] |
|
That's strange. Using OP's code on 5.24.0, it breaks. If I swap those two lines around, it works fine. I've never had an issue with assigning values to an array out of order before, but in this case, it definitely matters :)
Works:
use warnings;
use strict;
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t\t\t";
my @flds = split(/\t/, $value);
$flds[8] = 0;
$flds[16] = undef;
print scalar @flds;
__END__
17
Borked:
use warnings;
use strict;
my $value = "A\tB\tC\tD\tE\tF\t\t\t\t\t\t";
my @flds = split(/\t/, $value);
$flds[16] = undef;
$flds[8] = 0;
print scalar @flds;
__END__
Modification of a read-only value attempted at split.pl line 7.
| [reply] [d/l] [select] |
|
Re: split problem
by rmarkman (Initiate) on Nov 29, 2016 at 23:19 UTC
|
The code was distilled from a much larger program, just to demonstrate splits behavior that I believe is a bug.
| [reply] |
|
It looks like you are right. See perlbug.
#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.
| [reply] |