在Perl中返回最后一个非零元素的索引的优雅方法是什么?

4
我发现自己想要找到数组中最后一个非零元素的索引。因此,给定以下内容:
my @array = (0,0,5,9,0,0,0,7,0,3,0,0);
my $indexLastNonZero = insertElegantMethodHere(@array);
# expect $indexLastNonZero to be equal to 9;

我已经完成了以下操作:
for my $i (0 .. $#array) {
    $indexLastNonZero = $i if $array[$i] != 0;
};

我能工作,但是我总觉得在perl中一定有一种超级优雅(更智能?更好看?更高效?)的方法来完成这件事。我已经研究过List::Utils,但没有找到一个好的方法,想要一种独立于核心模块的方法。
有什么想法吗?
干杯。

4
我认为你的意思是9而不是10。 - FMc
确实,我甚至用我的肥手指数了一下元素,以确保我不会犯这样一个学生错误...但我还是犯了!一定是我的手指真的太肥了! - moigescr
问题已根据@FMc的评论进行了编辑,以具有正确的索引。 - moigescr
“一个独立于非核心模块的方法” - 如果您告诉我们您使用的Perl版本,这个限制将更容易考虑。 - Dave Cross
好的,我简化一下 -- 不使用模块! - moigescr
6个回答

13

使用 List::MoreUtils 来完成这样的任务:

use warnings;
use strict;

use List::MoreUtils;

my @array = (0,0,5,9,0,0,0,7,0,3,0,0);

print List::MoreUtils::lastidx { $_ } @array

谢谢。我确实研究过List::MoreUtils,但(如果我错了,请纠正我)我认为它不是核心模块,而我的问题指定了“想要一个独立于非核心模块的方法”(因为我运行Perl的计算机不是我的,出于各种原因,我不能添加模块!)。 - moigescr
1
@moigescr 然后下载 List::MoreUtils 的源代码,并将其与您的脚本一起包含。 - ThisSuitIsBlackNot
@moigescr,这没有任何意义。如果你可以从SO安装Perl,那么你也可以从CPAN安装代码。 - ikegami
@ikegami,我无法安装Perl(我使用运行Perl的Linux系统,在其中我被授予带有限制权限的帐户)...但我认为我们可能正在偏离主题! - moigescr
1
@moigescr ikegami的观点是,如果你可以从Stack Overflow使用Perl 代码,那么你也可以使用来自CPAN的Perl代码。安装CPAN模块不需要root访问权限;最坏的情况下,只需下载源代码并将其包含在您的项目中(大多数CPAN模块都是根据GPL或艺术许可证授权的)。避免使用CPAN会严重限制您的能力。为什么要重新发明轮子,当别人已经为您创建、完善和经过路试了成千上万个轮子? - ThisSuitIsBlackNot

5
my @array = (0,0,5,9,0,0,0,7,0,3,0,0);
my ($indexLastNonZero) = grep $array[$_], reverse 0 .. $#array;

到目前为止,我个人最喜欢的解决方案。它感觉很“Perl”。不过,如果数组非常大(在我的情况下并非如此)且大部分元素都是非零的,那么你将会创建一个大的临时数组,而@MichaelCarman的方法则不会。 - moigescr
这样做会做很多额外的工作:它建立一个与数组大小相同的索引列表,将其反转(创建第二个列表),然后找到所有非零元素的索引(仅保留第一个)。 - Michael Carman
@MichealCarman。确实,这远非高效...但我认为它看起来不错,而且我的数组很小。这是我最喜欢的,但我同意它可能不是最好的(目前仍然是你的;))。等待看看是否有更多的想法出现,但接近向你点头;) - moigescr
2
@moigescr,你的方法已经很高效了(for (0..$#arr)不会在内部生成列表),如果你愿意,你可以缩短它:$array[$_] and $indexLastNonZero = $_ for 0 .. $#array; - mpapec

5
从数组末尾开始向前查找,直到找到一个非零元素:
my @array = (0,0,5,9,0,0,0,7,0,3,0,0);

my $i = $#array;
$i-- while $i >= 0 && $array[$i] == 0;

print "The last non-zero element is at index $i\n";

$i >= 0 的测试是为了防止所有元素都为零的边缘情况。在这种情况下,$i 的结果值为 -1。


1
我最喜欢的解决方案之二。谢谢。正如你们中的许多人启发了我一样,诀窍是从数组末尾开始查找! - moigescr

1
您可以使用核心功能List::Util
use strict;
use warnings; 

use List::Util qw(first);

my @array = (0,0,5,9,0,0,0,7,0,3,0,0);
my $index = @array;

first { $index-- && $_ } reverse @array;

print "Last index that is non-zero: $index\n"; 

0
sub last_true {
    pop and return scalar @_ while @_;
    undef;
}

my $index = last_true(@foo);

0

采用破坏性方法,因此首先复制数组:

my @array2 = @array;
while (!pop @array2) {} # Remove up to and including the last non-zero
print scalar @array2;   # Size of remaining elements is index of last non-zero

无法处理所有元素都为false的情况。 - salva

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