3

I cannot exactly understand how the following snippet works:

my $str = 'abc def ghi';
my $num = () = $str =~ /\w+/g;
say $num; # prints the word count, 3

I know that $str =~ /\w+/g returns a list of the words which, apparently, is conveyed to the leftmost assignment. Then $num imposes a scalar context on that list and becomes 3.

But what does () = ('abc', 'def', 'ghi') mean? Is it something like my $a = my @b = (3, 5, 8)? If so, how is the list at the rightmost side transferred to $num at the leftmost side?

Tolga Kurt
  • 85
  • 1
  • 6
  • 1
    See [Scalar vs List Assignment Operator](https://stackoverflow.com/q/54564428/589924) – ikegami Apr 28 '20 at 18:04
  • 1
    You've got answers but I'll comment on a particular aspect. "_Is it something like..._" -- yes, precisely like that. (It would work as it stands without `my`). As for "_how is the list at the end transferred to..._" -- in Perl _expressions_ return values as well. `$v1 = $v2` returns `$v1` which can thus be further assigned (chained). So that `@b` in your example returns its list ... and then its assignment to a scalar produces its length (size). (We follow execution right to left, so I find "end" and "beginning" misleading.) – zdim Apr 29 '20 at 08:16
  • You're right @zdim, I updated the copy a bit to clarify that. – Tolga Kurt Apr 29 '20 at 13:38
  • Thanks @ikegami for pointing me to that question. It was exactly the generalized version of my question, but I couldn't find the search keywords that'd lead me there. – Tolga Kurt Apr 29 '20 at 13:40
  • Correction to what I said above ("_work as it stands without `my`_"): your example works as it stands -- with all `my`s as well: `perl -wE'my $n = my @ary = (3, 5, 8); say $n'` – zdim Apr 29 '20 at 17:17

2 Answers2

3

Each perl operator has specific behavior in list and scalar context. Operators give context to their operands, but receive context from what they are an operand to. When a list assignment is placed in scalar context, it returns the number of elements on the right side of the assignment. This enables code like:

while (my @pair = splice(@array, 0, 1)) {

There's nothing special about how = () = is handled; you could just as well do = ($dummy) = or = (@dummy) =; the key part is that you want the match to be list context (producing all the possible matches) and then to just get a count of them.

So you do a list assignment (which is what = does whenever there's either a parenthesized expression or an array or slice as the left operand) but since you don't actually want the values, you can use an empty list. And then place that in scalar context; in this case, using the list assignment as the right operand to a scalar assignment.

ysth
  • 96,171
  • 6
  • 121
  • 214
  • Nevermind, the part you extended contains more or less what I tried to add. – choroba Apr 28 '20 at 20:44
  • @choroba suggestions for improvement always appreciated – ysth Apr 28 '20 at 20:58
  • Can you be so kind as to explain this as well: evaluating `my $num = () = split /\s+/, 'abc def ghi';` makes `$num` 1. Split returns a list of 3 members here as well and the rest seems the same. However, $num evaluates to 1. – Tolga Kurt Apr 29 '20 at 15:15
  • 1
    @TolgaKurt split takes a third parameter that tells it how many fields to split, and in a ()= assignment, that defaults to one more than the number of things being assigned. do `my $num = () = split /\s+/, 'abc def ghi', -1;` to make it split an unlimited number of fields – ysth Apr 29 '20 at 17:45
  • Thank you @ysth for pointing that out. I try to isolate the split part like `(split /\s+/, 'abc def ghi')` hoping that it will use 0 for the omitted LIMIT parameter (as it would normally do), but it returns 1. I explicitly state 0: `(split /\s+/, 'abc def ghi', 0)`, but the result is still 1 (now this is strange, 0 means as many fields as required). When I state -1, it's finally 3 as you suggested. Now this became a totally different question I guess but I still wonder how `() =` defaults the LIMIT to one more than the number of things being assigned though (even when I state 0 explicitly). – Tolga Kurt Apr 29 '20 at 23:00
  • 1
    @TolgaKurt 0 is the same as not specifying it. perldoc says "In time-critical applications, it is worthwhile to avoid splitting into more fields than necessary. Thus, when assigning to a list, if LIMIT is omitted (or zero), then LIMIT is treated as though it were one larger than the number of variables in the list". You have to specify -1 (or any negative number, or just a very large number) to get all the fields. – ysth Apr 29 '20 at 23:36
  • 1
    this is a weird place where split (when compiled) reaches out and looks at how it is being called in an unusual way – ysth Apr 29 '20 at 23:37
2

Nowadays fewer people start learning Perl, one of reason is it has some obscure code like your example. Check the perlsecret page for Saturn https://metacpan.org/pod/distribution/perlsecret/lib/perlsecret.pod#Goatse

=( )=

(Alternate nickname: "Saturn")

If you don't understand the name of this operator, consider yourself lucky. You are advised not to search the Internet for a visual explanation.

The goatse operator provides a list context to its right side and returns the number of elements to its left side. Note that the left side must provide a scalar context; obviously, a list context on the left side will receive the empty list in the middle.

The explanation is that a list assignment in scalar context returns the number of elements on the right-hand side of the assignment, no matter how many of those elements were actually assigned to variables. In this case, all the elements on the right are simply assigned to an empty list (and therefore discarded).

Boying
  • 1,404
  • 13
  • 20
  • Warning: I feel bad after searching the meaning word goatse, I don't advise to search it for visual explanation also.... – Boying Apr 28 '20 at 16:50