使用 Perl 在列表列表中搜索项目
Posted
技术标签:
【中文标题】使用 Perl 在列表列表中搜索项目【英文标题】:Search for item in list of lists with Perl 【发布时间】:2013-12-20 04:29:04 【问题描述】:要确定项目是否在数组或列表中,我使用智能匹配 ~~
运算符。但这仅适用于简单的数组/列表,以找出此类项目是否存在。是否有类似的方法可以将此运算符用于列表列表?列表按第一列排序。由于规模很大,我正在寻找一些最有效,最快的核心解决方案。另外应该返回第二列的值。
列表示例,第一列具有唯一值,已排序:
my $list = [
[ 'alpha' , 'item X' ],
[ 'beta' , 'item Q' ],
[ 'gama' , 'item C' ],
...
];
...搜索'beta'
得到结果'item Q'
,搜索'omega'
得到结果undef
。
【问题讨论】:
多大才算相当大? 写一些代码并计时;如果它太慢,那么担心最快的方法。几乎以任何方式遍历 2000 个元素都不会花费可察觉的时间。 @ysth - 现在大约是 2000,但我预计大小会增长,所以我最好优化代码,这样以后就不必更改了 如果有一百万,它仍然可能不到十分之一秒。首先,有速度问题;只有这样才能优化速度。 好吧,既然您告诉我们它是按您正在搜索的列排序的,是的:) 【参考方案1】:~~~
运算符或智能匹配应该适用于二维数组。
这是我能想到的最有效的方法来查看一个项目是否在一个数组中,而不是遍历数组。
编辑:
请查看我回复的第一条评论。它似乎是实验性的,建议不要使用智能匹配。直到发表评论后我才知道。我很抱歉。
【讨论】:
从 5.18 开始,smart match is experimental:“很明显,smartmatch 几乎肯定会在未来改变或消失。不建议依赖其当前行为。” 嗯,好的,谢谢你的提示。我不知道这一点!【参考方案2】:此代码有效……
my @array = (
[ 'alpha' , 'item 1' ],
[ 'beta' , 'item 2' ],
[ 'alpha' , 'item 3' ],
);
say 'beta' ~~ @array ? "yep" : "nope"; #=> yep
... 因为 smartmatch 递归:SCALAR ~~ ARRAY
智能匹配数组的每个条目,直到成功。碰巧,外部数组的元素本身就是数组,所以同样的事情又会发生。
当然,您不应该使用智能匹配。 use List::MoreUtils qw< any >
改为:
use List::MoreUtils qw< any >;
my @array = (
[ 'alpha' , 'item 1' ],
[ 'beta' , 'item 2' ],
[ 'alpha' , 'item 3' ],
);
if (any any 'beta' eq $_ @$_ @array)
say "yep";
else
say "nope";
#=> yep
这相当难看,但any
应该比grep
更有效(您可以在此处等效地使用它)。与 smartmatch 不同,这些解决方案不会做偷偷摸摸的意外事情,例如递归或执行 coderefs -.-
如果你想获得“其他”条目:
use List::MoreUtils qw< any firstval >;
my @array = (
[ 'alpha' , 'item 1' ],
[ 'beta' , 'item 2' ],
[ 'alpha' , 'item 3' ],
);
if (my $array = firstval any 'beta' eq $_ @$_ @array)
my $other_item = firstval 'beta' ne $_ @$array;
say "yep: $other_item";
else
say 'nope';
#=> yep: item 2
【讨论】:
如果'beta'
也出现在第二列怎么办?
谢谢。您的解决方案(编辑后)适用于线性搜索。如果对@array
进行排序,可以提供更好的解决方案和性能,我相信...【参考方案3】:
使用以下结构/数据:
my $list = [
[ "alpha" , "X" ],
[ "beta" , "Q" ],
[ "gama" , "Z" ],
[ "delta" , "C" ],
];
my $str = "gama";
我很快找到了以下解决方案:
my ($min, $max, $pos, $cmp) = (-1, $#$list + 1, undef, -1);
while (($cmp != 0) && (($max - $min) > 1))
$cmp = $str cmp $$list[$pos = int(($min + $max) / 2)][0];
($min, $max) = ($cmp > 0) ? ($pos, $max) : ($min, $pos);
return ($cmp == 0) ? $$list[$pos][1] : undef;
【讨论】:
这里我会非常小心。众所周知,实现二分搜索很难做到完全正确。***上的版本与您的版本有几个小差异,应该引起关注。 @woolstar - 您能否更具体地说明您在我的解决方案中看到的风险?更进一步,如果您发布自己的解决方案会很好,所以我可以对其进行测试并考虑它,如果运行良好且足够快。 您的解决方案中的风险是您可能没有正确实施二进制搜索的所有边缘情况。例如,***上的示例设置($min,$max) = ($cmp > 0) ? ($pos +1, $max) : ($min, $pos-1)
,但也测试循环内的相等性并直接返回。我使用哈希的解决方案如下。
@woolstar - 我在我的解决方案中看不到任何问题/风险。在我的代码中,$min
和 $max
是外部边界,需要测试内部的任何内容。我的代码O(log2 n)
比你的代码O(n)
效率更高,所以列表中有数百万条记录,你的代码将进行数百万次迭代,我的代码最多 14 次。
在哈希中查找值是摊销的O(1)
。因此,如果您查找许多值,构建散列很可能会更好(对于 many 的某些值)。【参考方案4】:
如果$list
对于多个查询是固定的,那么构建一个键值的映射:
# do this only when $list changes
my %listvalues= map $_->[0], $_ @$list ;
...
sub lookup
my ($key)= @_ ;
return (exists $listvalue$key ) ? $listvalues$key[1] : undef ;
【讨论】:
这个解决方案很慢,因为构建哈希是O(n)
如果$list
是固定的,那么这个解决方案就是O(1)
进行查找。以上是关于使用 Perl 在列表列表中搜索项目的主要内容,如果未能解决你的问题,请参考以下文章
如何在列表中查找项目的索引,在 Python 中使用正则表达式搜索项目?
使用 LINQ 根据 C# 中的属性值搜索嵌套在另一个列表中的列表中的项目?