В Perl, каков разумный способ преобразования строки в список ее символов? - PullRequest
6 голосов
/ 01 марта 2010

Мне было интересно, есть ли лучший, но лаконичный способ разбить строку на символы

@characters = split //, $string

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

Я придумал это:

@characters = map { substr $string, $_, 1 } 0 .. length($string) - 1

но я нахожу это уродливее и менее читабельным. Как вы предпочитаете разбивать эту строку на символы?

Ответы [ 7 ]

9 голосов
/ 01 марта 2010

Различные примеры и сравнение скорости.

Я подумал, что было бы неплохо узнать, как быстро некоторые из способов разбивают строку на каждый символ.

Я провел тест на нескольких версиях Perl, которые у меня есть на моем компьютере.

test.pl

use 5.010;
use Benchmark qw(:all) ;
my %bench = (
   'split' => sub{
     state $string = 'x' x 1000;
     my @chars = split //, $string;
     \@chars;
   },
   'split-string' => sub{
     state $string = 'x' x 1000;
     my @chars = split '', $string;
     \@chars;
   },
   'split-capture' => sub{
     state $string = 'x' x 1000;
     my @chars = split /(.)/, $string;
     \@chars;
   },
   'unpack' => sub{
     state $string = 'x' x 1000;
     my @chars = unpack( '(a)*', $string );
     \@chars;
   },
   'match' => sub{
     state $string = 'x' x 1000;
     my @chars = $string =~ /./gs;
     \@chars;
   },
   'match-capture' => sub{
     state $string = 'x' x 1000;
     my @chars = $string =~ /(.)/gs;
     \@chars;
   },
   'map-substr' => sub{
     state $string = 'x' x 1000;
     my @chars = map { substr $string, $_, 1 } 0 .. length($string) - 1;
     \@chars;
   },
);
# set the initial state of $string
$_->() for values %bench;
cmpthese( -10, \%bench );
<code>for perl in /usr/bin/perl /opt/perl-5.10.1/bin/perl /opt/perl-5.11.2/bin/perl;
do
  $perl -v | perl -nlE'if( /(v5\.\d+\.\d+)/ ){
    say "## Perl $1";
    say "<pre>";
    last;
  }';
  $perl test.pl;
  echo -e '
\ п '; сделанный

Perl v5.10.0

               Rate split-capture match-capture map-substr match unpack split split-string
split-capture 296/s            --          -20%       -20%  -23%   -58%  -63%         -63%
match-capture 368/s           24%            --        -0%   -4%   -48%  -54%         -54%
map-substr    370/s           25%            0%         --   -3%   -48%  -53%         -54%
match         382/s           29%            4%         3%    --   -46%  -52%         -52%
unpack        709/s          140%           93%        92%   86%     --  -11%         -11%
split         793/s          168%          115%       114%  107%    12%    --          -0%
split-string  795/s          169%          116%       115%  108%    12%    0%           --

Perl v5.10.1

               Rate split-capture map-substr match-capture match unpack split split-string
split-capture 301/s            --       -31%          -41%  -47%   -60%  -65%         -66%
map-substr    435/s           45%         --          -14%  -23%   -42%  -50%         -50%
match-capture 506/s           68%        16%            --  -10%   -32%  -42%         -42%
match         565/s           88%        30%           12%    --   -24%  -35%         -35%
unpack        743/s          147%        71%           47%   32%     --  -15%         -15%
split         869/s          189%       100%           72%   54%    17%    --          -1%
split-string  875/s          191%       101%           73%   55%    18%    1%           --

Perl v5.11.2

               Rate split-capture match-capture match map-substr unpack split-string split
split-capture 300/s            --          -28%  -32%       -38%   -59%         -63%  -63%
match-capture 420/s           40%            --   -5%       -13%   -42%         -48%  -49%
match         441/s           47%            5%    --        -9%   -39%         -46%  -46%
map-substr    482/s           60%           15%    9%         --   -34%         -41%  -41%
unpack        727/s          142%           73%   65%        51%     --         -10%  -11%
split-string  811/s          170%           93%   84%        68%    12%           --   -1%
split         816/s          171%           94%   85%        69%    12%           1%    --

Как видите, split - самый быстрый из-за того, что это особый случай в коде для split.

split-capture - самый медленный, вероятно, потому что он должен установить $1 вместе с несколькими другими переменными соответствия.

Так что я бы порекомендовал перейти с простой старой split //, ..., или примерно эквивалентной split '', ....

6 голосов
/ 01 марта 2010

Почему использование регулярного выражения было бы "излишним"? Многие беспокоятся о том, что регулярные выражения в Perl излишни, потому что они думают, что для их запуска используется очень сложный и медленный алгоритм регулярных выражений. Это не всегда верно: реализация высоко оптимизирована, и многие простые случаи обрабатываются специально: то, что выглядит как регулярное выражение, может фактически выполнять так же, как и простой поиск по подстроке. Я бы совсем не удивился, если бы этот тип split также был оптимизирован. split на быстрее вашего map в некоторых тестах, которые я проводил. unpack кажется немного быстрее, чем split.

Я рекомендую split, потому что это «идиоматический» способ. Вы найдете это в perldoc, во многих книгах, и любой хороший программист на Perl должен знать это (если вы не уверены, что ваша аудитория поймет это, вы всегда можете добавить комментарий к коду, как кто-то предложил).

OTOH, если регулярные выражения "излишни" только потому, что синтаксис уродлив, то для меня слишком субъективно что-либо говорить. ; -)

5 голосов
/ 01 марта 2010

Для менее читаемого и более краткого (и все же с избыточным регулярным выражением):

@characters = $string =~ /./g;

(я выучил эту идиому из игры в гольф-код.)

5 голосов
/ 01 марта 2010

Это не намного понятнее, чем использование функции split для разбиения строки. Я полагаю, вы могли бы утверждать, что нулевой шаблон не является интуитивным; хотя я нахожу это достаточно ясным. Если вы хотите «чистую» альтернативу, оберните ее в подпункт:

my @characters = chars($string);
sub chars { split //, $_[0] }
4 голосов
/ 01 марта 2010

Я предпочитаю использовать технику split . Это хорошо известно и задокументировано.

Еще один способ ...

@characters = $string =~ /./gs;
4 голосов
/ 01 марта 2010

Ты прав. Стандартный способ сделать это - split //, $string. Чтобы сделать код более читабельным, вы можете создать простую функцию:

sub get_characters {
    my ($string) = @_;
    return ( split //, $string );
}

@characters = get_characters($string);
2 голосов
/ 01 марта 2010

Используйте split с нулевым шаблоном, чтобы разбить строку на отдельные символы:

@characters = split //, $string;

Если вам нужны только коды символов, используйте распаковать:

@values = unpack("C*", $string);

Возможно, вам придется включить use utf8 для распаковки, чтобы она работала правильно. И вы также можете использовать unpack + chr, чтобы разбить строку на отдельные символы, просто TMTOWTDI:

@characters = map chr, unpack("C*", $string);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...