Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conversion of list context result to array in one line in perl?

Tags:

perl

I wrote this code in perl:

shift( @interfaces = qx'ifconfig -s' );

And got this error:

Type of arg 1 to shift must be array (not list assignment)

When I write it this way:

@interfaces = qx'ifconfig -s';
shift @interfaces;

It does what I want, which is to get the output of the ifconfig command as an array of lines and remove the first element in the array (which is a header, not an actual interface).

My personal preference is to write this as a one liner. It seems to me the parentheses in the first example should cause the assignment to be resolved fully, therefore allowing shift to see @interfaces as an array, but clearly perl thinks it's a "list assignment."

This is surely an easy question for the perl gurus but I've googled and googled and haven't found enlightenment.

If someone would please provide the specific semantics to accomplish what I want in one line I would appreciate it. If you would also please take the time to explain why my first version isn't working I would be eternally grateful (teach a man to fish and all that).

Thank you in advance for your help.

like image 854
par Avatar asked Oct 25 '11 20:10

par


2 Answers

As you saw, shift requires a literal array, not the result of an assignment. This is because when perl parses shift @interfaces it is actually rewriting it into something like &CORE::shift(\@interfaces) and you can not take a reference of an assignment and get an array ref.

You could break it into two lines as you have found, you could hide the assignment inside a bracketed dereference as mob shows, or you could simply throw away the first value:

(undef, @interfaces) = qx'ifconfig -s';

undef in an lvalue position is a placeholder for values that you don't need.

(parsing of shift has changed a bit in perl 5.14+, but the argument above still holds)


a few more ways that you probably shouldn't use, ordered only by increasing length :)

my @interfaces = sub {shift; @_}->(qx'ifconfig -s');

my @interfaces = sub {@_[1..$#_]}->(qx'ifconfig -s');

my @interfaces = map {@$_[1..$#$_]} [qx'ifconfig -s'];

my @interfaces = map {shift @$_; @$_} [qx'ifconfig -s'];

our @interfaces; shift @{*interfaces = [qx'ifconfig -s']};

my @interfaces = sub {*_ = [qx'ifconfig -s']; shift; @_}->();
like image 192
Eric Strom Avatar answered Oct 05 '22 09:10

Eric Strom


shift @{EXPR} is valid syntax, so

shift @{$interfaces = [qx'ifconfig -s']}

will give you an array reference that has the first element removed.

I discovered this from the diagnostics output about calling shift from list assignment:

$ perl -Mdiagnostics -e 'print shift(@a = (2,3,4))'
Type of arg 1 to shift must be array (not list assignment) at -e line 1, at end of line
 Execution of -e aborted due to compilation errors (#1)
    (F) This function requires the argument in that position to be of a
    certain type.  Arrays must be @NAME or @{EXPR}.  Hashes must be
    %NAME or %{EXPR}.  No implicit dereferencing is allowed--use the
    {EXPR} forms as an explicit dereference.  See perlref.

Perl enforces this behavior on any user-defined subroutine or builtin that is prototyped with \@ or \% characters. The prototype is a clue for the interpreter that Perl should treat an array or hash function argument as an array or hash type, and not try to unroll the list into several arguments.

One way to think about it (though I'm not sure if this is accurate for the builtins) is that Perl will read the array or hash variable from your list of arguments to a function call, but actually pass a reference to that variable to the prototyped function. So the interpreter needs to identify an array or hash in your argument list, and it needs to be able to get a reference to that array or hash. Perl doesn't or can't (hands waving here) do that with a list assignment expression -- note that the result of

\(@a = (1,2,3))

is a list of 3 references to scalars, not a reference to a list with 3 scalars.

You can see the prototypes (if any) for most of the Perl builtins with the prototype function:

$ perl -e 'print prototype("CORE::shift")'      ===>   \@
$ perl -e 'print prototype("CORE::each")'       ===>   \%
$ perl -e 'print prototype("CORE::push")'       ===>   \@@
like image 43
mob Avatar answered Oct 05 '22 07:10

mob