按照分割大小对数组排序

3

我正在尝试按元素部分的大小对数组进行排序。元素遵循一个模式x/y[.../z],我希望它们按照元素数量排序。例如,1/2在拆分/后有两个元素,12/365/85有三个元素。我似乎无法弄清楚在这样一个看似简单的代码片段中出了什么问题。

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper;

my @dummy_arr = ['12345/3/365/45/12', '1/2', '3/2', '1/2/3', '2/3/4'];
@dummy_arr = sort { scalar ( split /\//, $a ) < scalar ( split /\//, $b ) } @dummy_arr;

print Dumper(\@dummy_arr);

输出:

$VAR1 = [
          [
            '12345/3/365/45/12',
            '1/2',
            '3/2',
            '1/2/3',
            '2/3/4'
          ]

        ];

期望输出:

$VAR1 = [
            '1/2',
            '3/2',
            '1/2/3',
            '2/3/4',
            '12345/3/365/45/12'
        ];
1个回答

11

您把一些东西混淆了。

您将一个数组引用放入了@dummy_arr中。

my @dummy_arr = [ '12345/3/365/45/12', '1/2', '3/2', '1/2/3', '2/3/4' ];
这相当于:
my @dummy_arr =  ( [ '12345/3/365/45/12', '1/2', '3/2', '1/2/3', '2/3/4' ] );

在您的 Dumper 输出中也可见。

$VAR1 = [                   # <-- because of the ref \@dummy_arr you pass to Dumper
          [                 # <-- first elem is already an array ref
            '12345/3/365/45/12',
            '1/2',
            '3/2',
            '1/2/3',
            '2/3/4'
          ]
        ];

当你执行 sort { ... } @dummy_arr 时,只有一个值被传递给了 sort。因为 sort 很聪明,如果只有 $a 没有 $b,它会跳过排序,因为没有可排序的内容。当你在 sort lambda 中放入 { warn $a; scalar ... },它就变得很明显了。这里不会出现警告,所以它永远不会被调用。

但无论如何你都没有进行排序。 sort lambda 需要返回类似于 -10 或者 1 的值。

如果指定了 SUBNAME,则其应该是一个子例程名,其返回值为小于、等于或大于 0 的整数,具体取决于待排序列表中的元素应如何排序。

但你正在使用 <,它只返回 10。你需要使用 <=> 进行数字比较

my @dummy_arr = ( '12345/3/365/45/12', '1/2', '3/2', '1/2/3', '2/3/4' );
@dummy_arr = sort { scalar( split /\//, $a ) <=> scalar( split /\//, $b ) } 
   @dummy_arr;

print Dumper( \@dummy_arr );

__END__
$VAR1 = [
          '1/2',
          '3/2',
          '1/2/3',
          '2/3/4',
          '12345/3/365/45/12'
        ];

最后,由于分割是一项昂贵的操作,如果数组中有很多元素,需要重复执行很多次,因此您可能希望使用Schwartzian变换来完成此操作。

my @dummy_arr = ( '12345/3/365/45/12', '1/2', '3/2', '1/2/3', '2/3/4' );
@dummy_arr =
    map  { $_->[0] }
    sort { $a->[1] <=> $b->[1] }
    map  { [ $_, scalar split( qr{/}, $_ ) ] } @dummy_arr;

这会产生相同的输出,但以内存为代价换取CPU,因此速度更快。


1
更准确地说,排序 lambda 应该返回 < 0、0 或 > 0。因此,另一种方法是用“-”替换“<”。 - Jonathan Hall
1
糟糕!整天使用不同的编程语言让我忘记了不同语言中数据类型的差异。此外,Schwartzian变换值得一试,非常有趣! - Bram Vanroy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接