为什么我的Perl地图列表只返回1?

时间:2009-09-22 06:38:15

标签: perl map

我写的代码如下:

#!/usr/bin/perl 

my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = map { $_ =~ s/\..*$// } @input ;

print @output ;

我的目的是让没有扩展名的文件名存储在数组@output中。 但它存储s///返回的值而不是@output中更改的文件名,因此结果如下所示

1
1
1

那么在这种情况下使用map的正确方法是什么?

8 个答案:

答案 0 :(得分:16)

在所有这些答案中,没有人简单地说map返回最后一个评估表达式的结果。无论你最后做什么都是map返回的东西。它就像一个子程序或do返回结果的最后一个评估表达式。

Perl v5.14添加了非破坏性替换,我在Use the /r substitution flag to work on a copy中写道。它不返回替换次数,而是返回修改后的副本。使用/r标志:

my @output = map { s/\..*$//r } @input;

请注意,您不需要将$_与绑定运算符一起使用,因为这是默认主题。

答案 1 :(得分:15)

好的,首先,您可能需要$_ =~ s/\..*$// - 请注意示例中缺少的s。此外,您可能意味着map而不是grep

其次,这不符合你的要求。这实际上修改了@input!在grep(和map以及其他几个地方)中,$_实际上是每个值的别名。所以你实际上是在改变价值。

另请注意,模式匹配不会返回匹配的值;它返回true(如果有匹配)或false(如果没有)。这就是你所看到的所有1个。

相反,做这样的事情:

my @output = map {
    (my $foo = $_) =~ s/\..*$//;
    $foo;
} @input ;

首先将$_复制到$foo,然后修改$foo。然后,它返回修改后的值(存储在$foo中)。你不能使用return $foo,因为它是一个块,而不是一个子程序。

答案 2 :(得分:7)

已经讨论过$_别名列表值的问题。

但更重要的是:你的问题标题清楚地写着“地图”,但是你的代码使用了grep,虽然它看起来确实应该使用map。

grep将评估您提供的列表中的每个元素作为第二个参数。在列表上下文中,它将返回一个列表,该列表包含表达式返回true的原始列表的那些元素。

另一方面,

map使用表达式或块参数来转换list参数的元素,返回一个新的列表,该列表由原始的转换参数组成。

因此,您的问题可以通过以下代码解决:

@output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } @input;

这将匹配不是扩展名的文件名部分,并作为评估结果返回,如果没有扩展名,则返回原始名称。

答案 3 :(得分:2)

你错过了's'代替。

$_ =~ /\..*$//

应该是

$_ =~ s/\..*$//

另外,您可能最好使用s/\.[^\.]*$//作为正则表达式,以确保即使文件名中包含“。”也只是删除了扩展名。 (点)字符。

答案 4 :(得分:1)

derobert向您展示了将@input映射到@output的正确方法。

但是,我建议使用File::Basename

#!/usr/bin/perl

use strict;
use warnings;

use File::Basename;

my @input = qw( a.1.txt b.txt c.txt );
my @output = map { scalar fileparse($_, qr/\.[^.]*/) } @input ;

use Data::Dumper;
print Dumper \@output;

<强>输出:

C:\Temp> h
$VAR1 = [
          'a.1',
          'b',
          'c'
        ];

答案 5 :(得分:1)

如上所述,s ///返回执行的替换次数,map返回每次迭代计算的最后一个表达式,因此你的map返回全1。实现目标的一种方法是:

s/\..*$// for my @output = @input;

另一种方法是使用Algorithm::Loops

中的过滤器

答案 6 :(得分:0)

您的代码示例在匹配运算符中缺少s。除此之外,它对我来说很好:

$, = "\n";
my @input = ( "a.txt" , "b.txt" , "c.txt" );
my @output = grep { $_ =~ s/\..*$// } @input;
print @output;

输出是:

a
b
c

答案 7 :(得分:0)

问题:the s/../.../ operatorPerl's map都是必不可少的,期望您想要修改每个输入项; Perl事实上没有内置函数map,它可以在不修改输入的情况下产生结果。

使用s时,选项是附加r修饰符:

#!/usr/bin/perl 

my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = map { s/\..*$//r } @input ;

print join(' ', @output), "\n";

一般解决方案(由derobert建议)是使用List::MoreUtils::apply

#!/usr/bin/perl 

use List::MoreUtils qw(apply);

my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
my @output = apply { s/\..*$// } @input ;

print join(' ', @output), "\n";

或将其定义复制到您的代码中:

sub apply (&@) {
    my $action = shift;
    &$action foreach my @values = @_;
    wantarray ? @values : $values[-1];
}