Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Converting python list range expressions to perl

by kcott (Archbishop)
on Dec 04, 2022 at 03:00 UTC ( #11148543=note: print w/replies, xml ) Need Help??


in reply to Converting python list range expressions to perl

G'day ibm1620,

I think what you're looking for is splice.

Test script:

#!/usr/bin/env perl use v5.36; use constant { AREF => 0, PYTHON => 1, EXP => 2, }; use Test::More; my @test_array = 'a' .. 'g'; my @tests = ( [\@test_array, '[:3]', 'abc'], [\@test_array, '[:-3]', 'abcd'], [\@test_array, '[3:]', 'defg'], [\@test_array, '[-3:]', 'efg'], ); plan tests => 0+@tests; for my $test (@tests) { is get_array_slice_by_python_expr($test->@[AREF, PYTHON]), $test->[EXP], "Testing: $test->[PYTHON]"; } sub get_array_slice_by_python_expr ($aref, $python) { state $re = qr{^\[(|-?\d+):(|-?\d+)\]$}; my @temp_array = $aref->@*; my ($offset, $length) = $python =~ $re; $offset ||= 0; return length($length) ? join('', splice @temp_array, $offset, $length) : join('', splice @temp_array, $offset); }

Output:

1..4 ok 1 - Testing: [:3] ok 2 - Testing: [:-3] ok 3 - Testing: [3:] ok 4 - Testing: [-3:]

— Ken

Replies are listed 'Best First'.
Re^2: Converting python list range expressions to perl
by ibm1620 (Friar) on Dec 04, 2022 at 17:13 UTC
    Ken, thanks for your writeup, including forcing me to learn a bit about Test -- I needed that! :-)

    I'd rather stick with Perl's slice capability since it reads more cleanly (to me, anyway) and doesn't require copying and modifying the source array. Here's how I ended up implementing and testing pyrange():

    #!/usr/bin/env perl use v5.36; use constant { AREF => 0, PYTHON => 1, EXP => 2, }; use Test::More; my @test_array = 'a' .. 'g'; my @tests = ( [\@test_array, '[:3]', 'abc'], [\@test_array, '[:-3]', 'abcd'], [\@test_array, '[3:]', 'defg'], [\@test_array, '[-3:]', 'efg'], [\@test_array, '[-3:-1]', 'ef'], # testing double-ended ranges [\@test_array, '[-3:-3]', ''], [\@test_array, '[3:-1]', 'def'], ); plan tests => 0+@tests; for my $test (@tests) { is get_array_slice_by_python_expr($test->@[AREF, PYTHON]), $test->[EXP], "Testing: $test->[PYTHON]"; } sub get_array_slice_by_python_expr ($aref, $python) { ### $re modified to return undef instead of '' when endpoint omitt +ed state $re = qr{^ \[ ( -? \d+ )? : ( -? \d+ )? \] $}x; my ($start, $stop) = $python =~ $re; my $range_aref = pyrange($aref, $start, $stop); return join '', @$range_aref; } sub pyrange($aref, $start=undef, $stop=undef) { if (!defined $start) { $start = 0; } elsif ($start < 0) { $start = @$aref + $start; } if (!defined $stop) { $stop = $#$aref; } elsif ($stop >= 0) { $stop = $stop - 1; } else { $stop = @$aref + $stop - 1; } return [@$aref[$start .. $stop]]; }
    Output:
    $ ./test 1..7 ok 1 - Testing: [:3] ok 2 - Testing: [:-3] ok 3 - Testing: [3:] ok 4 - Testing: [-3:] ok 5 - Testing: [-3:-1] ok 6 - Testing: [-3:-3] ok 7 - Testing: [3:-1]
      "Ken, thanks for your writeup, including forcing me to learn a bit about Test -- I needed that! :-)"

      You're welcome. It's good to be able to start a script with use v5.36;.

      "I'd rather stick with Perl's slice capability since it reads more cleanly (to me, anyway) ..."

      What you choose is entirely up to you. This correlation stood out for me:

      PythonPerl
      [OFFSET:LENGTH]splice ARRAY, OFFSET, LENGTH
      [:LENGTH]splice ARRAY, 0, LENGTH
      [OFFSET:]splice ARRAY, OFFSET
      [:]splice ARRAY, 0
      "... and doesn't require copying and modifying the source array."

      The source array, @test_array, is not modified at all. The temporary copy, @temp_array, is modified in the last statement of (my) get_array_slice_by_python_expr() function; it's then immediately discarded as it goes out of scope.

      I added your three new tests, plus a fourth ([:]), to my original code:

      [\@test_array, '[-3:-1]', 'ef'], [\@test_array, '[-3:-3]', ''], [\@test_array, '[3:-1]', 'def'], [\@test_array, '[:]', 'abcdefg'],

      All pass:

      1..8 ok 1 - Testing: [:3] ok 2 - Testing: [:-3] ok 3 - Testing: [3:] ok 4 - Testing: [-3:] ok 5 - Testing: [-3:-1] ok 6 - Testing: [-3:-3] ok 7 - Testing: [3:-1] ok 8 - Testing: [:]

      — Ken

        I didn't mean that @test_array was modified; I meant that splice() modifies its argument array, requiring a temp copy be made. It just seemed wasteful to copy an array, and modify it, and then dispose of it, when Perl slice doesn't do so. Of course, that's pure guesswork on my part - and I haven't attempted to time it.

        In your representation of Python vs Perl slicing & splicing, I didn't understand your notation OFFSET:LENGTH for Python. Python slices are OFFSET:OFFSET. (Which is why Perl slices seemed a better fit to me.) Could you clarify?

Re^2: Converting python list range expressions to perl
by Anonymous Monk on Dec 04, 2022 at 14:20 UTC

    splice() alters the array, slice does not. I do not intend this as disagreement. I am just trying to add information to help a newbie choose between implementations.

      "splice() alters the array, slice does not."

      Which array do you believe is being altered?

      "I do not intend this as disagreement."

      Fair enough. With what are you not intending to disagree?

      "I am just trying to add information to help a newbie choose between implementations."

      Well, I suppose we might be able to help with that. What information are you trying to add?

      Also, as it may affect the wording, is this generic information for an arbitrary newbie, or did you have a specific newbie in mind?

      — Ken

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11148543]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (2)
As of 2023-02-06 05:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I prefer not to run the latest version of Perl because:







    Results (33 votes). Check out past polls.

    Notices?