3

A simple simulation of the problem:

use strict;
use warnings;

sub uniq
{
  my %seen;
  grep !$seen{$_}++, @_;
}

my @a = (1, 2, 3, 1, 2);

print shift @{uniq(@a)}; 

Can't use string ("3") as an ARRAY ref while "strict refs" in use

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Ωmega
  • 42,614
  • 34
  • 134
  • 203
  • What do you actrually do with that first item ... assign it to a variable, or really print it (or pass it to a function or such) ? – zdim Jan 27 '23 at 18:25
  • I want to avoid assigning it to a dummy/temporary variable. I want to use it as a parameter in another function. – Ωmega Jan 27 '23 at 18:26
  • OK. Then do you need to actually remove it from the return list as well (is the `shift` necessary)? What with the rest, keep it or not? – zdim Jan 27 '23 at 18:31
  • I need just the 1st item, rest is useless to me. But the code above is not exactly what I have – I use a different function (which **I cannot modify**) that at the end uses the `uniq` before it returns it. – Ωmega Jan 27 '23 at 18:34

3 Answers3

6

Need to impose a list context on the function call, and then pick the first element from the list.

The print, or any other subroutine call, already supplies a list context. Then one way to extract an element from a returned list

print +( func(@ary) )[0];

This disregards the rest of the list.

That + is necessary (try without it), unless we equip print itself with parentheses around all its arguments, that is

print( (func(@ary))[0] );

See this and/or this for examples and docs about the need for that + or ().

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Technically, `(uniq @a)[0]` should be identical to `$a[0]`, for any possible values of `@a`. Perhaps it will not differ between `undef` and `''` (empty string). – TLP Jan 28 '23 at 16:53
  • @TLP What they posted is just an example, their real function does more work. (See their comment.) Imagine `f(qw(a b c)) -> 3,4,5,6`. (Or, rather, what I imagine is a library returning a list but they only need the first value. And they can't touch library's code.) – zdim Jan 28 '23 at 21:21
  • Yes, I see it now. I was confused by the OP posting a self-made function included in the question, rather than a library function elsewhere. – TLP Jan 29 '23 at 11:44
4

If uniq returned an array reference, then @{uniq(...)} would be the correct idiom to get an array (which is a suitable argument for shift). For a more general list, you can cast the list to an array reference and then dereference it.

print shift @{ [ uniq(@a) ] };
mob
  • 117,087
  • 18
  • 149
  • 283
  • If we had an array reference instead, postfix dereferencing (available since v5.20), makes it more readable to get values out of references. To get the first element, use `f()->[0]`. A slice of the result would be `f()->@[0..2]`; or the entire array as an array would be `f()->@*`. Your example would be `print uniq(@a)->[0]` – Bob Lied Feb 01 '23 at 14:16
3

One option could be to return an array reference:

sub uniq {
  my %seen;
  [grep !$seen{$_}++, @_];
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Useful, but not applicable to my case, as in reality my function is more complex, and I cannot modify it (it is used multiple times in different part of the app). Thanks. – Ωmega Jan 27 '23 at 18:41
  • @Ωmega Aha, ok, yeah, that limits the possibilities of course :-) Cheers! – Ted Lyngmo Jan 27 '23 at 18:42
  • 1
    @Ωmega, Re "*I cannot modify it (it is used multiple times in different part of the app)*", You could make it context aware: `return wantarray ? @results : $results[0];` – ikegami Jan 27 '23 at 20:32
  • @ikegami Sidenote: `wantarray` is one of my favorite perl things along with `unless` and the general _"it [the perl interpreter] doesn't do what you tell it but what it thinks you want it to do"_ (loosely quoted). :-) – Ted Lyngmo Jan 27 '23 at 20:40