Why is the list my Perl map returns just 1's?

后端 未结 8 1809
时光说笑
时光说笑 2021-01-02 07:32

The code I wrote is as below :

#!/usr/bin/perl 

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

print @o         


        
相关标签:
8条回答
  • 2021-01-02 07:51

    The problem of $_ aliasing the list's values was already discussed.

    But what's more: Your question's title clearly says "map", but your code uses grep, although it looks like it really should use map.

    grep will evaluate each element in the list you provide as a second argument. And in list context it will return a list consisting of those elements of the original list for which your expression returned true.

    map on the other hand uses the expression or block argument to transform the elements of the list argument returning a new list consisting of the transformed arguments of the original.

    Thus your problem could be solved with code like this:

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

    This will match the part of the file name that is not an extension and return it as a result of the evaluation or return the original name if there is no extension.

    0 讨论(0)
  • 2021-01-02 07:53

    Your code sample is missing an s in the match operator. Other than that, it worked fine for me:

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

    Output is:

    a
    b
    c
    
    0 讨论(0)
  • 2021-01-02 07:54

    The problem: both the s/../.../ operator and Perl's map are imperative, expecting you to want to modify each input item; Perl doesn't in fact have a builtin for the functional map that yields its results without modifying the input.

    When using s, an option is to append the r modifier:

    #!/usr/bin/perl 
    
    my @input = ( "a.txt" , "b.txt" , "c.txt" ) ;
    my @output = map { s/\..*$//r } @input ;
    
    print join(' ', @output), "\n";
    

    The general solution (suggested by derobert) is to use 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";
    

    or copy its definition into your code:

    sub apply (&@) {
        my $action = shift;
        &$action foreach my @values = @_;
        wantarray ? @values : $values[-1];
    }
    
    0 讨论(0)
  • 2021-01-02 07:56

    As mentioned, s/// returns the number of substitutions performed, and map returns the last expression evaluated from each iteration, so your map returns all 1's. One way to accomplish what you want is:

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

    Another way is to use Filter from Algorithm::Loops

    0 讨论(0)
  • 2021-01-02 08:01

    Out of all of those answers, no one simply said that map returns the result of the last evaluated expression. Whatever you do last is the thing (or things) map returns. It just like a subroutine or do returning the result their last evaluated expression.

    Perl v5.14 adds the non-destructive substitution, which I write about in Use the /r substitution flag to work on a copy. Instead of returning the number of replacements, it returns the modified copy. Use the /r flag:

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

    Note that you don't need to use the $_ with the binding operator since that's the default topic.

    0 讨论(0)
  • 2021-01-02 08:09

    derobert shows you a correct way of mapping @input to @output.

    I would, however, recommend using 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;
    

    Output:

    C:\Temp> h
    $VAR1 = [
              'a.1',
              'b',
              'c'
            ];
    
    0 讨论(0)
提交回复
热议问题