изменить $ _, если параметры не указаны - PullRequest
8 голосов
/ 26 января 2012

У меня есть эта trim подпрограмма:

sub trim {
    for (@_) {
        s|^\s+||;
        s|\s+$||;
    }
}

Она обрезает пробелы "на месте", то есть:

trim $x, $y;

урежет пробелы в $ x и $ y(изменяя их).

Как я могу улучшить trim, чтобы он позволял мне называть его следующим образом:

trim;

, и это будет то же самое, что и

trim $_;

Ответы [ 3 ]

11 голосов
/ 26 января 2012

Все, что вам нужно сделать, это заменить @_ на @_ ? @_ : $_ в аргументе для цикла for.

sub trim {
    for (@_ ? @_ : $_) {
        s|^\s+||;
        s|\s+$||;
    }
}

Согласно ответу ikegami, возникает неприятная проблема:делать с лексикой $_ (объявленной с my $_;), поскольку эта версия $_ не глобальна, а привязана к лексической панели.

Прототип (_) является одним из способовчтобы решить эту проблему, но это также означает, что подпрограмма принимает только один аргумент, и этот аргумент имеет скалярный контекст, что делает его таким же плохим, как и прототип ($) (из-за непреднамеренных последствий скалярного контекста).

Каждый раз, когда вы пытаетесь сотворить магию на лексике, вам пригодится модуль PadWalker .Итак, вот версия, которая корректно работает со списками, работает как на лексическом, так и на глобальном $_, и не накладывает скалярный контекст на сайт вызова:

use PadWalker 'peek_my';

sub trim {
    my $it;
    unless (@_) {
        my $pad = peek_my 1;
        $it = $$pad{'$_'} || \$::_
    }
    for (@_ ? @_ : $$it) {
        s/^\s+//;
        s/\s+$//;
    }
}

{local $_ = " a "; trim;    say "[$_]"}  # prints "[a]"
{my $_    = " a "; trim;    say "[$_]"}  # prints "[a]"
{my $x    = " a "; trim $x; say "[$x]"}  # prints "[a]"

Лично я просто избегаю лексических $_ и такие ужасные проблемы просто исчезают.Но если вам нужно это поддержать, это способ.

11 голосов
/ 26 января 2012

Просто проверьте, нет ли предоставленных аргументов:

use strict;
use warnings;

$_=" abc ";

sub trim
{
    if(@_)
    {
        foreach my $i(@_)
        {
            $i=~s/^\s+//;
            $i=~s/\s+$//;
        }
    }
    else
    {
        s/^\s+//;
        s/\s+$//;
    }
}

trim;

print "!$_!\n"; #Throwing in exclamation points just to make sure white space is gone.

Это производит вывод

!abc!
4 голосов
/ 26 января 2012

Если ваш trim только что взял один аргумент, вы можете использовать прототип _.

sub trim(_) {
   for ($_[0]) {
      s|^\s+||;
      s|\s+$||;
   }
}

Тогда все это будет работать:

{ local $_ = "  abc  "; trim;    say;    }
{ my    $_ = "  abc  "; trim;    say;    }
{ my    $x = "  abc  "; trim $x; say $x; }

Но поскольку ваш trim принимает список аргументов, вы не можете использовать прототип _. Вы должны использовать @_ ? @_ : $_.

sub trim {
   for (@_ ? @_ : $_) {
      s|^\s+||;
      s|\s+$||;
   }
}

Но это означает, что он не будет работать с лексическим $_.

{ local $_ = "  abc  "; trim;    say;    }
{ my    $_ = "  abc  "; trim;    say;    }  # XXX $::_ changed instead of $_
{ my    $x = "  abc  "; trim $x; say $x; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...