Каков наилучший способ выполнить итерацию от элемента n до конца массива?
-
19-09-2019 - |
Вопрос
Проблема в том, что у меня есть n аргументов командной строки.Их всегда будет как минимум 2, однако максимальное число не ограничено.Первый аргумент задает режим работы, а второй - файл для обработки.С 3-го по n-е - это то, что нужно сделать с файлом (чего может и не быть, поскольку пользователь может просто захотеть очистить файл, что будет сделано, если вы просто передадите ему 2 аргумента).
Я смотрю на доступные мне методы в Perl для работы с массивами, но я не уверен, что такое "Perlish" способ итерации от элемента 3 до конца моего массива.
Некоторые варианты, которые я видел:
- Просматривайте массив с конца, пока я не найду элемент, который не начинается с "-" (поскольку путь к файлу не начинается с "-", хотя я полагаю, что это возможно, что может вызвать проблемы).
- Дважды сдвиньте массив, чтобы удалить первые два элемента.Все, что у меня осталось, я могу просто повторить, если его размер равен хотя бы 1.
Мне нравится второй вариант, но я не знаю, подходит ли он по-английски.И поскольку я пытаюсь выучить Perl, я мог бы с таким же успехом научиться правильно делать что-то на Perl.
Решение
Помимо использования модуля Getopt, как писал Синан, я бы, вероятно, выбрал:
my ( $operation, $file, @things ) = @ARGV;
И тогда вы сможете:
for my $thing_to_do ( @things ) {
...
}
Другие советы
ИМХО, лучшим способом достижения того, что вам нужно, было бы использовать один из Модули 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;
}
Я бы так и сделал весьма рекомендую использовать Getopt::Длинный для синтаксического анализа аргументов командной строки.Это стандартный модуль, он работает потрясающе и с легкостью выполняет именно то, что вы пытаетесь сделать.
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) {
...
}
Это позволяет вам иметь длинное название опции, автоматически вводить информацию в переменные для вас и позволяет вам протестировать ее.Это даже позволяет вам добавлять дополнительные команды позже, без необходимости выполнять какой-либо дополнительный синтаксический анализ аргументов, например, добавлять опцию "версия" или "справка":
# 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
я позабочусь обо всех этих настройках за вас.
Самый стандартный способ делать что-либо в Perl - это через CPAN.
Так что моим первым выбором было бы Getopt::Длинный.Существует также учебное пособие по DevShed: Обработка параметров командной строки с помощью Perl
Вы можете использовать нарезать чтобы извлечь 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
)
глубокий ответ - это один из хороших путей.
Также нет ничего плохого в вашем втором варианте:
my $op = shift; # implicit shift from @ARGV
my $file = shift;
my @things = @ARGV;
# iterate over @things;
Вы также можете пропустить копирование @ARGV
в @things
и работайте непосредственно над этим.Однако, если сценарий не очень короткий, очень простой и вряд ли будет усложняться со временем, я бы не стал прибегать к слишком большому количеству сокращений.
Выберете ли вы подход deepesz или этот, во многом зависит от вкуса.
Решение о том, что лучше, на самом деле является вопросом философии.Суть проблемы заключается в том, следует ли вам изменять глобальные переменные, такие как @ARGV
.Кто-то сказал бы, что в этом нет ничего особенного, если это делается очень заметным образом.Другие высказались бы за то, чтобы уйти @ARGV
нетронутый.
Не обращайте внимания на тех, кто спорит в пользу того или иного варианта из-за проблем со скоростью или памятью.Тот Самый @ARGV
большинство оболочек ограничивают массив очень небольшим размером, и поэтому при использовании одного метода поверх другого существенная оптимизация невозможна.
Getopt::Длинный, как уже упоминалось, тоже является отличным выбором.
Пожалуйста, взгляните на MooseX::Getopt потому что это может разжечь ваш аппетит к еще большему количеству вещей Муси!.
Пример 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 --операция 1 --файл file.txt --элемент вещей 1 --элемент вещей 2 --элемент вещей 3
Эти типы лосей проверены... ./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/
Для более общего случая с любым массивом:
for(my $i=2; $i<@array; $i++) {
print "$array[$i]\n";
}
Это перебирает массив, начиная с третьего элемента (индекс 2).Очевидно, что в конкретном примере, который вы указали, ответ депеша является самым простым и лучшим.