[SP-pm] Perl e horário de verão

Blabos de Blebe blabos at gmail.com
Fri Feb 28 09:28:20 PST 2014


Eu tava escrevendo uma resposta, mas o buss foi mais rápido então cortando
os overlaps, com bastante licença poética:

Primeiro as masturbações estilísticas:

> #!/usr/bin/perl

Eu prefiro usar:

#!/usr/bin/env perl

Porque, se o script for usado num ambiente onde espera-se outro perl que
não o do /usr/bin, como num perlbrew por exemplo, você não fica com a
versão antiga do perl hardcoded.

Mas no seu caso, não acredito que vá ocorrer esse caso de uso, então pode
ficar como está, numa boa. Não está errado.

> use strict;
> use warnings;

Bom garoto!

Ambas as pragmas são mandatórias, caso você esteja escrevendo algo
minimamente decente. Tem gente que gosta também do:

use diagnostics;

que te dá ainda mais informação em caso de problemas.

> use POSIX;

Aqui, a explicação longa é longa (call @garu), mas resumidamente, você está
importando alguns símbolos desnecessários automaticamente pro namespace
corrente.

Isso é completamente irrelevante pro seu caso, mas vc poderia fazer assim:

use POSIX ();

que significa, adicione o módulo POSIX mas não importe nenhum símbolo. Ou
ainda:

use POSIX qw{ strftime }

que significa, adicione o módulo POSIX e importe somente o símbolo (função)
strftime. Nesse caso, ao usar a sub, vc não precisaria mais prefixar com
POSIX::

Do meu ponto de vista, tanto faz:

use POSIX ();
POSIX::strftime();

ou

use POSIX qw( strftime );
strftime();


Eu também prefiro nomear_funcoes_em_minusculas_com_underscore do que
comCameCaseIgualAoJava.


***


Sobre a sua dúvida na captura e tratamento dos parâmetros tem algumas
sutiliezas que podem levar a erros.

*** Primeiramente, acessar elementos versus acessar slice de um array

A forma de acessar um escalar dentro de um array é indicando que ele é um
escalar, e dizendo qual índice você quer, ou seja, para acessar o terceiro
elemento do array foo, temos:

my $elemento = $foo[ 2 ];

que significa mais ou menos, pegue o escalar ($) que está no array foo na
posição 3 (índice 2).

Quando você faz:

$a = @_[3];

Você está pedindo, me dê um array (@) contendo os elementos que estão "nos
índices" 3 do array _. Isso é um slice, que normalmente é usado pra pegar
pedaços de array.

No seu caso, como você tá pedidndo uma lista com apenas um elemento, também
funciona mas experimente testar o seguinte:

my $a = @_[1,2];


Dito isso, quando vc faz:

$foo = @_[0], a menos que você saiba exatamente o que está fazendo, isso
está errado. Não é questão de estilo aqui. O correto seria:

$foo = $_[0];  ## Me dê o escalar que está no índice zero do array _


*** Acessando parâmetros de funções

Os parâmetros de funções são passados no array _, ou seja, @_.

Ele faz aliasing das variáveis passadas, ou seja, ao lidar com $_[]
diretamente, você pode causar efeitos colaterais externos inesperados.

Por exemplo, nesse oneliner apenas para efeitos didáticos:

perl  -e 'sub foo { $_[0]++ } $a=1; foo($a); print $a . $/'

Por isso é boa prática criar uma cópia local dos argumentos dentro da
função. Isso pode ser feito de varias maneiras, conforme o caso:


## Crie uma cópia do @_ chamada @args;
my @args = @_;


## Crie uma lista com dois escalares, atribua os dois primeiros elementos
do @_ a cada um deles e descarte o resto;
my ( $first, $second ) = @_;


## Crie uma lista contendo dois escalares, um array, guarde os dois
primeiros elementos de @_ nos escalares e o resto no array @tail
my ( $first, $second, @tail ) = @_


## Remova o primeiro elemento de @_ e coloque-o no escalar criado
my $first = shift;   ## Equivalente a my $first = shift @_;
my $second = shift;   # Pegue o "novo primeiro" elemento de @_

A vesão com shift remove definitivamente o primeiro elemento do array. O
segundo elemento passa a ser o primeiro, e assim sucessivamente. O tamanho
do array é reduzido em 1.

Minhas fontes não confiáveis apontam que a versão com shift é menos
eficiente. Por favor me corrijam quem estiver com as fontes corretas.

Eu prefiro usar as duas primeiras formas. my @args = @_;  ou my ( $first,
$second ) = @_;

***

Conforme o buss apontou, separe uma instrução por linha:

sub calcularDeslocamentoAno {
    my ( $ano ) = @_;

    die 'ano invalido'
      unless defined $ano;

    return $ano > 99 ? $ano - 1900 : $ano;
}

Veja como fica mais claro pro próximo cara que for dar manutenção nisso.

***

Use qx ao invés de ` pra invocar comandos externos

sub calcularDiaInicioHorarioVerao {
   my $dia_inicio = qx{ cal 10 2013 | tail -4 | head -1 | cut -d' ' -f7 };
}

Considerações finais:

1) Seu código está até bem razoável. Compartimentarizado em funções que só
fazem uma coisa. Ele está claro no que ele pretende fazer. Isso é muito
bom. Té melhor que muito programador "experiente" que tem por aí.

2) Tem uns requintes de Perl 4, mas fazendo os ajustes indicados, que não
são nada complicados, seu código vai rejuvenecer uns 20 anos!!!

3) Não é porque você executa comandos do Shell que o seu código Perl fica
ruim. Nada disso. É parte da linguagem e perfeitamente normal,
principalmente, porque você deixa bem claro o que você está fazendo e de
forma organizada. Acessar o shell continua sendo elegante.

Feio é fazer algazarra.

Existem módulos pra rodar comandos externos, com mil funcionalidades, mas
acredito que não são necessários no seu caso.

4) DateTime, pode se mais indicado do que calcular datas na mão, mas ele
não é "Core" (http://perldoc.perl.org/corelist.html), portanto pode ser
difícil de instalar no seu sistema com ambiente restrito. Você tem que
medir se o custo de instalar vale o benefício que ele fornece ou ainda se o
risco do seu código falhar é alto ou baixo. Isso é uma análise sua.

Eventualmente eu pelo menos comentaria no código que o DateTime poderia ser
uma solução mais adequada. Vai que o ambiente muda no futuro e ele possa
ser instalado mais facilmente.

5) Tente escrever testes automatizados que validem a sua aplicação.



Bom, espero que tenhamos te ajduado. Qualquer coisa avisa.


[]'s









2014-02-28 13:31 GMT-03:00 Bruno Buss <bruno.buss em gmail.com>:

> Oi Geraldo,
>
> Respostas inline :-)
>
>
> 2014-02-28 11:59 GMT-03:00 Geraldo Netto <geraldonetto em gmail.com>:
>
> Bom Dia Pessoal!
>>
>> Consegui evoluir no código com a api do posix mesmo
>> crio as datas com POSIX::strftime
>> e calculo o horário de verão pegando a saida do comando cal no shell
>>
>> yep, eu sei que é feito, mas resolve o meu problema de um jeito rápido...
>> por hora, a idéia é só gerar o "okay" da gerencia p/ matar umas 4
>> versões shell que fazem o mesmo trabalho, mas precisam de alguns
>> parâmetros diferentes e alguma intervenção humana especificamente no
>> período de horário de verão
>>
>
> Thumbs up por resolver o problema! :D
>
> Entretanto, o que a galera quer passar aqui, e' que mesmo que você
> realmente acredite que esta resolvendo seu problema de forma simples e
> rápida agora... não esta'. Ou melhor, o jeito rápido agora *provavelmente*
> ira' te causar uma baita dor de cabeça soon(tm).
>
> Tente utilizar o DateTime, e' uma solução ordens de grandeza melhor e não
> e' tao difícil assim :-)
>
>
>>
>> a minha dúvida é primária, suponho...
>> mas eu não entendi o porque usar o shift
>> se eu posso verificar com o defined() se o @_[<número>] está definido?
>>
>
> O shift serve para voce retirar e retornar o 1o elemento da lista de
> argumentos/parâmetros que foi passada para sua função.
>
> E' uma das formas idiomáticas de se fazer isso, a outra seria "my ($v_a,
> $v_b, $v_c) = @_;".
> Pessoalmente prefiro com shift :-)
>
>
>>
>> Aceito sugestões e Mais uma vez, Valeu mesmo pela força! :P
>>
>>
>> BTW, código todo a seguir:
>>
>> #!/usr/bin/perl
>>
>> use strict;
>> use warnings;
>>
>> use POSIX;
>>
>>
>> # a inicia a data em 1900 entao, se for passado 95 => 1995
>> sub calcularDeslocamentoAno {
>>     die "ano invalido" unless defined(@_[0]) && return ($_[0] > 99 ?
>> $_[0] - 1900 : $_[0]);
>> }
>>
>
> Então para você o ano 50 e' o mesmo que o ano 1950? :P
> Ou se eu passar para você o ano 1800, ele retorna -100.
>
> Outra coisa, você realmente não precisa colocar o return na mesma linha do
> die...
> Por exemplo:
>
> sub calcularDeslocamentoAno{
>     my $ano = shift;
>
>     #Rejeitando coisas que nossa funcao nao sabe tratar.
>     die "ano invalido" unless defined $ano && $ano >= 1900;
>
>     return $ano - 1900;
> }
>
>
>>
>> sub criarData {
>>     die "parametros invalidos" unless (defined(@_[0]) &&
>> defined(@_[1]) && defined(@_[2]));
>>     #Usage: POSIX::strftime(fmt,             sec, min, hour, mday,
>> mon, year, wday = -1, yday = -1, isdst = -1)
>>     return POSIX::strftime("%Y,%m,%d", 0, 0, 0, @_[0], (@_[1] -1),
>> &calcularDeslocamentoAno(@_[2]), 0, 0);
>> }
>>
>
> Idealmente você estaria tratando:
> * Se o 1o parâmetro e' um dia que esta' no mês (posso tentar usar stftime
> passando o dia 31 de Fev?)
> * Se o 1o parâmetro e' um dia valido at all (dia igual a 44 por exemplo).
> * Lembrar que o dia e o mês são 0-based, ou seja Janeiro => 0, Fev => 1,
> etc.
> ...
>
> Voce tambem poderia fazer:
> sub criarData {
>     my ($dia, $mes, $ano) = @_;
>     die "parametros invalidos" unless defined $dia   &&
>                                                      defined $mes &&
>                                                      defined $ano;
>     ...
> }
>
> Detalhe: O DateTime tem funções de formatação de data tao boas quanto ;-)
>
>
>>
>> sub calcularDiaInicioHorarioVerao {
>>     my $dia_inicio = `cal 10 2013 | tail -4 | head -1 | cut -d' ' -f7`;
>> }
>>
>> sub calcularDiaFimHorarioVerao {
>>     my $dia_fim = `cal 2 2014 | tail -4 | head -1 | cut -d' ' -f7`;
>> }
>>
>
> Alguém já disse em substituir isso tudo por DateTime? :P
>
>
>>
>> sub  trim {
>>     my $s = shift;
>>     $s =~ s/^\s+|\s+$//g;
>>     return $s;
>> }
>>
>> print &criarData(2, 5, 2014);
>> print &trim($dia_inicio);
>> print &trim($dia_fim);
>>
>
> Não sei exatamente qual Perl você ta usando, mas acredito que não e'
> necessário prefixar as chamadas de funções com &. Inclusive isso e'
> considerado não legal hoje em dia :-)
>
> [ ]'s
>
> PS: Utilize uma ferramenta tipo pastebin ou Gist (do GitHub) para copiar e
> enviar codigos em listas de e-mail :-)
>
>
>>
>> Geraldo Netto
>> Sapere Aude => Non dvcor, dvco
>> São Paulo, Brasil, -3gmt
>> site: http://exdev.sf.net/
>>
>> 2014-02-26 23:41 GMT-03:00 Geraldo Netto <geraldonetto em gmail.com>:
>> > Opa!
>> >
>> > Tudo okay Pessoal?
>> >
>> > Eu sou o Geraldo, um ghost reader da SPPM (e algumas outras PMs)
>> > e eu ando trabalhando vagarosamente num projetinho
>> > que eu dependo de verificação do horário de verão
>> >
>> > A idéia é separar o ano em 3 partes:
>> >
>> > fim do horário de verão (terceiro domingo de fevereiro)
>> > fora do horário de verão
>> > início do horário de verão (terceiro sábado de fevereiro)
>> >
>> > E baseado nisso, lançar um shell que vai receber o timezone, data de
>> > início e de fim
>> > p/ fazer a coleta de dados pelo hp openview (comando ovpmbatch)
>> >
>> > Só que...
>> > -eu tenho um servidor tru64 (sem suporte)
>> > -rodando perl 5.8 (2002 pelo o que o version diz...)
>> > -sem sudo/root/possibilidade de instalação de pacotes de terceiros
>> > -<inclua-aqui-todas-as-limitações-que-vocês-imaginarem-de-infra>
>> > -meu "core business" é análise de desempenho de software,
>> > então o código não pode ser muito "perlish"
>> > porque alguém pode precisar dar manutenção depois de mim
>> >
>> > Embora eu ainda não tenha código real (só comecei a brincar)
>> > Eu estou pensando em ir com a api do posix, especificamente a
>> > função/método strftime
>> >
>> > Eventualmente vocês teriam alguma sugestão?
>> >
>> >
>> > Grande Abraço!
>> >
>> > Geraldo Netto
>> > Sapere Aude => Non dvcor, dvco
>> > São Paulo, Brasil, -3gmt
>> > site: http://exdev.sf.net/
>> =begin disclaimer
>>    Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
>>  SaoPaulo-pm mailing list: SaoPaulo-pm em pm.org
>>  L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
>> =end disclaimer
>>
>
>
>
> --
> Bruno C. Buss
> http://www.brunobuss.net
>
>
> 2014-02-28 11:59 GMT-03:00 Geraldo Netto <geraldonetto em gmail.com>:
>
> Bom Dia Pessoal!
>>
>> Consegui evoluir no código com a api do posix mesmo
>> crio as datas com POSIX::strftime
>> e calculo o horário de verão pegando a saida do comando cal no shell
>>
>> yep, eu sei que é feito, mas resolve o meu problema de um jeito rápido...
>> por hora, a idéia é só gerar o "okay" da gerencia p/ matar umas 4
>> versões shell que fazem o mesmo trabalho, mas precisam de alguns
>> parâmetros diferentes e alguma intervenção humana especificamente no
>> período de horário de verão
>>
>> a minha dúvida é primária, suponho...
>> mas eu não entendi o porque usar o shift
>> se eu posso verificar com o defined() se o @_[<número>] está definido?
>>
>> Aceito sugestões e Mais uma vez, Valeu mesmo pela força! :P
>>
>>
>> BTW, código todo a seguir:
>>
>> #!/usr/bin/perl
>>
>> use strict;
>> use warnings;
>>
>> use POSIX;
>>
>>
>> # a inicia a data em 1900 entao, se for passado 95 => 1995
>> sub calcularDeslocamentoAno {
>>     die "ano invalido" unless defined(@_[0]) && return ($_[0] > 99 ?
>> $_[0] - 1900 : $_[0]);
>> }
>>
>> sub criarData {
>>     die "parametros invalidos" unless (defined(@_[0]) &&
>> defined(@_[1]) && defined(@_[2]));
>>     #Usage: POSIX::strftime(fmt,             sec, min, hour, mday,
>> mon, year, wday = -1, yday = -1, isdst = -1)
>>     return POSIX::strftime("%Y,%m,%d", 0, 0, 0, @_[0], (@_[1] -1),
>> &calcularDeslocamentoAno(@_[2]), 0, 0);
>> }
>>
>> sub calcularDiaInicioHorarioVerao {
>>     my $dia_inicio = `cal 10 2013 | tail -4 | head -1 | cut -d' ' -f7`;
>> }
>>
>> sub calcularDiaFimHorarioVerao {
>>     my $dia_fim = `cal 2 2014 | tail -4 | head -1 | cut -d' ' -f7`;
>> }
>>
>> sub  trim {
>>     my $s = shift;
>>     $s =~ s/^\s+|\s+$//g;
>>     return $s;
>> }
>>
>> print &criarData(2, 5, 2014);
>> print &trim($dia_inicio);
>> print &trim($dia_fim);
>>
>> Geraldo Netto
>> Sapere Aude => Non dvcor, dvco
>> São Paulo, Brasil, -3gmt
>> site: http://exdev.sf.net/
>>
>> 2014-02-26 23:41 GMT-03:00 Geraldo Netto <geraldonetto em gmail.com>:
>> > Opa!
>> >
>> > Tudo okay Pessoal?
>> >
>> > Eu sou o Geraldo, um ghost reader da SPPM (e algumas outras PMs)
>> > e eu ando trabalhando vagarosamente num projetinho
>> > que eu dependo de verificação do horário de verão
>> >
>> > A idéia é separar o ano em 3 partes:
>> >
>> > fim do horário de verão (terceiro domingo de fevereiro)
>> > fora do horário de verão
>> > início do horário de verão (terceiro sábado de fevereiro)
>> >
>> > E baseado nisso, lançar um shell que vai receber o timezone, data de
>> > início e de fim
>> > p/ fazer a coleta de dados pelo hp openview (comando ovpmbatch)
>> >
>> > Só que...
>> > -eu tenho um servidor tru64 (sem suporte)
>> > -rodando perl 5.8 (2002 pelo o que o version diz...)
>> > -sem sudo/root/possibilidade de instalação de pacotes de terceiros
>> > -<inclua-aqui-todas-as-limitações-que-vocês-imaginarem-de-infra>
>> > -meu "core business" é análise de desempenho de software,
>> > então o código não pode ser muito "perlish"
>> > porque alguém pode precisar dar manutenção depois de mim
>> >
>> > Embora eu ainda não tenha código real (só comecei a brincar)
>> > Eu estou pensando em ir com a api do posix, especificamente a
>> > função/método strftime
>> >
>> > Eventualmente vocês teriam alguma sugestão?
>> >
>> >
>> > Grande Abraço!
>> >
>> > Geraldo Netto
>> > Sapere Aude => Non dvcor, dvco
>> > São Paulo, Brasil, -3gmt
>> > site: http://exdev.sf.net/
>> =begin disclaimer
>>    Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
>>  SaoPaulo-pm mailing list: SaoPaulo-pm em pm.org
>>  L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
>> =end disclaimer
>>
>
>
>
> --
> Bruno C. Buss
> http://www.brunobuss.net
>
> =begin disclaimer
>    Sao Paulo Perl Mongers: http://sao-paulo.pm.org/
>  SaoPaulo-pm mailing list: SaoPaulo-pm em pm.org
>  L<http://mail.pm.org/mailman/listinfo/saopaulo-pm>
> =end disclaimer
>
>
-------------- Pr�xima Parte ----------
Um anexo em HTML foi limpo...
URL: <http://mail.pm.org/pipermail/saopaulo-pm/attachments/20140228/36d7a526/attachment-0001.html>


More information about the SaoPaulo-pm mailing list