Pular para o conteúdo principal

Postagem em destaque

BlackTDN :: LeetCode :: Comparando Implementações Harbour e TLPP para o Desafio Longest Palindromic Substring

_Créditos das imagens: ChatGPT_ ### LeetCode :: Comparando Implementações Harbour e TLPP para o Desafio Longest Palindromic Substring Resolver o problema do [Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/description/) é um exercício clássico de programação, que desafia desenvolvedores a encontrar a maior substring palindrômica dentro de uma string. Recentemente, exploramos soluções tanto em Harbour quanto em TLPP (Total Language Protheus Programming). Neste artigo, comparamos as implementações nessas duas linguagens, destacando suas semelhanças, diferenças e funcionalidades específicas. #### Implementações em Harbour ##### Versão 5.1 Essa solução utiliza a técnica de expansão a partir do centro do palíndromo. Cada caractere ou par de caracteres consecutivos é considerado um possível "centro". O algoritmo expande em ambas as direções enquanto os caracteres forem iguais, retornando o maior palíndromo encontrado. ##### Versão 5....

Protheus :: Otimizando o Relatório FINR550 ( Razonete de Contas Correntes )

Mais uma vez, e a pedido de minha querida amiga Carla Soneta, tive a missão de tentar otimizar o programa FINR550 que gera o relatório Razonete de Contas Correntes. A missão pareceu-me um desafio e aceitei (principalmente por não ser a minha área de negócios). Para que isso fosse possível primeiro eu precisaria do original do FINR550.prx mais atual (uma coisa boa, a Totvs/Microsiga fornece os fontes dos relatórios). Com o FINR550.prx em mãos começou a análise para verificar como otimizá-lo sem alterar sua lógica, estrutura e regras. Pude identificar, Considerando que o cliente que solicitou a otimização utiliza o SGBD DB2 e que possui a "Localização" apenas para o País Brasil, os seguintes pontos:
  1. Chamadas de funções desnecesárias;
  2. Resolver xFilial();
  3. Prefixar os campos e funções de db com seus respectivos Alias;
  4. Resolver "Localização";
  5. Resolver compilação condicional;
  6. Retirar parte do código específica para CodeBase;
  7. Armazenar retorno de funções em variáveis locais e reaproveitá-las quando necessário;
  8. Substituir a chamada a MsSeek() por dbSeek(); e
  9. Retirar a parte de relatório personalizável (tReport).

Vamos ao que interessa:

1. Chamadas de funções desnecesárias
Notei que o código original possuia várias chamadas a dbSelectArea(), xFilial(), dbSetOrder(), etc dentro do laço While/End While. Sendo assim elas foram retiradas. E tratamento de campos e funções de db efetuados conforme item 3.

Notei que chamavam Abs() várias vezes para um mesmo valor, então o número de chamadas a Abs() foi reduzido armazenando o conteúdo em uma variável conforme ítem 7.

Pude verificar que xFilial() era usada dentro de Laço. Então a chamada foi retirada do laço e xFilial() foi resolvida conforme ítem 2.


2. Resolver xFilial()
xFilial() é uma função em Advpl() para retornar a filial correte para uma determinada tabela conforme modo de acesso definido no dicionário de Tabelas ( o SX2). Se a Tabela estiver compartilhada entre filiais xFilial() retornará brancos, caso contrário, retornará a Filial armazenada na variável de ambiente setada com o conteúdo da filial correte. É uma péssima prática resolver xFilial() dentro de um laço, seja ele qual for ( While, For, etc.). Então para cada alias em que xFilial() precisava ser resolvido criei uma variável para esse fim e usei variável no lugar de xFilial como em:

Local cSA1Filial := xFilial("SA1")
Local cSA2Filial := xFilial("SA2")
Local cSE1Filial := xFilial("SE1")
Local cSE2Filial := xFilial("SE2")
Local cSE5Filial := xFilial("SE5")
Local cSX5Filial := xFilial("SX5")

e Depois usando:

While SE1->( !Eof() )

If mv_par18 == 1 // Seleciona clientes por conta contabil
SA1->( dbSeek(cSA1Filial+SE1->(E1_CLIENTE+E1_LOJA) ) )
If SA1->( A1_CONTA <> mv_par20 )
SE1->( dbSkip() )
Loop
Endif
Endif

ao ivés de

If mv_par18 == 1 .And. !(TcGetDb() $ "MSSQL/MSSQL7/ORACLE") // Seleciona clientes por conta contabil
dbSelectArea("SA1")
MsSeek(xFilial()+SE1->E1_CLIENTE+SE1->E1_LOJA)
If SA1->A1_CONTA <>A1_CONTA > mv_par20
dbSelectArea("SE1")
dbSkip()
Loop
Endif
Endif

3. Prefixar os campos e funções de db com seus respectivos Alias

Para evitar chamadas desnecessárias à função dbSelectArea() prefixei todos os campos com seus respectivos Alias.

Onde estava, por exemplo:

dbSelectArea("SE1")

IF E1_TIPO $ MVRECANT

dbSkip()

ENDIF

Alterei para

IF SE1->E1_TIPO $ MVRECANT

SE1->( dbSkip() )

EndIF

Onde estava:

dbSelectArea("SE1")

dbSetOrder(1)

Alterei para:

SE1->( dbSetOrder(1) )

Onde estava:

dbSelectArea("SE1")

...

dbSkip()

Alterei para:

SE1->( dbSkip() )

e assim por diante.


4. Resolver "Localização"

Para que testar se o País Local é o Brasil se sei que o código só será utilizado aqui. Então para deixa-lo mais enxuto e evitar testes desnecessários, retirei toda parte do código que não se referiam ao cPaisLoc == "BRA".

5. Resolver compilação condicional

Sabendo que o SGBD a ser utilizado seria o DB2, toda parte do código específica para CodeBase e/ou AS400() ou outro SGBD foi retirada.

6. Retirar parte do código específica para CodeBase

Idem ao item 5 e considerando que a parte retirada estava diretamente relacionada ao tratamento já dado à "View" retornada pela "Query".

7. Armazenar retorno de funções em variáveis locais e reaproveitá-las quando necessário
Tinha algo como:

nSaldoAtu -= ABS(cNomeArq->VALOR)
nTotDeb += ABS(cNomeArq->VALOR)
nTotDebG += ABS(cNomeArq->VALOR)
nSalAtuG -= ABS(cNomeArq->VALOR)

Oberve que ABS(cNomeArq->VALOR) era chamada 4 vezes para retornar o valor absoluto de um mesmo campo e armazena-la em variáveis diferentes. Imagine isso dentro de um Laço executado 1000 vezes. ABS seria executada 4000 vezes desnecessáriamente. Para solucionar o problema fiz o seguinte:

nABSValor := Abs(cNomeArq->VALOR)
nSaldoAtu -= nABSValor
nTotDeb += nABSValor
nTotDebG += nABSValor
nSalAtuG -= nABSValor
Observe que agora Abs(), se o laço for executado 1000 vezes só será executado 1000 vezes.

8. Substituir a chamada a MsSeek() por dbSeek(); e

MsSeek() é uma função "bufferizada" criada em Advpl para evitar o reposicionamento do "Ponteiro" do Banco de Dados quando este já estiver posicionado na chave que se deseja procurar. É útil, por exemplo, se usou a função de pesquisa dbSeek() para pesquisar e depois quer saber se o registro está posicionado evitando ter que pesquisa-lo novamente. Ai sim MsSeek() é útil, caso contrário é um desperdício, uma vez que MsSeek() faz vários tratamentos para verificar se a chave atual é a que procura e, se não for, irá chamar dbSeek() para posicionar.

9. Retirar a parte de relatório personalizável (tReport).

Considerando que o cliente não utiliza o FINR55o personalizável, a parte do código foi retirada para facilitar a manutenção do código.

Bem, é isso o que tinha a dizer sobre otimização do códgo FINR550 (baseado no código padrão). Para consultar na íntegra as alterações feitas no código e poder comparar com o original da microsiga utilizado como base para a otimização clique aqui

Utilize o apdiff ou outra ferramenta para comparar os códigos e visualizar as alterações.

Obs.: Sempre que for otimizar um código do padrão obtenha a última versão disponível junto a Totvs/Microsiga. Lembrando que a Totvs/Microsigadisponibiliza os fontes de Relatórios.

[]s

иαldσ

Comentários

  1. Boa noite Naldo.

    Muito bom, parabéns.

    Att.,

    Talvane (Arapiraca/AL)

    ResponderExcluir
  2. Bom dia Naldo, teria como voce me enviar o fonte do FINR550 otimizado??

    ResponderExcluir
  3. jfernandocv@gmail.com ou jfcvilela@sistemafieg.org.br

    ResponderExcluir
  4. Boa tarde,

    Teria como me enviar o fonte
    cleberfiscal23@gmail.com

    ResponderExcluir

Postar um comentário

Postagens mais visitadas