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

Hello, I'm embarrassed to ask such a trivial question, but I couldn't find anything searching Perlmonks or the Net. Consider a function that returns a real list (not a reference), and you are only interested in a particular element. Some standard modules behave like this, e.g. File::Spec->splitpath.

This does not work:

my $name = File::Spec->splitpath($path)[2];

This works, but it's ugly:

my $name; { my @dummy = File::Spec->splitpath($path); $name = $dummy[2]; }

Running through an intermediate anonymous array seems unnecessarily complex to me:

my $name = [ File::Spec->splitpath($path) ]->[2];

Many thanks for pointing me to a more elegant way!

Replies are listed 'Best First'.
Re: How to access directly an element of an array / list returned by a function?
by ikegami (Patriarch) on Aug 26, 2010 at 18:30 UTC
    Functions can't return arrays, they can only return a list of scalars. How does one extract a scalar from a list of scalars? Using a list slice.
    ( EXPR )[$index] ( EXPR )[@indexes]
    So,
    use File::Spec qw( ); my $file = ( File::Spec->splitpath($path) )[2];
    There's also
    use File::Basename qw( basename ); # Also core module my $file = basename($path);
    use File::Path qw( file ); # Addresses issues with File::Spec my $file = file($path)->basename();
Re: How to access directly an element of an array / list returned by a function?
by shawnhcorey (Friar) on Aug 26, 2010 at 19:43 UTC

    If you want more than one, use undef as a space holder:

     my ( undef, $file ) = File::Spec->splitpath($path);

      Picky, picky :)

      This is a good example of why undef must be used in certain cases though. However, since the OP was looking for the third element, it might be prudent to provide an accurate example:

      my ( undef, undef, $name ) = File::Spec->splitpath($path); print "$name\n";

      That code works identically to this:

      my $name = ( File::Spec->splitpath($path) )[2];

      In my code, we're dissecting the list on the right side and taking the piece that we want, whereas in the above code, (afaict) we're evaluating the right side of the operation completely, retrieving all the bits, and assigning them to placeholders on the left (undef) before they are actually used. (Correct me if I am wrong). In some situations, Shawn's implementation is required.

      fwiw, OP, if you ever are to write your own functions, I recommend using hashref-based named params, even when you think that your routine should never have to be expanded. It's one thing that I will never regret, as I *never* have to re-write API documentation anymore... I simply add to it where necessary.

      Consider this:

      sub family_member { my @params = @_; my $brother = $params[0]; my $sister = $params[1]; my $mother = $params[2]; my $father = $params[3]; print "$father\n"; } # to tell who my father is, I'd have to: family_member( undef, undef, undef, undef, 'Larry', ); # now, imagine if you could use a function like # this instead: family_member({ father => 'Larry', }); # ...and even bundle your brothers together: my @brothers = qw( Jeremy Josh ); family_member({ father => 'Larry', brothers => \@brothers, }); # now, in the above array-style example, what if you # wanted to add an 'aunt' between 'brother' and 'sister'? # Your API would be horribly broken, along with your # current documentation. You'd forever be having your # users moving around undefs to deal with your changes. # With named refs, the only time your users feel a bite # is if you change what happens to a pre-named param: sub family_members( my $params = shift; my $brother = $params->{ brother }; my $sister = $params->{ sister }; my $mother = $params->{ mother }; my $father = $params->{ father }; # add an aunt my $aunt = $params->{ aunt }; # how 'bout grandparents my $grandpa = $params->{ grandpa }; if ( $father ) { return $father; } else { return "I don't have one\n"; } ) # then, you can throw at it what you want, and # get back what you expect: print family_member({ father => 'Larry' }); # ...or assign it: my $father = family_member({ father => 'Larry' });

      Sorry for the offside...tis been a long day ;)

      Cheers,

      Steve

Re: How to access directly an element of an array / list returned by a function?
by TomDLux (Vicar) on Aug 26, 2010 at 17:29 UTC

    Turn it not into an anonymous array, but an anonymous list, and you have the standard Perl idiom:

    my $name = ( File::Spec->splitpath($path) )->[2];

    update: must have been the caffeine level dropping too low .. .no need for '->' in list.

    As Occam said: Entia non sunt multiplicanda praeter necessitatem.

      That doesn't work with the dereferencing operator (->), since the return of File::Spec->splitpath is not a reference. This works:

      my $name = ( File::Spec->splitpath($path) )[2];
      Steve
        Sorry, could have sworn I tried that first. Many thanks!!