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....

BlackTDN :: TwBrowse/ListBox On-Demand (por: Robson Luiz)


Oi Naldo, tudo bem cara?

Vivemos em uma época onde devemos superar expectativas e sempre tentar fazer algo diferente e inovador. Quando é dito diferente e inovador a idéia é surpreender, e é claro sempre fazendo mais por menos, seja lá o que for, mas devemos mostrar que o consumo é menor e a praticidade é benéfica. Neste contexto apresento uma solução que não criei, a idéia veio de um bate-papo relativo a melhoria de um processo e otimização de tempo, e depois de tanto procurar achei o que precisava. Logo implementei o que eu havia negociado, ajustei, coloquei alguns recursos a mais e aproveitei para fazer um modelo de exemplo e deixa-lo para vocês que nos acompanham no BlackTDN.

Criar uma consulta ou uma lista de dados por demanda, ou se preferirem, ListBox On-Demand. A necessidade é simples, mostrar apenas alguns dados e conforme o usuário for teclando <Page_Down> ou teclando seta para baixo a rotina vai atribuindo mais dados ao vetor do Listbox. Então, com esta idéia imaginem uma query que retorne seis mil registros, geralmente fazemos a leitura de todos estes registros e depois apresentamos no Listbox, correto? Isso causa espera demonstrando que a rotina é lenta. Contudo, nem sempre o usuário quer consultar ou analisar todos os dados, portanto a idéia é apresentar 'N', poucos, registros por página, e no buffer deixar mais alguns 'N' registros, por exemplo, no total será lido apenas os quarenta primeiros registros de sei lá quantos retornam no resultado total da query, daí em diante a rotina deverá alimentar o vetor do ListBox conforme o usuário for teclando <Page-Down> ou seta para baixo. Há um porém, se caso nesta lista inicial o usuário souber a chave do registro que deseja buscar, a rotina também deve contemplar esta necessidade, OK! Abaixo os detalhes do passo-a-passo da rotina.

Atenciosamente,

Robson Luiz rleg30@gmail.com
 

#Include 'Protheus.ch'
#DEFINE NMAXPAGE 50

//--------------------------------------------------------------------------
// Rotina | LbxOnDemand  | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para listar os registro da tabela SX5, porém por demanda.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
User Function LbxOnDemand()
Local nOpc := 0
Local aSay := {}
Local aButton := {}

Private cCadastro := "ListBox On-Demand"
aAdd(aSay,"Este ListBox mostra os 50 primeiros registros, porém na tabela SX5 existem")
aAdd(aSay,"muito mais registros. Ao navegar o cursor para baixo ou teclar Page Down,")
aAdd(aSay,"a rotina irá buscar mais 50 registros quando o buffer de 50 registros for alcançado.")
aAdd(aSay,"")
aAdd(aSay,"Clique em OK para prosseguir.")
aAdd(aButton, { 1,.T.,{|| nOpc := 1, FechaBatch() }})
aAdd(aButton, { 2,.T.,{|| FechaBatch()              }})
FormBatch( cCadastro, aSay, aButton )
If nOpc==1
ODConfig("SX5")
Endif
Return

//--------------------------------------------------------------------------
// Rotina | ODConfig     | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para configurar qual tabela, campos e índices.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODConfig(cAliasRef)
Local lRet := .F.
Local aCampos := {}
Local aIndices := {}
Local cWhere := ""
Do Case
Case cAliasRef == "SX5"
aCampos := {"X5_TABELA","X5_CHAVE","X5_DESCRI"}
aIndices:= {{"X5_TABELA+X5_CHAVE"},{"Tabela+Chave"}}
EndCase

lRet := ODShow(cAliasRef,aCampos,aIndices,cWhere)
Return lRet

//--------------------------------------------------------------------------
// Rotina | ODShow       | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para apresentar os dados em tela.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODShow(cAliasRef,aCampos,aIndices,cWhere)
Local cCmbIndice := ""
Local cPesq := Space(50)
Local cTrbName := "TMP"+cAliasRef
Local cTitle := ""
Local cBLine := ""
Local cSep := ""
Local cCadAnt := ""

Local nX := 0
Local nRecno := 0

Local lRet := .F.

Local bRet:= {|| lRet := .T.,nRecno := IIf(Len(oLstBx:aArray)>=oLstBx:nAt,ATail(oLstBx:aArray[oLstBx:nAt]),0),oDlg:End()}

Local aDados:= {}
Local aHeaders:= {}
Local oDlg
Local oPesq
Local oLstBx
DEFAULT cWhere := ""
//-------------------------------
// Remove o campo filial da lista
//-------------------------------
For nX := 1 to Len(aCampos)
If "_FILIAL" $ aCampos[nX]
ADel(aCampos,nX)
ASize(aCampos,Len(aCampos)-1)
Exit
Endif
Next nX
//------------------------
// Monta header do listbox
//------------------------
SX3->(DbSetOrder(2))
For nX := 1 to Len(aCampos)
SX3->(DbSeek(aCampos[nX]))
#IFDEF SPANISH
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITSPA)))
#ELSE
#IFDEF ENGLISH
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITENG)))
#ELSE
AAdd(aHeaders,AllTrim(Capital(SX3->X3_TITULO)))
#ENDIF
#ENDIF
Next nX
//-----------------
// Nome da pesquisa
//-----------------
SX2->(DbSetOrder(1))
SX2->(DbSeek(cAliasRef))
#IFDEF SPANISH
cTitle := ALLTRIM(SX2->X2_NOMESPA)
#ELSE
#IFDEF ENGLISH
cTitle := ALLTRIM(SX2->X2_NOMEENG)
#ELSE
cTitle := ALLTRIM(SX2->X2_NOME)
#ENDIF
#ENDIF
DEFINE MSDIALOG oDlg TITLE "Consulta" + " " + cTitle FROM 268,260 TO 642,796 PIXEL
//------------------
// Texto de pesquisa
//------------------
@ 17,2 MSGET oPesq VAR cPesq SIZE 219,9 COLOR CLR_BLACK PIXEL OF oDlg
//------------------------------------------
// Interface para selecao de indice e filtro
//------------------------------------------
@ 3,228 BUTTON "Filtrar" SIZE 37,12 PIXEL OF oDlg ACTION ;
(ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq))
@ 5,2 COMBOBOX cCmbIndice ITEMS aIndices[2] SIZE 220,010 PIXEL OF oDlg ON CHANGE ;
(ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq))
//-------------------------
// Invocar o objeto ListBox
//-------------------------
oLstBx := TWBrowse():New(30,3,264,139,Nil,aHeaders,,oDlg,,,,,,,,,,,,,,.T.)
oLstBx:bLDblClick := bRet
//--------------------------
// Botoes de ação do usuário
//--------------------------
DEFINE SBUTTON FROM 172,02 TYPE 1  ENABLE OF oDlg Action(Eval(bRet))
DEFINE SBUTTON FROM 172,35 TYPE 2  ENABLE OF oDlg Action(oDlg:End()) 
DEFINE SBUTTON FROM 172,68 TYPE 15 ENABLE OF oDlg Action(ODVisual(@oLstBx,cAliasRef))
//------------------------
// Carga inicial dos dados
//------------------------
ODSetArray(@oLstBx,@aDados,cWhere,cTrbName,aCampos,cAliasRef,cCmbIndice,aIndices,@oDlg,cPesq)
ACTIVATE MSDIALOG oDlg CENTERED
If Select(cTrbName) > 0
(cTrbName)->(DbCloseArea())
Endif
If lRet
DbSelectArea(cAliasRef)
DbGoTo(nRecno)
Endif
Return lRet

//--------------------------------------------------------------------------
// Rotina | ODSetArray   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para passar os dados do vetor para o objeto TwBrowse.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODSetArray(oLstBx,aDados,cWhere,cTrbName,aCampos,cAlias,cCmbIndice,aIndices,oDlg,cPesq)
Local cQuery := ""
Local cCpos := ""
Local cSep := ""
Local cChave := ""
Local cConcat := "+"
Local cLenPsq := "" 
Local cFiltro := ""
Local nX := 0 
Local cPrefx := PrefixoCpo(cAlias)
Local cOrder := ""
Local bLine := Nil
Local bNextReg := {|a,b,c,d,e| ODPageDown(@a,b,c,d,e)}
Local nTotReg := 0
Local nLenChave := 0
Local nInd := 0   
Local lFiltra := .F.
Local aCposAdd := AClone(aCampos)
Default cPesq := "" 
//-----------------------------------
// Remove espacos do texto pesquisado
//-----------------------------------
cPesq := AllTrim(cPesq)
cLenPsq := AllTrim(Str(Len(cPesq)))
//----------------------------------------
// Verifica se deve ser feito algum filtro
//----------------------------------------
If !Empty(cPesq)
lFiltra := .T.
Endif
//-------------------------
// Define a ordem utilizada
//-------------------------
nInd := AScan(aIndices[2],cCmbIndice)
//--------------------------------
// Filtro de acordo com a pesquisa
//--------------------------------
If lFiltra
//----------------------------------------------------------------
// Define o simbolo de concatenacao de acordo com o banco de dados
//----------------------------------------------------------------
If Upper(TcGetDb()) $ "ORACLE,POSTGRES,DB2,INFORMIX"
cConcat := "||"
Endif
cChave   := Upper(aIndices[1][nInd])
cChave   := StrTran(cChave,cPrefX+"_FILIAL+","")
cChvOrig := cChave
cChave   := StrTran(cChave,cPrefX+"_",cAlias+"."+cPrefX+"_")
cChave   := StrTran(cChave,"DTOS","")
If cConcat <> "+"
cChave := StrTran(cChave,"+",cConcat)
Endif
//-------------------------------------------------------
// Verifica se a chave de busca nao eh maior que o indice
//-------------------------------------------------------
nLenChave := ODTamChave(cChvOrig)   
If nLenChave < Val(cLenPsq)
cLenPsq := AllTrim(Str(nLenChave))
cPesq := SubStr(cPesq,1,nLenChave)
Endif
//--------------------------------
// Concatena a expressao do filtro
//--------------------------------
If lFiltra
cFiltro += " AND SUBSTRING(" + cChave + ",1," + cLenPsq + ")= '"+cPesq+"' "
Endif
Endif
//--------------------------------------------
// Monta lista de campos para o objeto ListBox
//--------------------------------------------
SX3->(DbSetOrder(2))
cBLine := "{||{"
For nX := 1 To Len(aCampos)
cBLine += cSep + "oLstBx:aArray[oLstBx:nAt]["+AllTrim(Str(nX))+"]"
cSep := ","
Next nX
cBLine += "}}"
bLine := &(cBLine)
cSep := ""
//--------------------------
// Prepara e executa a query
//--------------------------
cQuery := ODQuery(NIL,cAlias,cFiltro,cTrbName,.T.)
cQuery := ChangeQuery( cQuery )
If Select(cTrbName) > 0
(cTrbName)->(DbCloseArea())
Endif
DbUseArea(.T., "TOPCONN", TCGenQry(,,cQuery), cTrbName,.T.,.T.)
(cTrbName)->(DbGoTop())
If (cTrbName)->(Eof())
MsgStop("Nenhum registro foi encontrado")
Else
//--------------------------
// Conta registros da tabela
//--------------------------
DbSelectArea(cTrbName)
DbGoTop()
While !Eof() .AND. nTotReg <= NMAXPAGE
nTotReg++
DbSkip()
End
DbGoTop()
aDados := ODPageDown(NIL,cTrbName,aCposAdd,NMAXPAGE,cAlias)
oLstBx:SetArray(aDados)
oLstBx:bLine := bLine
oLstBx:GoTop()
oLstBx:Refresh()
oDlg:Refresh()
If (nTotReg > NMAXPAGE)
oLstBx:bGoBottom := {||Eval(bNextReg,oLstBx,cTrbName,aCposAdd,NMAXPAGE,cAlias),oLstBx:NAT := EVAL( oLstBx:BLOGICLEN ) }
oLstBx:bSkip := {|NSKIP, NOLD, nMax| nMax:=EVAL( oLstBx:BLOGICLEN ),NOLD := oLstBx:NAT, oLstBx:NAT += NSKIP,;
oLstBx:NAT := MIN( MAX( oLstBx:NAT, 1 ), nMax ),Iif(oLstBx:nAt==nMax,;
Eval(bNextReg,oLstBx,cTrbName,aCposAdd,NMAXPAGE,cAlias),.F.),oLstBx:NAT - NOLD}
Endif
Endif
Return

//--------------------------------------------------------------------------
// Rotina | ODQuery      | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para efetuar a query.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODQuery(aVend,cAlias,cFiltro,cArquivo,lSoQuery)
Local aArea := GetArea()
Local cArqTmp := ""
Local cQuery := ""
Local cConcat := ""
DEFAULT cFiltro := ""
DEFAULT lSoQuery:= .F.
//----------------------------------------------------------------
// Define o simbolo de concatenacao de acordo com o banco de dados
//----------------------------------------------------------------
If Upper(TcGetDb()) $ "ORACLE,POSTGRES,DB2,INFORMIX"
cConcat := "||"
Else
cConcat := "+"
Endif
//-----------------------------------------
// Verificar qual tabela irá fazer a query.
//-----------------------------------------
If cArquivo == NIL
cArqTmp := "TRBSX5"
Else
cArqTmp := cArquivo
Endif
If Select(cArqTmp) > 0
(cArqTmp)->(DbCloseArea())
Endif
//----------------------------
// Efetuar e executar a query.
//----------------------------
If cAlias == "SX5"
SX5->(DbSetOrder(1))
cQuery := "SELECT DISTINCT X5_TABELA"+cConcat+"X5_CHAVE FROM " + RetSqlName("SX5") + " SX5 "
//---------------
// Clausula Where
//---------------
cQuery += "WHERE SX5.X5_FILIAL = '" + xFilial("SX5") + "'"
If !Empty(cFiltro)
cQuery += " " + cFiltro + " "
Endif
If TcSrvType() != "AS/400"
cQuery += " AND SX5.D_E_L_E_T_ = '' "
Else
cQuery += " AND SX5.@DELETED@ = '' "
Endif
cQuery += " ORDER BY X5_TABELA"+cConcat+"X5_CHAVE"
If !lSoQuery
cQuery := ChangeQuery(cQuery)
dbUseArea(.T.,"TOPCONN",TcGenQry(,,cQuery),cArqTmp,.T.,.T.)
(cArqTmp)->(DbGoTop())
Endif
Endif
RestArea(aArea)
Return cQuery

//--------------------------------------------------------------------------
// Rotina | ODPageDown   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para ler os dados na tabela conforme retorno da query. E
//        | fazer o controle de número de linhas lidas/visualizas pelo user.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODPageDown(oLstBx,cAlias,aCampos,nLimite,cAliasOri)
Local aLinha := {}
Local aDados := {}

Local nX := 0
Local nRegs := 0

Local cChaveInd := ""
Local cSep := ""
Local cChave := ""
(cAliasOri)->(DbSetOrder(1))
For nX := 1 To (cAlias)->(FCount())
If Type((cAlias)->(FieldName(nX))) == "C"
cChaveInd += cSep + (cAlias)->(FieldName(nX))
cSep := "+"
Endif
Next nX
While !(cAlias)->(Eof()) .And. nRegs <= nLimite
aLinha := {}
cChave := (cAlias)->&(cChaveInd)
(cAliasOri)->(DbSeek(xFilial(cAliasOri)+cChave))
For nX := 1 To Len(aCampos)
AAdd(aLinha,(cAliasOri)->&(aCampos[nX]))
Next nX
AAdd(aLinha,(cAliasOri)->(Recno()))
If oLstBx <> NIL
AAdd(oLstBx:aArray,aClone(aLinha))
Else
Aadd(aDados,aClone(aLinha))
Endif
nRegs++
(cAlias)->(DbSkip())
End
If oLstBx <> NIL
Return aClone(oLstBx:aArray)
Else
Return aDados
Endif
Return .F.

//--------------------------------------------------------------------------
// Rotina | ODVisual     | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para visualizar os dados na íntegra conforme o registro.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODVisual(oLstBx,cAlias)
Local aArea := GetArea()
Local nReg := 0
//---------------------------------------------------------------
// Tratamento para casos em que o filtro não exiba clientes no F3
//---------------------------------------------------------------
If Len(oLstBx:aArray) >= oLstBx:nAt
nReg := aTail(oLstBx:aArray[oLstBx:nAt])
SaveInter()
//-------------------------------------------------------
// Cria um aRotina basico para evitar quaisquer problemas
// com a rotina diferente deste padrao                  
//-------------------------------------------------------
aRotina := {{"Pesquisar","AxPesqui",0,1},{"Visualizar","AxVisual",0,2}}
DbSelectArea(cAlias)
DbGoTo(nReg)
AxVisual(cAlias,nReg,2)
RestInter()
Endif
RestArea(aArea)
Return

//--------------------------------------------------------------------------
// Rotina | ODTamChave   | Autor | Robson Luiz - Rleg    | Data | 18.02.2013
//--------------------------------------------------------------------------
// Descr. | Rotina para verificar se a chave de busca eh maior que o indice.
//--------------------------------------------------------------------------
// Uso    | Oficina de Programação
//--------------------------------------------------------------------------
Static Function ODTamChave(cChvOrig)
Local nTam := 0
Local nX := 0
Local aCpos := {}
cChvOrig := StrTran(cChvOrig,"DTOS")
cChvOrig := StrTran(cChvOrig,"(")
cChvOrig := StrTran(cChvOrig,")")
aCpos := StrToKArr(cChvOrig,"+")
For nX := 1 To Len(aCpos)
nTam += TamSX3(aCpos[nX])[1]
Next nX
Return nTam

Comentários

Postar um comentário

Postagens mais visitadas