Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

curious behavior: why does it do this?

by perl-diddler (Chaplain)
on Nov 27, 2018 at 23:09 UTC ( #1226434=perlquestion: print w/replies, xml ) Need Help??

perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I playing with the syntax of ranges, like my $h={args=>[0..5]}, my $h={args=>[0..-1]} and a few others, I came up with a range that surprised me: my $h={args=>["0".."-1]}

Using P::P to print the value, as it's made for cases like this (1st, the more normal cases, then the odd one:

> tperl #alias tperl='perl -we'\''use strict; use P +;' my $h={args=>[0..5]}; P "h=%s", $h; ' h={args=>[0, 1, 2, 3, 4, 5]} > tperl my $h={args=>[0..-1]}; P "h=%s", $h; ' h={args=>[]} ### then I tried: > tperl my $h={args=>["0" .. "-1"]}; P "h=%s", $h; ' h={args=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 +, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, +52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 6 +9, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86 +, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]} # (and) > tperl my $h={args=>[q(00) .. q(-1)]}; P "h=%s", $h;' h={args=>[00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, +15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3 +2, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 +, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, +84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]}

So a quoted 0 started with 0, and a double zero ended up with a leading zero for 0-9, but the odd part was the "-1" turning into "99".

Huh? That's weird. Why does it do that?

Thanks...

Replies are listed 'Best First'.
Re: curious behavior: why does it do this?
by haukex (Chancellor) on Nov 27, 2018 at 23:33 UTC

    See the docs:

    The range operator (in list context) makes use of the magical auto-increment algorithm if the operands are strings. ... If the final value specified is not in the sequence that the magical increment would produce, the sequence goes until the next value would be longer than the final value specified.

    The magic string increment would normally produce the sequence "0", "1", "2", ... "98", "99", "100", "101", ..., and "-1" is not in that sequence.


      Hmmm....When I tried 000 as a starting value, I got an empty array.
      Someone else started w/"1", and also saw no output.
      Weird.
Re: curious behavior: why does it do this?
by davido (Cardinal) on Nov 27, 2018 at 23:43 UTC

    The hashref stuff is not actually even part of the necessary build-up to demonstrate the behavior:

    perl -E 'say for "0".."-1"'

    This produces 0, 1, 2, and so on up to 99. But the '-' is not part of the necessaries either:

    perl -E 'say for "0".."^1"'

    Same outcome. And in fact, the '1' isn't needed:

    perl -E 'say for "0".. "##"'

    Same behavior. And it turns out that the number of digits on the right side is the only thing that dictates how many numeric digits we count up to:

    perl -E 'say for "0" .. "abc"'

    ...produces 0 through 999 as output.

    Why does this all work so well? Here's the explanation from perlop:

    If the final value specified is not in the sequence that the magical increment would produce, the sequence goes until the next value would be longer than the final value specified.

    However, I feel it's stretching the documentation for convenience sake to say that the fact that the strings are never upgraded to numbers (even the one on the left) but are iterated upon within the confines of numeric digits until they fill the number of digits represented on the right, is actually well documented. ;)


    Dave

      Why does this all work so well?

      Thanks for the excellent clarification and elaboration.

      However, given that print for "0" .. "-1"; outputs the numbers 0 through to 99, I'm puzzled (in an idle sort of way) as to why print for "1" .. "-1"; doesn't output the numbers 1 through to 99.
      In fact, the latter produces no output whatsoever and I find myself thinking that perhaps it doesn't really work all that well at all.

      (I'm running perl-5.28.0 on Windows.)

      Cheers,
      Rob
        I'm puzzled (in an idle sort of way) as to why print for "1" .. "-1"; doesn't output the numbers 1 through to 99.

        That is indeed quite strange, and I can't make sense of it at the moment... seems like a bug to me.

        $ perl -wMstrict -MData::Dump -e' dd "0".."-1" ' (0 .. 99) $ perl -wMstrict -MData::Dump -e' dd "1".."-1" ' () $ perl -wMstrict -MData::Dump -e' dd "01".."-1" ' ("01", "02", "03", "04", "05", "06", "07", "08", "09", 10 .. 99) $ perl -wMstrict -MData::Dump -e' dd "90".."-1" ' () $ perl -wMstrict -MData::Dump -e' dd "1".."xx" ' (1 .. 99) $ perl -wMstrict -MData::Dump -e' dd "11".."xx" ' (11 .. 99) $ perl -wMstrict -MData::Dump -e' dd "90".."xx" ' (90 .. 99) $ perl -wMstrict -MData::Dump -e' dd "-1".."xx" ' -1 $ perl -wMstrict -MData::Dump -e' dd "0".." -1 " ' (0 .. 9999) $ perl -wMstrict -MData::Dump -e' dd " 0 ".." -1 " ' () $ perl -wMstrict -MData::Dump -e' dd " 11 ".." -1 " ' () $ perl -wMstrict -MData::Dump -e' dd "0.0".."-1.0" ' "0.0" $ perl -wMstrict -MData::Dump -e' dd " 0.0 ".." -1.0 " ' () $ perl -wMstrict -MData::Dump -e' dd "0.0".." 1.0 " ' "0.0" $ perl -wMstrict -MData::Dump -e' dd " 0.0 ".."1.0" ' (0, 1)

        Maybe there's some kind of heuristic going wrong... for example, maybe the intention is that if both sides looks_like_number, then treat them as numbers instead of strings - but several of the cases above don't work like that. I dunno at the moment, this may need some digging in the sources. (I ran a bisect earlier, and it seems that at least the "1".."-1" case has always returned the empty list.)

        Update: Reported as Bug #133695

        ... why print for "1" .. "-1"; doesn't output the numbers 1 through to 99. ... (I'm running perl-5.28.0 on Windows.)

        And likewise 5.8 on Win7. And likewise puzzled.

        c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "print qq{perl version: $]}; ;; my $h = { args => [ '1' .. '-1' ] }; dd $h; ;; print for '1' .. '-1'; " perl version: 5.008009 { args => [] }


        Give a man a fish:  <%-{-{-{-<

        *curiouser* and *curiouser*.... the 1st explanation sounded logical, until I read your post...now, not as sure.
      The hash was from the program I was testing, since the hash would be a blessed object holding, among other things, a 'range' for the acceptable number of parameters that could be entered.

      I.e. If I have a switch or option parser, say for the command line, and you want to have one option that can take 0 or more parameters, what would be a good syntax for specifying that?

      I was looking at the syntax of the range operator as a possible model for such an option, which is why I think I said I was playing around with the range syntax -- trying to think what my needs were, and how to make such a specification intuitive and useful.

      While the hash might not be directly useful, having the range specified in a dynamic array would be, as collected input could be stored there. Simply listing options for range syntax doesn't give me a feel for how such would be included in a data structure.

      If you put your examples into an array with other specifications named in a hash. Your examples might look more like what I was working with if you had them in a {}, since instead of (or in addition to), I have thought about specifying a min/max (but that didn't feel right) as well as one or more functions for validation and/or processing of the options on-input and perhaps for specifying a stopping condition.

      I mentioned that I used an alias -- because, at least while playing, I often start with 1-liners, or "few-liners" to get an idea on how something might look or might work.

      I hit the "-1" example when thinking about a way to specify "no maximum" # of params. Even specifying 0..5... looking at it I think 0 to 5 params (i.e. having no args would be ok too), but that's really not what that notation means...(*sigh*)

      Note: I'm not asking folks to solve that problem, just giving the context of how I ran into this and the reason for the form of the problem.

Re: curious behavior: why does it do this?
by haukex (Chancellor) on Nov 30, 2018 at 14:20 UTC

    Based on the discussion Re^2: curious behavior: why does it do this?, I reported bug #133695, where I just posted some patches. One of them explains the current* behavior of the range operator when its operands are strings:

    The range operator in list context can make use of the magical auto-increment algorithm if both operands are strings, subject to the following rules:

    • With one exception (below), if both strings look like numbers to Perl, the magic increment will not be applied, and the strings will be treated as numbers (more specifically, integers) instead.

      For example, "-2".."2" is the same as -2..2, "1".."-1" is the same as 1..-1 (producing the empty list), and "2.18".."3.14" produces 2, 3.

    • The exception to the above rule is when the left-hand string begins with 0, including the string "0" itself. In this case, the magic increment will be applied, even though strings like "01" would normally look like a number to Perl.

      For example, "01".."04" produces "01", "02", "03", "04", and "0".."-1" produces "0" through "99" - this may seem surprising, but see the following rules for why it works this way. To get dates with leading zeros, you can say:

          @z2 = ("01" .. "31");
          print $z2[$mday];

      If you want to force strings to be interpreted as numbers, you could say

          @numbers = ( 0+$first .. 0+$last );
    • If the initial value specified isn't part of a magical increment sequence (that is, a non-empty string matching /^[a-zA-Z]*[0-9]*\z/), only the initial value will be returned.

      For example, "ax".."az" produces "ax", "ay", "az", but "*x".."az" produces only "*x".

    • For other initial values that are strings that do follow the rules of the magical increment, the corresponding sequence will be returned.

      For example, you can say

          @alphabet = ("A" .. "Z");

      to get all normal letters of the English alphabet, or

          $hexdigit = (0 .. 9, "a" .. "f")[$num & 15];

      to get a hexadecimal digit.

    • If the final value specified is not in the sequence that the magical increment would produce, the sequence goes until the next value would be longer than the final value specified. If the length of the final string is shorter than the first, the empty list is returned.

      For example, "a".."--" is the same as "a".."zz", "0".."xx" produces "0" through "99", and "aaa".."--" returns the empty list.

    * However, in case one of my patches gets accepted, it would change "0".."-1" from returning "0".."99" to returning the empty list, which would be consistent with 0..-1. In any case, the patches include the corresponding documentation, so hopefully perlop will get an update.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1226434]
Approved by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2019-08-18 16:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    If you were the first to set foot on the Moon, what would be your epigram?






    Results (135 votes). Check out past polls.

    Notices?