В Perl, каков нормальный путь к преобразованию строки в список ее символов?

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

@characters = split //, $string

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

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

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

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

6
задан Jim Counts 1 March 2010 в 15:06
поделиться

7 ответов

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

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

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

6
ответ дан 8 December 2019 в 02:35
поделиться

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

@characters = split //, $string;

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

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

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

@characters = map chr, unpack("C*", $string);
2
ответ дан 8 December 2019 в 02:35
поделиться

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

my @characters = chars($string);
sub chars { split //, $_[0] }
5
ответ дан 8 December 2019 в 02:35
поделиться

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

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

Я провел тест на нескольких версиях 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 );
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 '</pre>\n';
done

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 », ... .

9
ответ дан 8 December 2019 в 02:35
поделиться

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

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

@characters = get_characters($string);
4
ответ дан 8 December 2019 в 02:35
поделиться

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

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

@characters = $string =~ /./gs;
4
ответ дан 8 December 2019 в 02:35
поделиться

Для менее читабельного и более краткого (и все еще с перебором regex):

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

(Я научился этой идиоме, играя в код-гольф)

.
5
ответ дан 8 December 2019 в 02:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: