Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Flattening out arguments to splice

by Oberon (Monk)
on May 29, 2018 at 22:12 UTC ( #1215410=perlquestion: print w/replies, xml ) Need Help??
Oberon has asked for the wisdom of the Perl Monks concerning the following question:

Oh enlightened ones, please share your wisdom on the following trivial topic:

use 5.14.0; use warnings; sub my_splice_like_func { my ($list, @args) = @_; my @copy = @$list; return splice @copy, @args; # this doesn't actually work : +-( } $, = ' '; # just keepin' it pretty my $list = [qw< one two three four >]; say my_splice_like_func($list, 2); # want "three four"; get "two +three four" say my_splice_like_func($list, 2, -1); # want "three"; get "three fou +r"

Now, I see what's actually happening: splice is forcing scalar context on @args, so all I'm really passing in is the number of args, which is obviously not what I want. So I see what's wrong, but I have no clue how to fix it. AFAIK there's no way to force list context (and Google seems to back me up on that). I suppose if this were Perl6 I could do something like *@args to flatten out the array, but in Perl5 I can't see any options other than something like:

if (@args == 0) { return @copy; } elsif (@args == 1) { return splice @copy, $args[0]; } elsif (@args == 2) { return splice @copy, $args[0], $args[1]; } else { die("too many args"); } }

... which works, but damn, that's clumsy. (Other than checking for too many args, which the original version should have had, granted.)

Does anyone see a way that I've misssed? As always, I humbly await illumination from the collective consciousness.

Replies are listed 'Best First'.
Re: Flattening out arguments to splice (updated)
by haukex (Abbot) on May 30, 2018 at 06:25 UTC

    What you're seeing is because splice has a prototype, and you can use prototype to see it (this is on Perl v5.26):

    $ perl -le 'print prototype("CORE::splice")//"undef"' \@;$$@

    (From Perl v5.14 thru v5.22, the prototype was +;$$@, because of the removed autoderef.)

    As of 5.26, you can use the & sub calling to avoid the prototype, as in &CORE::splice(@args);.

    But since that's not available in all versions of Perl, you may in fact need to resort to conditionals, even if it seems "clumsy" :-) For example, because some functions change their behavior based on how many arguments they get, I had to do exactly that in Tie::Handle::Base - see my binmode and open wrappers.

    However, the arguments to splice have clear defaults, so as tybalt89 and AnomalousMonk already showed you don't necessarily need to resort to a long if-elsif chain. This passes all the tests in t/op/splice.t*:

    sub mysplice (\@;$$@) { my ($arr,$off,$len,@list) = @_; splice @$arr, $off//0, $len // @$arr - ($off//0), @list; }

    And unlike CORE::splice, you can use a &mysplice call to ignore the prototype (before v5.26), or change the prototype and arguments however you like.

    * Full test code:

Re: Flattening out arguments to splice
by tybalt89 (Priest) on May 29, 2018 at 22:42 UTC
    #!/usr/bin/perl use 5.14.0; use strict; use warnings; sub my_splice_like_func { my ($list, $offset, $length) = @_; my @copy = @$list; return splice @copy, $offset // 0, $length // @copy - $offset; } $, = ' '; # just keepin' it pretty my $list = [qw< one two three four >]; say my_splice_like_func($list, 2); # want "three four"; get "two +three four" say my_splice_like_func($list, 2, -1); # want "three"; get "three fou +r"
      splice ARRAY,OFFSET,LENGTH,LIST

      You forgot the fourth argument: 'LIST'.

      my ($list, $offset, $length, @list) = @_;

        New splicer function operates on external referent (although I'm not sure what this gets you: it's almost exactly like splice) (update: needs Perl 5.10+ for // operator):

        c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "use 5.010; ;; sub my_splicer { my ($ar, $offset, $len, @list) = @_; $offset //= 0; $len //= @$ar - $offset; return splice @$ar, $offset, $len, @list; } ;; use constant TEST => qw(one two three four five six seven eight); ;; my $ar; my @spl; my @args; ;; $ar = [ TEST ]; @args = (); @spl = my_splicer($ar, @args); dd \@spl, $ar; ;; $ar = [ TEST ]; @args = (2); @spl = my_splicer($ar, @args); dd \@spl, $ar; ;; $ar = [ TEST ]; @args = (2, 3); @spl = my_splicer($ar, @args); dd \@spl, $ar; ;; $ar = [ TEST ]; @args = (2, 3, qw(FOO BAR)); @spl = my_splicer($ar, @args); dd \@spl, $ar; " ( ["one", "two", "three", "four", "five", "six", "seven", "eight"], [], ) ( ["three", "four", "five", "six", "seven", "eight"], ["one", "two"], ) ( ["three", "four", "five"], ["one", "two", "six", "seven", "eight"], ) ( ["three", "four", "five"], ["one", "two", "FOO", "BAR", "six", "seven", "eight"], )

        Update: Note this code will also work as expected with something like
           @args = ($ar, 2, 3, qw(FOO BAR));
           @spl = my_splicer(@args);


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

        I haven't tested this, but I believe that in tybalt89's code (and in your OP code), because the splice is operating on a copy of the  $list array reference argument, so a fourth argument  LIST would be assigned to the lexical array  @copy and then almost immediately lost along with that array.

        Do you mean that you want your function to operate on the external referent of the  $list array reference?


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

Re: Flattening out arguments to splice
by vr (Hermit) on May 30, 2018 at 15:52 UTC

    Splicing...

    >perl -wE "@a=qw/a b c/; say splice @a, 0, 1" a >perl -wE "@a=qw/a b c/; say splice @a, 0, 1e18" abc >perl -wE "@a=qw/a b c/; say splice @a, 0, 1e19" >perl -wE "@a=qw/a b c/; say splice @a, 0, 1e20" ab >perl -wE "@a=qw/a b c/; say splice @a, 0, 1" a >perl -wE "@a=qw/a b c/; say splice @a, 1e18, 1" splice() offset past end of array at -e line 1. >perl -wE "@a=qw/a b c/; say splice @a, 1e19, 1" Modification of non-creatable array value attempted, subscript -198128 +4352 at -e line 1. >perl -wE "@a=qw/a b c/; say splice @a, 1e20, 1" c

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1215410]
Approved by sundialsvc4
Front-paged by kcott
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (3)
As of 2018-07-21 21:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (450 votes). Check out past polls.

    Notices?