(Perl-)列表推导式的等价写法(类似于Python)

13

我正在寻找用Perl表达这段Python代码的方法:

data = {"A": None, "B": "yes", "C": None}
key_list = [k for k in data if data[k]]  
# in this case the same as filter(lambda k: data[k], data) but let's ignore that

从一个角度看,我只想获取值为 Noneundef 的键。从另一个角度看,我想要的是Perl中相应的简洁的列表推导式(带有条件)


1
你的意思是要排除值为None或undef的键吗?这就是你的示例所做的。 - glenn jackman
嗯。事后看来那个确实听起来很模棱两可。我想当我说“我想要钥匙”时,我的意思是“我想要它们 - 这样我可以将它们过滤掉”,但回答的问题是正确的:如何在一行代码中挑选出特定的东西。抱歉 :) - conny
通常我会使用 map{ f($_)} grep{cond($_)} @list - JJoao
3个回答

20

我认为你需要使用grep函数:

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

my %data = ( A => undef, B => 'yes', C => undef );

my @keys = grep { defined $data{$_} } keys %data;

print "Key: $_\n" for @keys;

我也认为我打字太慢了,在发布答案之前应该重新加载页面。顺便说一下,0undef都可以处理null值,但请确保记住您使用的是哪个。在Perl中,false值和undefined值不同。为了澄清:在布尔测试中,undef返回false,但0也返回false。如果0是一个有效值,则要明确测试其定义性,而不仅仅是真实性。(我提到这一点是因为James选择了0,而我选了另一种方式,您可能知道它是否重要。)


无论你打字速度慢,你仍然会得到更多的投票! :) - James Thompson
这很奇怪,因为我们基本上写了相同的答案。我完全不理解这里的投票。 - Telemachus
我认为这表明该网站上的Perl程序员认为在这种情况下undef > 0。:-P - C. K. Young
Chris在这里说得很到点子上。我肯定学到了教训。 :)投票有点奇怪。没关系,你的答案很好,所以我不介意。 - James Thompson
1
是的,这两个提出的解决方案非常相似,但我认为关于undef的注释很有帮助,所以我会选择多数票。谢谢大家! - conny

13

使用grep函数:

#!/usr/bin/perl

use strict;
use warnings;

my %data = ("A" => 0, "B" => "yes", "C" => 0 );
my @keys = grep { $data{$_} } keys %data;

Grep返回右侧列表中表达式计算结果为真的值。正如Telemachus所指出的,你需要确保理解Perl中的真/假值。 这个问题提供了有关Perl中真值的良好概述。

你可能需要查看map,它将一个表达式应用于列表的每个元素并返回结果。下面是一个例子:

my @data = ("A" => 0, "B" => 1, "C" => 0 );
my @modified_data = map { $data{$_} + 1 } @data;
print join ' ', @data, "\n";
print join ' ', @modified_data, "\n";

酷!我很少在Perl中见到“非m//”的通用grep。通常我会使用类似这样的"map",带有一个块:{ <<cond>> ? $_ : () }。TMTOWTDI,我猜 :-) - Roboprog
@Roboprog - 谢谢。我认为这更简洁,但你的方式绝对也很好。@ysth - 你能给一个更具体的例子吗?这些!!字符有什么问题吗?这是Perl语言,所以我很想试试它。 :) - James Thompson
1
@James Thompson:()x在列表上下文中是列表重复运算符。!!()将转换为布尔值;任何值为真(true)的都会变成1,任何值为假(false)的都会在由()x提供的数值上下文中变为 0。因此,grep { xxx } 等同于 map { ($_)x!!( xxx ) },因为当 xxx 为 false 时,$_将被重复0次;当xxx为 true 时,$_将被重复一次。我轻松地拿map来做grep的想法开了个玩笑,展示了更加愚蠢的做法。 - ysth
如果我理解正确,[f(x) | x in list , pred(x)] 可以编码为 map { f(x) x!! pred(x) } list,太酷了! - JJoao

6

如果想要更多变化,请查看autobox(查看其实现的autobox::CoreMoose::Autobox)。

use autobox::Core;

my %data = ( A => undef, B => 'yes', C => undef );
my $key_list = %data->keys->grep( sub { defined $data{$_} } );

say "Key: $_" for @$key_list;

# => Key: B


Moose::Autobox带有键/值'kv',使代码更加DRY:

my $key_list = %data->kv->grep( sub{ defined $_->[1] } )->map( sub{ $_->[0] } );

以下是更详细且更长的版本:

my $key_list = %data->kv
                    ->grep( sub { my ($k, $v) = @$_; defined $v } )
                    ->map(  sub { my ($k, $v) = @$_; $k }         );

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