Сортировка по второму слову в Perl - PullRequest
1 голос
/ 05 марта 2011

Эй, ребята, у меня есть этот файл под названием телефонная книга

Steve Blenheim:239-923-7366:238-934-7865:95 Latham Lane, Easton, PA 83755:11/12/56:20300
Betty Boop:245-836-8357:245-876-7656:635 Cutesy Lane, Hollywood, CA 91464:6/23/23:14500
Igor Chevsky:385-375-8395:385-333-8976:3567 Populus Place, Caldwell, NJ 23875:6/18/68:23400
Norma Corder:397-857-2735:397-857-7651:74 Pine Street, Dearborn, MI 23874:3/28/45:245700

И я пытаюсь отсортировать текст в обратном алфавитном порядке из второго слова (фамилии) и не смог найтикак это сделать.Я читаю из файла, делая это

  open (FILE, phonebook);
  @line = <FILE>;
  close(FILE);

какие-либо идеи?Я могу отсортировать первое поле в алфавитном порядке и в обратном порядке, но, кажется, не могу получить второе поле для правильной сортировки.Заранее спасибо

Ответы [ 6 ]

10 голосов
/ 05 марта 2011

Я разделяю беспокойство tadmc о том, что второе поле по пробелу не всегда будет фамилией, но, отвечая на вопрос, относящийся ко второму полю, вы можете получить его, используя split, и вы можете отсортировать егокак это:

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

@lines = sort { # Compare second fields
    (split " ", $a)[1]
    cmp
    (split " ", $b)[1]
} @lines;

версия с преобразованием Шварца (делает то же самое, что и предыдущая, только намного быстрее):

@lines = map { # Get original line back
    $_->[0]
} sort { # Compare second fields
    $a->[1] cmp $b->[1]
} map { # Turn each line into [original line, second field]
    [ $_, (split " ", $_)[1] ]
} @lines;
2 голосов
/ 02 декабря 2012

На основе решения Мигеля Прж я заменил «cmd» на «<=>». Это важно для чисел. Если используется CMP, то сортировка будет работать как строка (цифры) - первый символ наиболее важен, затем второй и так далее. Если у вас есть числа: 607, 8 и 35, то CMP отсортирует их как: 8, 607, 35. Чтобы отсортировать их как числа, мы используем метод «<=>», и в результате получим: 607, 35, 8

use strict;

open my $FILE, '<', 'phonebook';
my @lines = <$FILE>;

my @sorted = sort { 
                my @a = split(/\s+/,$a); 
                my @b = split(/\s+/,$b); 
                $b[1] <=> $a[1] } @lines;

foreach my $item(@sorted) {
    print "$item\n";
}

close $FILE;
2 голосов
/ 05 марта 2011

Если вы не возражаете против использования оболочки, sort -r -k2 отсортирует ваш файл в обратном порядке.

0 голосов
/ 06 марта 2011

Я удивлен, что никто не упомянул об этом, но если мы сортируем телефонную книгу, нам, скорее всего, не нужна чистая сортировка ASCII.

Действительно ли Bob DeCarlo принадлежит раньше Ralph Dearborn? Если вы сортируете с помощью cmp, то в результатах сначала появляется мистер ДеКарло.

Даже если вы нормализуетесь для случая, у вас все еще есть проблемы. Существует множество сложностей с сортировкой и хранением вещей. Различные организации имеют правила для обработки этих проблем.

Поскольку сортировка является дорогостоящей операцией, вам нужно, чтобы каждое сравнение работало как можно быстрее. Способ сделать это - использовать самый простой код для всех ваших сравнений. Поскольку cmp сам по себе не даст желаемого результата, нам нужно сгенерировать и кэшировать нормализованный термин сортировки для каждого элемента в телефонной книге.

Итак, если вы уже получили данные телефонной книги в массиве:

sub extract_and_normalize {
     # Do stuff here to embody your alphabetization rules.

     return [ $normed, $line ];   
}

# Generate your sort terms
my @processed = map extract_and_normalize($_), @lines;

# Sort by the normalized values
my @sorted = sort {$a->[0] cmp $b->[0]}, @processed;

# Extract the lines from the sorted set.
@lines = map $_->[1], @sorted;

Или используйте преобразование Шварца, как предлагает hobbs, чтобы избежать создания всех промежуточных переменных:

@lines = map $_->[1],
         sort { $a->[0] cmp $b->[0] }
         map extract_and_normalize($_), @lines;
0 голосов
/ 05 марта 2011

Я думаю, что интересно писать в современном Perl (решение то же самое), и это полный сценарий:

use strict;

open my $FILE, '<', 'phonebook';
my @lines = <$FILE>;

my @sorted = sort { 
                my @a = split(/\s+/,$a); 
                my @b = split(/\s+/,$b); 
                $b[1] cmp $a[1] } @lines;

foreach my $item(@sorted) {
    print "$item\n";
}

close $FILE;
0 голосов
/ 05 марта 2011

Вам нужно будет прочитать файл построчно, чтобы сделать это. Примерно так:

my %list;
open(FILE, phonebook);
while(<FILE>){
    my @vals = split(/:/, $_);
    (my $key = $vals[0]) =~ s/(\S+)\s+(.+)/$2 $1/; # split first field, reverse word order
    $list{$key} = $_; #save row keyed on $key
}

foreach my $key(sort {$b cmp $a} keys(%list)){
    print $list{$key};
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...