CodeBlocks em Advpl – Parte 01

Introdução

Este tópico vai abordar exclusivamente de um recurso da Linguagem ADVPL, chamado CodeBlock, ou bloco de código. Trata-se de um recurso muito poderoso, com características interessantes e peculiares, muito utilizada em componentes de interface e algumas funções básicas da linguagem.

O que é

Um ClodeBlock nada mais é do que uma função “inline”, onde podem ser declarados parâmetros de entrada, e dentro dele podemos colocar uma ou mais expressões Advpl separadas por vírgulas, como por exemplo atribuições de valores em variáveis e chamadas de funções ou métodos.

A declaração de um CodeBlock obedece à seguinte sintaxe:

{ | [parm1][,parm2...N] | [expr1] [,expr2...N] }

Os parâmetros são declarados entre um par de pipes “||”. Caso o CodeBlock não receba parâmetros, usamos um par de pipes sem nada entre eles. As expressões ADVPL colocadas após o par de pipes devem ser separadas por vírgulas, e serão executadas da esquerda para a direita.

A última expressão do bloco de código será o seu retorno. Dentro das expressões não é possível usar estruturas de programação, como WHILE , CASE e afins. É permitido usar as funções IF() e/ou IIF(), que veremos posteriormente.

Chamada

Para executar o CodeBlock, você deve usar a função EVAL(), onde informamos no primeiro parâmetro o CodeBLock ou a variável ou propriedade onde o mesmo está contido, e a partir do segundo parâmetro podemos informar os parâmetros que serão repassados para o CodeBlock. A função Eval() retorna o resultado da última expressão do CodeBlock executado.

Exemplo 01

CodeBlock sem parâmetros, chamando uma função de interface para mostrar uma caixa de diálogo com uma mensagem fixa

User Function Ola01()
Local bCB := {|| MsgInfo("Olá CodeBlock") }
Eval(bCB)
Return

Exemplo 02

CodeBlock recebendo um parâmetro, chamando uma função de interface para mostrar uma caixa de diálogo com o texto informado na chamada.

User Function Ola02()
Local bCB := {| cMsg | MsgInfo(cMsg) }
Eval(bCB,"Olá CodeBlock")
Eval(bCB,"Adeus CodeBlock")
Return

Exemplo 03

CodeBlock para retornar o menor argumento, a partir de dois argumentos fornecidos na chamada, mostrando o resultado em uma caixa de diálogo no SmartClient

User Function Ola03()
Local bCB := {| x1,x2 | IIF( X1 < x2 , x1 , x2 ) }
Local nRet
Local cRet
nRet := Eval(bCB,10,5)
MsgInfo( cValToChar(nRet),"Número")
cRet := Eval(bCB,"ABC","ACD")
MsgInfo( cRet,"String")
Return

Flexibilidade

Quando você cria um bloco de código para seu uso, você define se e quantos parâmetros ele deve receber, e se ou qual será seu retorno. Os parâmetros podem ser nomeados usando a regra de nomenclatura de variáveis ADVPL, e o escopo dos parâmetros é restrito ao corpo do CodeBlock. Atribuições em linha são permitidas no CodeBlock, inclusive podemos passar parâmetros por referência na chamada do CodeBlock, e obter assim mais de um retorno.

Exemplo 04

Passagem de parâmetros por referência para múltiplos retornos.

User Function Ola04()
Local bCB := {| x,y,z | y := x/2 , z := x*2 , x%2 == 0 }
Local nTeste := 4
Local lPar
Local nY , nZ
// O bloco de codigo recebe em x o valor de nTeste
// e recebe em y e z a referência das variáveis 
// nY e nZ respectivamente
lPar := Eval( bCB , nTeste , @nY , @nZ )
MsgInfo(lPar , "O numero é par ? ")
MsgInfo(nY , "Numero / 2 ")
MsgInfo(nZ , "Numero * 2 ")
Return

Outros usos

Quando uma função recebe ou requer como parâmetro um CodeBlock, a documentação da função informa se e quantos parâmetros serão informados na chamada do CodeBlock, e se algum retorno específico é esperado. Por exemplo, quando usamos a função aEval(), onde informamos um array e um CodeBlock como parâmetros, onde a função aEval() vai se encarregar de chamar o CodeBlock para cada elemento do array informado, passando cada elemento do array como parâmetro em cada chamada.

Exemplo 05

Cálculo da média de valores em um array de números

User Function Ola05()
Local aValores := {}
Local nSoma := 0
Local bSoma := {| nValor , nPos | nSoma += nValor }
aadd(aValores,3)
aadd(aValores,4)
aadd(aValores,3)
aadd(aValores,10)
aEval(aValores,bSoma)
MsgInfo(nSoma,"Valores Somados")
Return

Repare que no exemplo acima foi feito algo diferente: A variável local nSoma foi declarada e inicializada dentro da função Ola05(), mas não foi passada como parâmetro para o CodeBlock, ela simplesmente foi usada na expressão dentro do CodeBlock para acumular a soma do elemento nValor recebido como parâmetro no CodeBlock. Logo, um code-block declarado dentro de uma função pode acessar diretamente as variáveis locais disponíveis nesta função.

Esta característica permite utilizar o CodeBlock em modelos mais sofisticados, porém isto pode ter consequências no consumo de memória da aplicação, dependendo de como e onde o bloco é criado e utilizado. Este assunto exige uma profundidade maior de abordagem, que será realizada em um tópico posterior e exclusivo para esta fim.

Outra função que usa code-block de forma interessante é a aSort(), onde informamos um array Advpl para ser ordenado, e podemos montar um CodeBlock para definir a regra de ordenação. Este CodeBlock recebe sempre dois parâmetros, correspondendo a dois elementos quaisquer do Array a ser ordenado, e o retorno desse CodeBlock deve ser um valor booleano. Caso o retorno seja .T., isto significa que o primeiro elemento deve preceder o segundo elemento na ordenação. Caso contrário, o retorno .F. indica que o segundo elemento informado deve anteceder o primeiro na ordenação.

Pré-Conclusão

Um post com 5 exemplos é a pontinha do iceberg … O recurso do CodeBlock é equivalente ao uso de funções anônimas (também conhecidas por funções lambda), disponíveis em outras linguagens de mercado de formas distintas, desde 1958 até os dias de hoje. Vamos abordá-lo em maior profundidade nos próximos post.

Até o próximo post, pessoal 🙂

Anúncios

7 comentários sobre “CodeBlocks em Advpl – Parte 01

  1. Boa tarde Julio,

    Tenho uma dúvida. Seria possível com a ajuda da função _SetNamedPrvt declarar um bloco de código privado em uma função anterior da pilha da chamada e assim acessar suas variáveis locais?

    Parabéns pelo blog, muito bom!

    Att’

    Curtido por 1 pessoa

    • Olá RSGomes, boa noite,

      Interessante a sua dúvida … até fiz uns testes aqui para verificar o comportamento do Advpl. Você consegue declarar uma private e injetar ela no nível anterior, porém para conseguir acessar as variáveis locais do ponto anterior do stack, você precisaria saber os nomes das variáveis, e a instrução Eval() que executaria este codeblock deveria ser executada no mesmo nível de stack de onde estão as variáveis desejadas.

      De qualquer modo, as variáveis locais de uma rotina normalmente são criadas para não serem propagadas, a menos que o programador assim o queira, normalmente passando-as pra frente como parâmetro 😉

      Abraços 😀

      Curtir

      • Vejamos … Se você vai criar um aHeader para um Browse “dinâmico” partindo da estrutura da tabela, acho que o mais elegante é fazer algo mais ou menos assim … crie uma função para retornar o Header, que recebe o alias como parâmetro, e ela monta algo assim:

        Static Function HeadBrow(cAlias)
        Local aHead := {}
        Local aStruct , nI , nT

        // Pega a estrutura do alias
        aStruct := (cAlias)->(DbStruct())
        nT := len(aStruct)

        //Campos que aparecerão na MBrowse, como não é baseado no SX3 deve ser criado.
        //Sequência do vetor: Título, Campo, Tipo, Tamanho, Decimal, Picture

        For nI := 1 to nT

        // Ignora campos “M” Memo
        If aStruct[nI][2] != ‘M’

        AAdd( aHead, { aStruct[nI][1] , ; // Titulo do campo
        &(“{|| “+cAlias+”->”+aStruct[nI][1]+” }”) ,; // CodeBlock do campo
        aStruct[nI][2], ; // Tipo
        aStruct[nI][3], ; // Tamanho
        aStruct[nI][4], ; // Decimal
        “” } )

        Endif

        Next

        Return aHead

        Fonte baseado no exemplo de mBrowse() para tabela temporária disponível no BlackTDN, no link http://www.blacktdn.com.br/2012/05/e-ai-galera-beleza-entao.html

        😀

        Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s