Ternary Trouble in Perl
hackernoon.com
hackernoon.com
# 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
EDIT: fix the code slightly, and provide interactive pastebin.
# 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. 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
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...
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.htmlBut 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 semicolonHow 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.
foreach my $sentence(@sentences) {
foreach my $fragment (split /\s*[-:;]\s*/, $sentence) {
print $fragment, "\n";
}[] "parens" (arrayrefs to enable two list assignments) would be ok, but we don't call [] parens, they are called brackets.
perlmonks is long dead.