0

I have the following array:

  my @anim = ('rn4,mm8,bosTau2,canFam2,dasNov1,echTel1',
  'rn4,mm8,oryCun1,bosTau2,canFam2,dasNov1,echTel1');

It contain multiple strings, each string are comma separated. What I want to do is to sort them based on the greatest members of the string. Hence what I tried to do is this:

my @animsort = sort{scalar(split(",",$b)) <=> scalar(split(",",$a))} @anim;

But it gives this error:

Use of implicit split to @_ is deprecated at ./scripts/mycode.pl line 35

What's the correct way to do it?

neversaint
  • 60,904
  • 137
  • 310
  • 477
  • I just saw code like this somewhere. Do the various strings in your array `rn4, mm8 ...` mean anything? – gideon Feb 13 '13 at 07:13

4 Answers4

6

If "greatest member" means the last one, use a list slice:

my @sorted_anim = sort {
    (split /,/, $b)[-1] cmp (split /,/, $a)[-1]
} @anim;

Using a Schwartzian Transform:

my @sorted_anim = map $_->[0],
    sort { $b->[1] cmp $a->[1] }
    map [ $_, (split /,/, $_)[-1] ],
    @anim;

Or a Guttman-Rosler Transform:

my @sorted_anim = map /,(.*)/s,
    sort { $b cmp $a }
    map { (split /,/, $_)[-1] . ",$_" }
    @anim;

The Schwartzian Transform aims at improving efficiency by calculating the sort key only once for each element; Guttman-Rosler goes one better by also temporarily combining the sort key with the full data (often with pack, where the sort key is of known length; here just with a known delimiter character) in such a way that perl will use an optimized built-in sort routine instead of executing perl code for each comparison.

ysth
  • 96,171
  • 6
  • 121
  • 214
  • if my guess of your intent was right, it is a little odd that your sample data has both elements with the same last member. – ysth Feb 12 '13 at 09:05
  • +1 for Guttman-Rosler Transform, which I haven't seen before. –  Feb 12 '13 at 10:13
  • @ysth by greatest member I think @neversaint means the string with the most comma separated elements. So changing your 1st schwarzian transform map into `map [ $_, scalar(split /,/, $_) ]` does the business – andeyatz Feb 12 '13 at 11:14
  • but gets right back to the original question of not invoking the old deprecated scalar-context split functionality. it should be `map [ $_, scalar( () = split /,/, $_ ) ]` then – ysth Feb 12 '13 at 16:32
2

scalar() does not return the "greatest member of a string" or cause split to do so. Maybe you're looking for List::Util's maxstr?

use List::Util qw( maxstr );

my @sorted_anim = sort {
   maxstr(split /,/, $b) cmp maxstr(split /,/, $a)
} @anim;
Zaid
  • 36,680
  • 16
  • 86
  • 155
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

If you want to sort by the count of elements in each string, your code will do that. I am not getting any errors with your posted code in Perl 5.16. Do you have an older version of Perl? (I found some evidence that your code may generate the error in older versions).

Here is another option:

my @animsort = sort{(() = $b =~ /,/g) <=> (() = $a =~ /,/g)} @anim;

This sorts by counting the commas, rather than actually splitting the string. The funny () = syntax is to make sure the regex is treated as being in list context, so we can count the matches.

Community
  • 1
  • 1
0

Regexp could sole everything or you could remove the warnings with 'no warnings;' pragma locally.

my $comma_re = qr!,!;   
my @animsort = sort{$b =~ s!$comma_re!!g <=> $a =~ s!$comma_re!!g} @anim;
print Dumper(\@anim,\@animsort);
TLP
  • 66,756
  • 10
  • 92
  • 149
user1126070
  • 5,059
  • 1
  • 16
  • 15
  • 1
    Problem: this actually modifies the data by deleting all the commas. It is better to use `(() = $b =~ /$comma_re/g)`, even if it is rather ugly. –  Feb 12 '13 at 09:47