Как Perlish способ итерации от элемента n до конца массива? - PullRequest
6 голосов
/ 05 октября 2009

Проблема в том, что у меня есть n аргументов командной строки. Там всегда будет как минимум 2, однако максимальное количество не ограничено. Первый аргумент определяет режим работы, а второй - файл для обработки. С 3-го по n-й - это то, что нужно сделать с файлом (который может быть нулевым, поскольку пользователь может просто захотеть очистить файл, что будет сделано, если вы просто передадите ему 2 аргумента).

Я смотрю на методы, доступные мне в Perl для работы с массивами, но я не уверен, что такое "Perlish" способ итерации от пункта 3 до конца моего массива.

Некоторые варианты, которые я видел:

  • Выскочить из конца массива, пока я не найду элемент, который не начинается с "-" (поскольку путь к файлу не начинается с "-", хотя я полагаю, что это может, что может вызвать проблемы).
  • Сдвиньте массив дважды, чтобы удалить первые два элемента. Все, что у меня осталось, я могу просто повторить, если его размер не меньше 1.

Мне нравится второй вариант, но я не знаю, это Perlish. И поскольку я пытаюсь изучать Perl, я мог бы также научиться правильно делать вещи в Perl.

Ответы [ 8 ]

17 голосов
/ 05 октября 2009

Помимо использования модуля Getopt, как писал Синан, я бы, вероятно, согласился с:

my ( $operation, $file, @things ) = @ARGV;

И тогда вы можете:

for my $thing_to_do ( @things ) {
...
}
12 голосов
/ 05 октября 2009

ИМХО, способ Perlish для достижения того, что вам нужно, это использовать один из модулей Getopt на CPAN .

Если вы все еще хотите сделать это вручную, я бы выбрал второй вариант (это аналогично тому, как мы обрабатываем первый аргумент вызова метода):

die "Must provide filename and operation\n" unless @ARGV >= 2;

my $op = shift @ARGV;
my $file = shift @ARGV;

if ( @ARGV ) {
    # handle the other arguments;
}
9 голосов
/ 05 октября 2009

Я бы очень рекомендовал использовать Getopt :: Long для анализа аргументов командной строки. Это стандартный модуль, он работает потрясающе и делает именно то, что вы пытаетесь сделать на одном дыхании.

use strict;
use warnings;
use Getopt::Long;

my $first_option = undef;
my $second_option = undef;

GetOptions ('first-option=s' => \$first_option, 
            'second-option=s' => \$second_option);

die "Didn't pass in first-option, must be xxxyyyzzz."
    if ! defined $first_option;
die "Didn't pass in second-option, must be aaabbbccc."
    if ! defined $second_option;

foreach my $arg (@ARGV) {
    ...
}

Это позволяет вам иметь длинное имя опции, автоматически вводит информацию в переменные для вас и позволяет вам проверить ее. Он даже позволяет добавлять дополнительные команды позже, без необходимости дополнительного разбора аргументов, например, добавление опции 'version' или 'help':

# adding these to the above example...
my $VERSION = '1.000';
sub print_help { ... }

# ...and replacing the previous GetOptions with this...
GetOptions ('first-option=s' => \$first_option, 
            'second-option=s' => \$second_option)
            'version' => sub { print "Running version $VERSION"; exit 1 },
            'help' => sub { print_help(); exit 2 } );

Затем вы можете вызвать его из командной строки, используя -, --, первую букву или всю опцию, и GetOptions выяснит все это за вас. Это делает вашу программу более надежной и легкой для понимания; это более "предположительно", вы могли бы сказать. Самое приятное то, что вам никогда не придется менять код, который обрабатывает @ARGV, потому что GetOptions позаботится обо всех этих настройках для вас.

7 голосов
/ 05 октября 2009

Самый стандартный способ работы в Perl - через CPAN.

Так что мой первый выбор будет Getopt :: Long . Существует также учебник по DevShed: Обработка параметров командной строки с помощью Perl

6 голосов
/ 05 октября 2009

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

[dsm@localhost:~]$ perl -le 'print join ", ", @ARGV[2..$#ARGV];' 1 2 3 4 5 6 7 8 9 10 00
3, 4, 5, 6, 7, 8, 9, 10, 00
[dsm@localhost:~]$ 

однако, вы, вероятно, должны использовать shift (или даже лучше, GetOpt::Long)

3 голосов
/ 05 октября 2009

Взгляните на MooseX :: Getopt , потому что это может разжечь ваш аппетит еще больше Moosey! .

Пример MooseX :: Getopt:

# getopt.pl

{
    package MyOptions;
    use Moose;
    with 'MooseX::Getopt';

    has oper   => ( is => 'rw', isa => 'Int', documentation => 'op doc stuff' );
    has file   => ( is => 'rw', isa => 'Str', documentation => 'about file' );
    has things => ( is => 'rw', isa => 'ArrayRef', default => sub {[]} );

    no Moose;
}

my $app = MyOptions->new_with_options;

for my $thing (@{ $app->things }) {
    print $app->file, " : ", $thing, "\n";
}

# => file.txt : item1
# => file.txt : item2
# => file.txt : item3

При запуске будет работать так:

perl getopt.pl --oper 1 - файл file.txt - вещи item1 - вещи item2 - вещи item3


Эти типы лося проверены ... ./getopt --oper "not a number" производит:

Value "not a number" invalid for option oper (number expected)

И бесплатно вы всегда получаете список использования; -)

usage: getopt.pl [long options...]
         --file         bit about file
         --oper         op doc stuff
         --things    

/ I3az /

3 голосов
/ 05 октября 2009

Deepesz ответ - один хороший способ пойти.

В вашем втором варианте тоже нет ничего плохого:

my $op     = shift; # implicit shift from @ARGV
my $file   = shift; 
my @things = @ARGV;

# iterate over @things;

Вы также можете пропустить копирование @ARGV в @things и работать непосредственно над ним. Однако, если сценарий не очень короткий, очень простой и вряд ли станет более сложным со временем, я бы избегал слишком большого количества коротких путей.

Выбираете ли вы подход Deepesz или этот - во многом вопрос вкуса.

Решение о том, что лучше, действительно является вопросом философии. Суть вопроса в том, следует ли вам модифицировать глобальные переменные, такие как @ARGV. Кто-то скажет, что это не так уж важно, если это сделано очень наглядно. Другие поспорили бы за то, чтобы оставить @ARGV нетронутым.

Не обращайте внимания на тех, кто выступает в пользу того или иного варианта из-за проблем со скоростью или памятью. Массив @ARGV ограничен большинством оболочек очень маленьким размером, и, следовательно, при использовании одного метода над другим существенная оптимизация недоступна.

Getopt :: Long , как уже упоминалось, также является отличным выбором.

0 голосов
/ 06 октября 2009

Для более общего случая с любым массивом:

for(my $i=2; $i<@array; $i++) {
    print "$array[$i]\n";
}

Это проходит через массив, начиная с третьего элемента (индекс 2). Очевидно, что конкретный пример, который вы указываете, ответ Депеса самый простой и лучший.

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