2013-05-28 2 views
3

[Perl 5.8.8]Perl: как сделать компактное имя из пронумерованной последовательности

У меня есть последовательность имен вещей, как:

names='foobar1304,foobar1305,foobar1306,foobar1307' 

где имена отличаются только прилежащей строкой цифр где-то в названии. Строки цифр в любой последовательности имеют одинаковую длину, а строки цифр образуют непрерывную числовую последовательность без пропусков, например. 003,004,005.

Я хочу компактное представление, как:

compact_name='foobar1304-7' 

(. Компактная форма это просто имя, так что точная форма является предметом переговоров) Там, как правило, только < 10 вещей, хотя некоторые наборы могли бы охватывать десятилетие, например

'foobaz2205-11' 

Есть ли какой-нибудь краткий способ сделать это в perl? Я не большой Perl хакера, так что немного нежного ...

бонусные баллы для обработки встроенных последовательностей, как:

names='foobar33-pqq,foobar34-pqq,foobar35-pqq' 

Идеальный сценарий будет аккуратно падать обратно 'firstname2301-lastname9922' в случае она может» t идентифицировать последовательность в именах.

+2

Этот вопрос недоопределен. Вам нужно разработать набор правил, которые обрабатывают все возможные входы. Для вашего последнего примера подразумеваемое упрощение 'foobar33-35-pqq' может оказаться неоднозначным в контексте. Основная идея извлечения числа и коллапсирующих последовательностей довольно проста, учитывая возможности регулярных выражений Perl, но ваша большая проблема решает, что вы на самом деле хотите сделать. –

+0

фиксированный, думаю. Я не вижу двусмысленности в 'foobar33-35-pqq'. Примечание. Я указал, что «** a ** непрерывная строка цифр». Если в названии есть несколько строк цифр, я просто заступлю. –

+0

Я думаю, что вы хотите разбить строку на массив ('@list = split (", ", $ names)' или что-то подобное), затем найдите [самый длинный общий префикс] (http://stackoverflow.com/questions/9114402/regexp-find-longest-common-prefix-of-two-strings) этих слов в массиве. Бонусные баллы за поиск самого длинного общего суффикса. Таким образом, вы разделяете слова на префикс, переменную часть и суффикс. Тогда ваш ответ «$ prefix $ varFirst». «-». "$ VarLast $ Суффикс". Звучит ли это правильно? –

ответ

2

Я не уверен, что я получил вашу спецификацию, но работает как-то:

#!/usr/bin/perl 
use warnings; 
use strict; 

use Test::More; 

sub compact { 
    my $string = shift; 
    my ($name, $value) = split /=/, $string; 

    $name =~ s/s$// or die "Cannot create compact name for $name.\n"; #/ SO hilite bug 
    $name = 'compact_' . $name; 

    $value =~ s/^'|'$//g;            #/ SO hilite bug 
    my @values = split /,/, $value;         #/ SO hilite bug 
    my ($prefix, $first, $suffix) = $values[0] =~ /^(.+?)([0-9]+)(.*)$/; 

    my $last = $first + $#values; 
    my $same = 0; 
    $same++ while substr($first, 0, $same) eq substr($last, 0, $same); 
    $last = substr $last, $same - 1; 

    for my $i ($first .. $first + $#values) { 
     $values[$i - $first] eq ($prefix . $i . $suffix) 
      or die "Invalid sequence at $values[$i-$first].\n"; 
    } 
    return "$name='$prefix$first-$last$suffix'"; 
} 


is(compact("names='foobar1304,foobar1305,foobar1306,foobar1307'"), 
    "compact_name='foobar1304-7'"); 

is(compact("names='foobaz2205,foobaz2206,foobaz2207,foobaz2208,foobaz2209,foobaz2210,foobaz2211'"), 
    "compact_name='foobaz2205-11'"); 

is(compact("names='foobar33-pqq,foobar34-pqq,foobar35-pqq'"), 
    "compact_name='foobar33-5-pqq'"); 

done_testing(); 
1

Кто-то уверено, разместит более элегантное решение, но следующий

use strict; 
use warnings; 

my $names='foobar1308-xy,foobar1309-xy,foobar1310-xy,foobar1311-xy'; 
my @names = split /,/,$names; 

my $pfx = lcp(@names); 

my @nums = map { m/$pfx(\d*)/; $1 } @names; 
my $first=shift @nums; 
my $last = pop @nums; 
my $suf=$names[0]; 
$suf =~ s/$pfx\d*//; 

print "$pfx\{$first-$last}$suf\n"; 

#https://gist.github.com/3309172 
sub lcp { 
    my $match = shift; 
    substr($match, (($match^$_) =~ /^\0*/, $+[0])) = '' for @_; 
    $match; 
} 

принты:

foobar13{08-11}-xy