2010-06-26 3 views
2

Я новичок в функции map и grep, и я пытаюсь сделать существующий сценарий более кратким.Perl Map Function

Я могу успешно «grep» @tracknames, но у меня проблема с «картой». Я хочу, чтобы @trackartist возвращал true, если два последовательных «-» находятся в строке и принимают значение $ 1, в противном случае значение false, но возвращает всю строку, если верхнее условие не выполняется. Что я делаю неправильно?

my @tracknames = grep /^\d\d\..*?(\.(?:flac|wv))$/, <*.*>; 
my @trackartist = map { s/^\d\d\.\s(.*?)\s--.*?\.(?:flac|wv)$/$1/; $_; } <*.*>; 

Sample of files 
01. some track artist 1 -- some track name 1.(flac or wv) 
02. some track artist 2 -- some track name 2.(flac or wv) 
03. some track artist 3 -- some track name 3.(flac or wv) 
etc. 
+0

Не могли бы вы уточнить, что вы хотите? ('@ trackartist' - это массив, он ничего не возвращает * per se *.) Было бы полезно, если бы мы увидели список файлов, которые вы хотели:' <*.*> 'glob'd и содержимое' @ trackartist' вас разыскивается. – pilcrow

+1

Я думаю, вы можете смутить насчет 'map'. Принимая во внимание, что имеет смысл подчеркнуть истинно-ложную оценку, когда речь идет о «grep», на самом деле это не точка «карты». Более подробную информацию о различии между 'grep' и' map' см. По адресу http://stackoverflow.com/questions/575490/whats-the-difference-between-grep-and-map-in-perl/576044#576044. – FMc

+0

Я опубликовал образец возможной структуры имени файла. С @tracknames я grep все имя файла с расширениями, но я также хочу grep или назначить массив именам исполнителя трека, если они существуют, а затем использовать с $ trackartist [cnt ++] для целей s ///. – thebourneid

ответ

6

Помните, что grep для фильтрации список и map для преобразования списка. Прямо сейчас, ваш оператор map возвращает $_ для каждого элемента в списке. Если $_ соответствует шаблону в вашей замене, он будет изменен и заменен первым совпадением. В противном случае он не изменяется и возвращается исходный $_.

Похоже, вы хотите отфильтровать элементы, которые не соответствуют шаблону. Одним из способов было бы совместить карту и Grep:

my @trackartist = map { s/^\d\d\.\s(.*?)\s--.*?\.(?:flac|wv)$/$1/; $_; } 
        grep { /^\d\d\.\s(.*?)\s--.*?\.(?:flac|wv)$/ } <*.*>; 

Конечно, это означает, что вы делаете то же матч шаблон дважды. Другой подход - сделать преобразование с map, но преобразовать все, что не соответствует шаблону, в пустой список.

my @trackartist = map { /^\d\d\.\s(.*?)\s--.*?\.(?:flac|wv)$/ ? $1 : () } <*.*> 

Это использует тройной условный оператор (?:), чтобы проверить, если регулярное выражение соответствует (возвращает истинное значение). Если это так, возвращается $1 из блока map, если нет, возвращается пустой список (), который ничего не добавляет к списку в результате map.

В качестве дополнительной заметки вы можете изучить функцию glob, а не <>, что имеет некоторые недостатки.

+0

Спасибо за вашу помощь.Из объяснения я пришел к выводу, что «карта» может быть не самым подходящим решением в моем случае, и я мог бы попробовать другой подход. Является ли это правильным кодом, если совпадение шаблонов не выполняется - верните значение $ artist. Я обязательно займусь функцией glob. my @trackartist = map {/^\d\d\.\s(.*?)\s--.*?\.(?:flac|wv)$/; $ 1? $ 1: ($ artist)} <*.*>; – thebourneid

+2

Эта идиома лучше выражена как '/.../? $ 1:() '. –

+0

Хорошая точка, gbacon. Обновлено. – friedo

2

Мне нравится map и grep так же, как и следующий парень, но ваша задача больше подходит для подхода к разбору и победе. Я говорю об этом, потому что ваши комментарии говорят о том, что ваш интерес к map ведет вас по дороге, в результате которой вы получите модель данных, состоящую из параллельных массивов - @tracks, @artists и т. Д. - которые часто трудно поддерживать в долгое время. Вот эскиз того, что я имею в виду:

my @tracks; 

while (my $file_name = <DATA>){ # You'll use glob() or <*.*> 
    # Filter out unwanted files. 
    my ($num, $artist_title, $ext) = $file_name =~/
     ^(\d\d) \. \s* 
     (.*) 
     \. (flac|wv) $ 
    /x; 
    next unless $ext; 

    # Try to parse the artist and title. Adjust as needed. 
    my ($artist, $title) = split /\s+--\s+/, $artist_title, 2; 
    ($artist, $title) = ('UNKNOWN', $artist) unless $title; 

    # Store all info as a hash ref. No need for parallel arrays. 
    push @tracks, { 
     file_name => $file_name, 
     ext  => $ext, 
     artist  => $artist, 
     title  => $title, 
    }; 
} 

__DATA__ 
01. Perl Jam -- Open or die.wv 
02. Perl Jam -- Map to nowhere.flac 
03. Perl Jam -- What the #[email protected]!?.wv 
04. Perl Jam -- Regex blues.wv 
05. Perl Jam -- Use my package, baby.wv 
06. Perl Jam -- No warnings.wv 
07. Perl Jam -- Laziness ISA virtue.wv 
08. Guido and the Pythons -- Home on the xrange.flac 
09. Guido and the Pythons -- You gotta keep em generated.flac 
10. StackOverflow medley.wv 
foo.txt