
Ternary Trouble in Perl - ethanJ
https://hackernoon.com/ternary-trouble-in-perl-fc138b345843
======
simcop2387
His problem has nothing to do with commas at all. my (@array1, @array2) = ...;
will always assign all of the right side to the first array. This is just the
way perl works with assigning lists to arrays. There's a few other ways you
can do this instead.

    
    
        # with array references
        my ($arr_ref1, $arr_ref2) = 
            (index($str2, '-') != -1) ? 
              ([split /-/,$str2], [split /;/, $str4]) :
              ([split /:/,$str2], [split /;/, $str4]);
    

The above will assign two array references to each scalar variable.

The other option is similar but only works in newer perls with a feature
called "refaliasing" and "declared_refs", which are still considered
experimental, but which are expected to be marked as stable in 5.30 (If my
memory serves)

    
    
        use feature qw/refaliasing declared_refs/;
        my (\@arr1, \@arr2) = 
            (index($str2, '-') != -1) ? 
               ([split /-/,$str2], [split /;/, $str4]) : 
               ([split /:/,$str2], [split /;/, $str4]);
    

This takes the array references and assigns binds them to the arrays similar
to what the author originally intended.

This is a result of the same problem that perl's default argument passing has
with list flattening.

    
    
        sub foo {
          my (@a, @b) = @_; # load the arguments that were passed into the subroutine
    
          ...
        }
    
        foo(@data, @names);
    

That will result in both sets of values to be assigned to @a and nothing in
@b, because @a ends up consuming everything.

Ref-aliasing with perl's prototypes will allow you to make this work like you
might expect if you aren't experienced with perl's quirks but generally when
you see this pattern you should be passing a reference to begin with and not
trying to use the "magic" to hide that from the callers.

You can play around with these examples in an pastebin that allows running
live perl code (server side), here
[https://perl.bot/p/gblj94](https://perl.bot/p/gblj94)

EDIT: fix the code slightly, and provide interactive pastebin.

~~~
simcop2387
A slightly bizarre version of this that nobody should ever use. If you see
someone doing this, tell them they are a bad programmer and shouldn't be
putting code into production. It's also unlikely that this would actually be
useful for the real code in the authors situation because it ends up mostly
limited, but the technique can be useful even if this example shouldn't be
done this way.

    
    
        # declare our arrays
        my @arr1;
        my @arr2;
        
        # alias specific elements to the arrays
        my \$first = \$arr1[0];
        my \$second = \$arr1[1];
        my \$third = \$arr2[0];
        my \$fourth = \$arr2[1];
         
        # assign to those aliases.  note the , 2) added to each split.  This forces each split to only ever produce two values.
        ($first, $second, $third, $fourth) = 
            (index($str2, '-') != -1) ? 
               (split(/-/,$str2,2), split(/;/, $str4, 2)) : 
               (split(/:/,$str2,2), split(/;/, $str4, 2));
    

This is bad code simply because you're unrolling the arrays and not using
things the way you should in the language. But this does demonstrate how
refaliasing can be useful for doing some other weirdly specific things. This
also works for things inside hashes and other perl data structures.

------
jonathonf
It appears the author is trying to assign two variables to a single result,
essentially,

    
    
        my (@a, @b) = ["resulting", "string"]
    

Hence @b has no assigned value.

The Perl Beginners list is also a good place for this sort of discussion,
[https://lists.perl.org/list/beginners.html](https://lists.perl.org/list/beginners.html)

------
Oatseller
There are two issues here.

First, in list assignment the first array or hash will soak up all remaining
values not yet assigned.

    
    
        my ($first, $next, @rest, @empty) = (1, 2, 3, 4, 5);
    
      

$first is now 1

$next is 2

@rest is (3,4,5)

@empty is undefined;

see: [http://perldoc.perl.org/perldata.html#List-value-
constructor...](http://perldoc.perl.org/perldata.html#List-value-constructors)

The second issue is that the split() function takes an optional "Limit"
argument (split /PATTERN/,EXPR,LIMIT)

This causes the example code (split /-/,$str2, split /;/, $str4) to be
evaluated as split /-/,$str2,split(/;/, $str4) which evaluates as split
/-/,$str2,2

Here, split /;/,$str4 evaluates to 2 because, in scalar context, split returns
the length of the resulting list - since there are two elements after
splitting $str4 it returns 2.

In this case, the result of splitting $str2 results in two elements so the
limit doesn't matter.

If $str2 were 'This-string-has-many-dashes', the result of (split /-/,$str2,
split /;/, $str4) would be:

    
    
        array 3[0] is: This
        array 3[1] is: string-has-many-dashes
        array 4[0] is: 
        array 4[1] is:
    
      

see:
[http://perldoc.perl.org/functions/split.html](http://perldoc.perl.org/functions/split.html)

------
perl4ever
I want to preface this with _please_ don't write Perl like this, and don't
blame it on the language.

But anyway, there are multiple issues. One is that, as other comments point
out, all the results are going to the leftmost variable.

But the other is indeed fixable with parentheses. The code provided outputs
two items when it presumably should give four.

    
    
        my (@arr3, @arr4);
    
        (@arr3[0..1],@arr4[0..1]) = (index($str2, '-') != -1) ? (split (/-/,$str2), split (/;/, $str4)) : (split (/:/,$str2), split (/;/, $str4));
    
        array 3[0] is: This string has two sentences
        array 3[1] is:   A dash separates them
        array 4[0] is: This string has different punctuation
        array 4[1] is:  a semicolon

~~~
simcop2387
Yea, using slices can accomplish this too. But if you're going to be using
slices or a fixed number of elements on the left hand side you should also be
using the third argument to split($delim, $input, $count) to tell split to
only produce so many outputs. If the input string would split into more than
two pieces in your original code you would end up overflowing into @arr4 and
losing results from the second split.

~~~
perl4ever
Making it robust or sensible presumes there's some sort of coherent intent
behind it, which was already disclaimed in the blog post.

------
jacknews
Check arr3[2] and arr3[3]

How would perl know to assign only the first 2 strings to arr3 and the next to
arr4? It can't, so it assigns them all to arr3.

Also why such terrible names.

Also why ternary at all, it's clearly(!) less clear in this case.

------
cafard
If you are not preserving the separator, why not

foreach my $sentence(@sentences) {

    
    
       foreach my $fragment (split /\s*[-:;]\s*/, $sentence) {
    
         print $fragment, "\n";
    }

------
termie
Try more parenthesis or perlmonks.com

~~~
rurban
Both are wrong answers.

[] "parens" (arrayrefs to enable two list assignments) would be ok, but we
don't call [] parens, they are called brackets.

perlmonks is long dead.

~~~
simcop2387
Perlmonk's isn't dead even if it's a shadow of it's former self. That said
reddit's perl subreddit [1] might be a decent choice as is IRC (freenode
#perl, magnet/irc.perl.org #perl-help) for live help.

[1] [https://reddit.com/r/perl/](https://reddit.com/r/perl/)

