Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

split problem

by rmarkman (Initiate)
on Nov 29, 2016 at 22:13 UTC ( #1176863=perlquestion: print w/replies, xml ) Need Help??

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

Perl 5, version 22, subversion 0 (v5.22.0) built for MSWin32-x64-multi-thread gives this error

Modification of a read-only value attempted at test.plx line 6.

With this program:

use strict; my $value = "A\tB\tC\tD\tE\tF\t\t\t\t"; my @flds; @flds = split(/\t/,$value); $flds[16] = undef; $flds[8] = 0;

No error is generated if any of: eliminate the trailing tabs in $value; eliminate the call to split; call split with a -1 limit. I am mystified.

Replies are listed 'Best First'.
Re: split problem
by haukex (Bishop) 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

      a git bisect shows the first bad commit is 4ecee209 (the first release that contains this commit appears to be v5.21.5)
      The bisect is misleading; the issue is with split when it is optimised to split directly to an array (rather than just returning a list which later gets assigned to something). When exactly this optimisation is enabled has changed between releases, and that commit is one of those changes.

      The real issue was introduced in perl 5.19.4, and I've just fixed in it in blead with this commit:

      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.

        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

        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!

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.

      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...

      Breaks also in perl 5.25.5 (x86_64-linux-thread-multi, CentOS 6.8).

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).

    — Ken

      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.

        "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."

        You're correct. See my update. I've stricken the text concerning "past the end of the array".

        — Ken

      There must be something else the issue here, as if you remove all of the trailing tabs from the string, it works fine on all versions of perl I have installed currently.

      Works:

      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;

        Use Devel::Peek with working/nonworking to see the difference

        Sounds like a bug in perl

        #!/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__

        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.

        — Ken

      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

        "There is no such error in perl, arrays don't have end ..."

        Quite correct. I have stricken what I wrote from my post.

        — Ken

        Yep:

        perl -wMstrict -E 'my @a; $a[16]=0; say @a;'
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.
Re: split problem
by stevieb (Canon) on Nov 29, 2016 at 22:44 UTC

    I believe you have to initialize the array in order. Switching these two lines:

    $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 ];

      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

        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.
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.

      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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1176863]
Approved by sweetblood
Front-paged by LanX
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (2)
As of 2022-07-03 17:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?