Qual é a maneira Perlish de iterar do item n até o final de uma matriz?
-
19-09-2019 - |
Pergunta
O problema é que tenho n argumentos de linha de comando.Sempre haverá pelo menos 2, porém o número máximo é ilimitado.O primeiro argumento especifica um modo de operação e o segundo é um arquivo a ser processado.Do terceiro ao enésimo são as coisas a serem feitas no arquivo (que podem ser nenhuma, já que o usuário pode querer apenas limpar o arquivo, o que é feito se você passar apenas 2 argumentos).
Estou analisando os métodos disponíveis em Perl para trabalhar com matrizes, mas não tenho certeza de qual é a maneira "Perlish" de iterar do item 3 até o final da minha matriz.
Algumas opções que vi:
- Pop do final da matriz até encontrar um elemento que não comece com "-" (já que o caminho do arquivo não começa com "-", embora eu suponha que poderia, o que pode causar problemas).
- Mude a matriz duas vezes para remover os dois primeiros elementos.Tudo o que resta, posso simplesmente iterar, se o tamanho for pelo menos 1.
Gosto da segunda opção, mas não sei se é Perlish.E como estou tentando aprender Perl, é melhor aprender a maneira correta de fazer as coisas em Perl.
Solução
Além de usar o módulo GETOpt como Sinan escreveu, eu provavelmente iria com:
my ( $operation, $file, @things ) = @ARGV;
E então você pode:
for my $thing_to_do ( @things ) {
...
}
Outras dicas
IMHO, a maneira perlish de realizar o que você precisa seria usar um dos Módulos Getopt no CPAN.
Se você ainda quiser fazer isso manualmente, eu iria para a segunda opção (é semelhante à maneira como lidamos com o primeiro argumento de uma chamada de método):
die "Must provide filename and operation\n" unless @ARGV >= 2;
my $op = shift @ARGV;
my $file = shift @ARGV;
if ( @ARGV ) {
# handle the other arguments;
}
Eu poderia altamente recomendo usar Obteropt::Longo para analisar argumentos de linha de comando.É um módulo padrão, funciona muito bem e facilita exatamente o que você está tentando fazer.
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) {
...
}
Isso permite que você tenha um nome de opção longo e preencha automaticamente as informações em variáveis para você, além de testá-las.Ele ainda permite adicionar comandos extras posteriormente, sem precisar fazer nenhuma análise extra dos argumentos, como adicionar uma opção de 'versão' ou 'ajuda':
# 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 } );
Então, você pode invocá-lo na linha de comando usando -
, --
, a primeira letra ou a opção inteira e GetOptions
descobre tudo para você.Isso torna seu programa mais robusto e fácil de entender;é mais "adivinhável", você poderia dizer.A melhor parte é que você nunca precisa alterar o código que processa @ARGV
, porque GetOptions
cuidará de toda essa configuração para você.
A maneira mais padrão de fazer as coisas em Perl é através da CPAN.
Então minha primeira escolha seria GetOpt :: Long. Há também um tutorial sobre Devshed: Processando opções de linha de comando com Perl
Você pode usar um fatiar Para extrair o 2º. Para último itens, por exemplo:
[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:~]$
No entanto, você provavelmente deveria estar usando shift
(ou até melhor, GetOpt::Long
)
A resposta Deepesz é um bom caminho a percorrer.
Também não há nada de errado com sua segunda opção:
my $op = shift; # implicit shift from @ARGV
my $file = shift;
my @things = @ARGV;
# iterate over @things;
Você também pode pular a cópia @ARGV
em @things
e trabalhar diretamente nele. No entanto, a menos que o script seja muito curto, muito simples e improvável que fique mais complexo ao longo do tempo, eu evitaria fazer muitos cortes.
Se você escolhe a abordagem de Deepsz ou esta é em grande parte uma questão de gosto.
Decidir o que é melhor é realmente uma questão de filosofia. O ponto crucial da questão é se você deve modificar globais como @ARGV
. Alguns diriam que não é grande coisa, desde que seja feito de uma maneira altamente visível. Outros argumentariam a favor de sair @ARGV
intocado.
Não preste atenção a ninguém discutindo a favor de uma opção ou de outra devido a problemas de velocidade ou memória. o @ARGV
A matriz é limitada pela maioria das conchas a um tamanho muito pequeno e, portanto, nenhuma otimização significativa está disponível usando um método sobre o outro.
GetOpt :: Long, como foi mencionado também é uma excelente escolha.
Dê uma olhada Moosex :: getOpt Porque pode aguçar seu apetite por ainda mais coisas Moosey!.
Exemplo de 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
Produzirá o acima quando correr assim:
perl getopt.pl --Oper 1 -File File.txt --things Item1 --things Item2 -itens 3
Esses tipos de alces são verificados ... ./getopt --oper "not a number"
produz:
Value "not a number" invalid for option oper (number expected)
E de graça, você sempre recebe uma lista de uso ;-)
usage: getopt.pl [long options...] --file bit about file --oper op doc stuff --things
/I3az/
Para o caso mais geral com qualquer matriz:
for(my $i=2; $i<@array; $i++) {
print "$array[$i]\n";
}
Isso percorre a matriz, começando com o terceiro elemento (índice 2). Obviamente, o exemplo específico que você especifica, a resposta de Depesz é a mais direta e melhor.