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

Is there a more elegant way of accomplishing the following snippet?
my @arr = ref( $x ) eq 'ARRAY' ? @$x : ( $x );

Replies are listed 'Best First'.
Re: elegant array assignment
by grinder (Bishop) on Feb 15, 2008 at 22:20 UTC

    There is elegant, and there is correct.

    If the arrayref has been blessed into another package you will run into trouble. In the general case, you should use the following:

    my @arr = UNIVERSAL::isa($x,'ARRAY') ? @$x : ($x);

    • another intruder with the mooring in the heart of the Perl

      It's not clear to me that the OP wants to treat a blessed array reference as an array reference and not as an object.

      There was a discussion of this kind of thing recently...um...here. The conclusion seemed to be that if you really want to assess arrayref-ness, wrap a dereference in an eval.

      if ( ! eval { @$x; 1 } ) { # not array ref (ish) } else { # can be used as array reference! }

      This will detect objects that have used overload to pretend to be an arrayref as well as any real arrayref that's been blessed. It also won't fall for any random reference that some joker did "bless $x, 'ARRAY'" on.

      Here again, however, I can't tell that this is what the OP wanted to find out.

Re: elegant array assignment
by pc88mxer (Vicar) on Feb 15, 2008 at 20:26 UTC
    I don't think so - at least not that I'm aware of. However, you might able to solve them problem elsewhere. For instance, if $x is the return value of a function, the function could check wantarray to see what the caller wants, i.e.

    sub func { ... return (wantarray ? ($x) : [$x]); } my @arr = func(); # returns an array my $arr = func(); # returns an array ref
Re: elegant array assignment
by dragonchild (Archbishop) on Feb 16, 2008 at 03:33 UTC
    The eval method (or the isArray() I've told tye to submit somewhere on CPAN) is the best way to find out if something responds to @{}.

    In general, I find that code like that is attempting to compensate for "clever" code elsewhere. And, by "clever", I mean "poorly written". I'm betting that whatever populates $x does something like:

    sub some_function { # Stuff here that populates @x return @x > 1 ? \@x : $x[0]; }
    That meme is one of the most horrendous ones to work with.
    1. some_function() will almost always return a single thing.
    2. We know this because some_function() expects that the assignment is always to a scalar, otherwise why normalize to a scalar?
    3. But, since some_function() builds an array, it might return more than one thing.
    4. So, the caller has to know this and normalize back to an array.

    So, given that the proper normalization (as performed by sane callers) is to an array, why is some_function() normalizing to a scalar? Simply put, there's no good reason to, other than FUD. Remove the FUD, trust the array, and stop the silliness.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: elegant array assignment
by kyle (Abbot) on Feb 15, 2008 at 21:04 UTC

    If I were writing this, I might move the parentheses:

    my @arr = ( ref $x eq 'ARRAY' ) ? @$x : ( $x );

    That's a pretty irrelevant style consideration, though. I've seen it suggested that ref $x should be compared to ref of a literal. In that case, it'd be like so:

    my @arr = ( ref $x eq ref [] ) ? @$x : ( $x );

    That's probably a good idea if you can't remember what ref sub {} is (for example), but in this case, I think I'd rather see 'ARRAY' there instead.

    (I played around with trying to write something based around eval { @$x } instead of testing ref directly, but it got pretty ugly before it worked: my @arr; eval { @arr = @$x; 1 } || ( @arr = ($x) ))

Re: elegant array assignment
by salva (Canon) on Feb 15, 2008 at 22:56 UTC
    I think that the only way to cope with all the special cases (blessed references, objetcs with @{} overloaded, etc.) is to just try to unreference the scalar and recover when it fails:
    my @arr = eval { @$x }; @arr = ($x) if $@;
Re: elegant array assignment
by snoopy (Curate) on Feb 16, 2008 at 00:08 UTC
    If an object has been blessed, Perls ref built in will return the class, not its type. Thus this code might not do what you expect for blessed array references.

    Scalar::Util's reftype function consistantly returns the type and might be a better choice here:

    use Scalar::Util qw/reftype/; my @arr = reftype( $x ) eq 'ARRAY' ? @$x : ( $x );