Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.
Comentário: Migration of unmigrated content due to installation of a new plugin
Composition Setup
import.css=/download/attachments/327912/newLayout.css
Portuguese

Pagetitle
Blocos de Código
Blocos de Código

Blocos de Código


Blocos de código são um conceito existente há muito tempo em linguagens xBase. Não como algo que apareceu da noite para o dia, e sim uma evolução progressiva utilizando a combinação de muitos conceitos da linguagem para a sua implementação.

 

Primeiro Lembrete

O AdvPl é uma linguagem baseada em funções. Funções têm um valor de retorno. Assim como o operador de atribuição :=. Assim, ao invés de escrever:

x := 10 // Atribui o valor 10 à variável chamada X
Alert("Valor de x: " + cValToChar(x))


Pode-se escrever:

// Atribui e então exibe o valor da variável X
Alert("Valor de x: " + cValtoChar(X := 10))


A expressão x:=10 é avaliada primeiro, e então seu resultado (o valor de X, que agora é 10) é passada para a função cValToChar() para a conversão para caracter e, em seguida, para a função Alert() para a exibição. Por causa desta regra de precedência é possível atribuir um valor a mais de uma varíavel ao mesmo tempo:

Z := Y := X := 0


Por causa dessa regra, essa expressão é avaliada como se fosse escrita assim: 

Z := ( Y := (X := 0) )


Apesar do AdvPL avaliar expressões da esquerda para a direita, no caso de atribuições isso acontece ao contrário, da direita para a esquerda. O valor é atribuído à variável X, que retorna o valor para ser atribuído à variável Y e assim sucessivamente. Pode-se dizer que o zero foi "propagado através da expressão".

 

Outro Lembrete

Em AdvPL pode-se juntar diversas linhas de código em uma única linha física do arquivo. Por exemplo, o código:

If lAchou   Alert("Cliente encontrado!")
Endif


pode ser escrito assim:

If lAchou ; Alert("Cliente encontrado!") ; Endif


O ponto-e-vírgula indica ao AdvPL que a nova linha de código está para começar. Pode-se então colocar diversas linhas lógicas de código na mesma linha física através do editor de texto utilizado. Apesar da possibilidade de se escrever todo o programa assim, em uma única linha física, isto não é recomendado pois dificulta a legibilidade do programa e, consequentemente, a manutenção.

 

Lista de Expressões

A evolução dos blocos de código começa com as listas de expressões. Nos exemplos a seguir, o símbolo ==> indicará o retorno da expressão após sua avaliação (seja para atribuir em uma variável, exibir para o usuário ou imprimir em um relatório), que será impresso em um relatório por exemplo.

Duas Linhas de Código

@00,00 PSAY x := 10      ==>     10
@00,00 PSAY y := 20 ==> 20


Cada uma das linhas terá a expressão avaliada, e o valor da variável será então impresso.

Duas Linha de Código em uma , Utilizando Ponto-e-Vírgula

Este é o mesmo código que o anterior, apenas escrito em uma única linha:

Alert( cValToChar( x := 10 ; y := 20 ) )    ==>    10


Apesar desse código se encontrar em uma única linha física, existem duas linhas lógicas separadas pelo ponto-e-vírgula. Ou seja, esse código é equivalente a:

Alert( cValToChar( x := 10 ) )
y := 20


Portanto, apenas o valor 10 da variável x será passado para as funções cValToChar() e Alert() para ser exibido. E o valor 20 apenas será atribuído à variável y.

 

Convertendo para uma Lista de Expressões

Quando parênteses são colocados ao redor do código e o sinal de ponto-e-vírgula substituído por uma vírgula apenas, o código torna-se uma lista de expressões:

Alert( cValToChar ( ( X := 10 , Y := 20 ) ) )   ==>   20


O valor de retorno resultante de uma lista de expressões é o valor resultante da última expressão ou elemento da lista. Funciona como se fosse um pequeno programa ou função, que retorna o resultado de sua última avaliação (efetuadas da esquerda para a direita).Neste exemplo, a expressão x := 10 é avaliada, e então a expressão y := 20, cujo valor resultante é passado para a função Alert() e cValToChar(), e então exibido. Depois que essa linha de código é executada, o valor de X é igual a 10 e o de y igual a 20, e 20 será exibido.

Teoricamente, não há limitação para o número de expressões que podem ser combinadas em uma lista de expressões. Na prática, o número máximo é por volta de 500 símbolos.Debugar listas de expressões é difícil porque as expressões não estão divididas em linhas de código-fonte, o que torna todas as expressões associadas a uma mesma linha de código. Isto pode tornar muito difícil determinar onde um erro ocorreu.

 

Onde pode-se utilizar uma Lista de Expressões ?

O propósito principal de uma lista de expressões é agrupá-las em uma única unidade. Em qualquer lugar do código AdvPL que uma expressão simples pode ser utilizada, pode-se utilizar uma lista de expressões. E ainda, pode-se fazer com que várias coisas aconteçam onde normalmente apenas uma aconteceria.

X := 10 ; Y := 20
If X > Y
Alert("X")
Z := 1
Else
Alert("Y")
Z := -1
Endif


Aqui temos o mesmo conceito, escrito utilizando listas de expressões na função iif:

X := 10 ; Y := 20
iif( X > Y , ;
( Alert("X"), Z := 1 ) , ;
( Alert("Y"), Z := -1 ) )

 


De Listas de Expressões para Blocos de Código

Considere a seguinte lista de expressões:

Alert( cValToChar( ( x := 10, y := 20 ) ) )  ==>  20


O AdvPL permite criar funções, que são pequenos pedaços de código, como se fosse um pequeno programa, utilizados para diminuir partes de tarefas mais complexas e reaproveitar código em mais de um lugar num programa. Para mais detalhes, consulte a documentação sobre a criação de funções em AdvPL. Porém, a idéia neste momento é que a lista de expressões utilizada na linha anterior pode ser criada como uma função:

Function Lista()
X := 10
Y := 20
Return Y


E a linha de exemplo com a lista de expressões pode ser substituída, tendo o mesmo resultado, por:

Alert( cValToChar( Lista() ) )  ==>  20


Como mencionado anteriormente, uma lista de expressões é como um pequeno programa ou função. Com poucas mudanças, uma lista de expressões pode se tornar um bloco de código:

( X := 10 , Y := 20 )   // Lista de Expressões{|| X := 10 , Y := 20 } // Bloco de Código

 

Observe as chaves {} utilizadas no bloco de código. Ou seja, um bloco de código é uma matriz. Porém na verdade, não é uma lista de dados, e sim uma lista de comandos, uma lista de código.

// Isto é uma matriz de dadosA := {10, 20, 30}
// Isto é um bloco de código, porém funciona como
// se fosse uma matriz de comandos
B := {|| x := 10, y := 20}

 

Executando um Bloco de Código

Diferentemente de uma matriz, não se pode acessar elementos de um bloco de código através de um índice numérico. Porém blocos de código são semelhantes a uma lista de expressões, e a uma pequena função. Ou seja, podem ser executados. Para a execução, ou avaliação, de um bloco de código, deve-se utilizar a função EVal():

B := {|| x := 10, y := 20}
nRes := Eval(B) ==> 20


Essa função recebe como parâmero um bloco de código e avalia todas as expressões contidas neste bloco de código, retornando o resultado da última expressão avaliada. ( O resultado de uma atribuição é o próprio conteúdo atribuído).

 

Passando Parâmetros

Já que blocos de código são como pequenas funções, também é possível a passagem de parâmetros para um bloco de código. Os parâmetros devem ser informados entre as barras verticais (||) separados por vírgulas, assim como em uma função.

B := {| N | X := 10, Y := 20 + N}


Porém, deve-se notar que já que o bloco de código recebe um parâmetro, um valor deve ser passado quando o bloco de código for avaliado.

C := Eval(B, 1)  ==>  21

 

Retorno de um bloco de código

O retorno de um bloco de código será o resultado da última expressão avaliada dentro do bloco de código. Veja o exemplo abaixo : 

A = 0
B = {| x | IIF( x = 0 , time(), date() ) }
C = Eval( B , 0 )
D = Eval( B , 1 )


 

Se a chamada do bloco de código informar o parâmetro 0 (zero), será retornado pelo bloco de código o retorno da função Time() do AdvPL. Ao ser chamado com um valor diferente de zero, será devolvido o retorno da função Date(), que retorna a data atual no servidor.   

 

Utilizando Blocos de Código - Exemplo aSort()

Blocos de código podem ser utilizados em diversas situações. Geralmente são utilizados para executar tarefas quando eventos de objetos são acionados. Por exemplo, considere a matriz abaixo:

A := {"GARY HALL", "FRED SMITH", "TIM JONES"}


Esta matriz pode ser ordenada pelo primeiro nome, utilizando-se a chamada da função ASort(A), resultando na matriz com os elementos ordenados dessa forma:

{"FRED SMITH", "GARY HALL", "TIM JONES"}


A ordem padrão para a função ASort é ascendente. Este comportamento pode ser modificado através da informação de um bloco de código que ordena a matriz de forma descendente:

B := { |X, Y| X > Y }
aSort(A ,,, B)


O bloco de código utilizado neste exemplo (de acordo com a documentação da função ASort()) deve ser escrito para aceitar dois parâmetros, que são os dois elementos da matriz para comparação. Observe que o bloco de código não conhece que elemento está comparando - a função aSort() seleciona os elementos e passa-os para o bloco de código. O bloco de código compara-os e retorna verdadeiro (.T.) se encontram na ordem correta, caso contrário retorna falso (.F.). A função ASort() executará quantas chamadas forem necessárias para ordenar o array até que a sequência de elementos esteja devidamente ordenada.

Para ordenar a mesma matriz pelo último nome, também em ordem decrescente, pode-se utilizar o seguinte bloco de código:

B := { |X, Y| Substr(X,At(" ",X)+1) > Substr(Y,At(" ",Y)+1) }


Observe que este bloco de código procura e compara as partes dos caracteres imediatamente seguinte a um espaço em branco. Depois de utilizar esse bloco de código para a função ASort, a matriz estará na seguinte ordenação:

{"GARY HALL", "TIM JONES", "FRED SMITH"}

 

Utilização Avançada de Blocos de Código

É possivel criar um bloco de código dinamicamente no AdvPL, utilizando-se do operador de macro-execução, bem como chamar de dentro de um bloco de código qualquer função da linguagem AdvPL e funções compiladas no repositório. Estes recursos possuem algumas particularidades de uso, exploradas nos exemplos abaixo.

Exemplo 01 : Bloco de código referenciando variável local

USER FUNCTION BL001() 
Local nContador := 0
Local bIncr := { | i | nContador := nContador + i }
conout("Antes : " + str( nContador , 5 ))
BLCONTA( bIncr )
conout("Depois : " + str( nContador , 5 ))
ReturnSTATIC FUNCTION BLCONTA( bBloco )
Eval( bBloco , 5 )
Return


Após executar a função  U_BL001(), o resultado obtido no log de console do Application Server será : 

Antes: 0
Depois: 5


Repare que a variável nContador foi declarada no escopo LOCAL da função BL001. A princípio, por ser uma variável local, apenas instruções dentro da função BL001 poderiam acessar ou alterar este valor, ou o conteúdo desta variável poderia ser passado por referência para uma outra função. No exemplo, esta variável foi usada dentro de um bloco de código, onde o bloco atualiza o conteúdo da variável com o conteúdo atual somado ao parâmetro recebido pelo bloco de código. O bloco de código foi passado como parâmetro para outra função, onde dentro dela foi feita a chamada deste bloco através da função Eval(), informando o parâmetro numérico 5 . Ao ser executado o bloco de código, a variável nContador referenciada dentro do bloco de código será atualizada. Quando a função retorna, e consultamos o conteúdo de nContador, vemos que ele foi atualizado para o número 5.

Exemplo 02 : Bloco de código com parâmetros por referência

USER FUNCTION BL002()
Local bTeste := { |x| x := x + 1 , u_mostra(x) }

Local nContador := 0
// retorna NIL , mostra valor 1, chamada passando apenas nContador

Eval( bTeste , nContador )
// nContador continua com 0 ( zero )

conout( " Contador = " + str( nContador,10 ))
// retorna NIL , mostra valor 1, chama passando nContador por referência

Eval( bTeste , @nContador )
// Mas agora o nContador está com 1 ( um )

conout( " Contador = " + str( nContador,10 ))


ReturnUser Function Mostra(nValor)
conout( "Valor = " + str( nValor , 10) )
Return


Neste exemplo, o bloco de código não referência explicitamente a variável nContador. Na verdade, ele recebe um parâmetro em x, usa o proprio parâmetro x para receber o resultado da soma dele mesmo com o numero 1, e chama uma função de usuário ( U_MOSTRA ), para mostrar o conteúdo de x. Os parâmetros de um bloco de código são como os parâmetros de uma função AdvPL normal: São todos considerados locais dentro do bloco de código. Da mesma forma que uma função AdvPL, ao passar uma variável com um conteúdo string, data, numérico ou booleano para uma função, a função recebe uma cópia do conteúdo desta variável, de modo que uma alteração na variável que recebe o parâmetro não reflete na variável usada na chamada. Porém, é possível passar um parâmetro por referência a uma função, e se a variável que recebe este parâmetro é alterada dentro da função, esta alteração é refletida na variável original usada na chamada da função. Esta mesma regra é válida para um bloco de código, onde podemos passar por referência um ou mais parâmetros na chamada da função Eval(), reproduzindo o mesmo comportamento.

 

Dicas Importantes/Informações Adicionais

  • Uma chamada de função dentro de um bloco de código, pode conter uma chamada para uma STATIC FUNCTION. Porém, como podemos passar um bloco de código para uma função de outro fonte e executar o Eval() em outra pilha de execução, e fontes diferentes podem conter uma função STATIC com o mesmo nome, o comportamento será indeterminado, isto é, não é possível afirmar qual das funções será chamada, e este comportamento pode mudar dependendo da ordem de execução do bloco de código. É fortemente não recomendado que um bloco de código seja criado com chamada de função de escopo estático. 
  • É boa prática de uso de blocos de código evitar  a troca de contexto de um bloco de código para níveis superiores da pilha de chamadas AdvPL. Um bloco de código trafegado na pilha de chamadas leva consigo uma área de memoria correspondente ao ambiente de escopo local da pilha de chamadas no momento da declaração do bloco de código (para bloco de codigo estático), ou da criação do bloco de código na memória (para bloco de código dinâmico criado com macro-execução). Caso o bloco de código seja trafegado para um nível superior da pilha de execução de funções AdvPL e armazenado neste nível (por exemplo, guardado em uma variável de escopo estático - STATIC ), a memória alocada para levar o bloco de código e o contexto de criação somente será liberada automaticamente pelo servidor de aplicação quando o programa for finalizado, ou manualmente caso a variável usada para armazenar o bloco de código receba o conteúdo NIL, e o bloco de código em si não seja mais referenciado em nenhum outro ponto da aplicação em tempo de execução. 
  • Um bloco de código deve obedecer aos requisitos para o qual o mesmo foi criado. Uma função projetada para receber um bloco de código como argumento provavelmente vai, em algum momento, chamar a execução deste bloco. A passagem de parâmetros e retorno esperados do bloco são informações que devem ser providas na documentação da função que está realizando a chamada, para que o desenvolvedor saiba o que ele receberá quando o bloco for executado, sob que contexto originalmente a aplicação fará a execução do bloco de codigo, e qual é o retorno esperado para o bloco de código. 
  • Seguindo as boas práticas de programação AdvPL, a utilização de um bloco de código pode facilitar muito o desenvolvimento de uma aplicação, mas não é uma solução que serve para todos os problemas. Um bloco de código muito grande ou muito amarrado pode tornar mais difícil a manutenção e a depuração de um código. Se o bloco de código ficar muito extenso, e existe a real necessidade de utilizá-lo, ao invés de colocar 4 KB de statements dentro de um bloco de código, escreva uma função que atenda a necessidade da aplicação, e use o bloco de código para realizar a chamada da função.