Varáveis

Blog Advpl - Share knowledge

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:

Pode-se escrever:

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:

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

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.

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

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:

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.

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():

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 :

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

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

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.

 

Criação e Atribuição de Variáveis

Variáveis de memória são um dos recursos mais importantes de uma linguagem. São áreas de memória criadas para armazenar informações utilizadas por um programa para a execução de tarefas. Por exemplo, quando o usuário digita uma informação qualquer, como o nome de um produto, em uma tela de um programa esta informação é armazenada em uma variável de memória para posteriormente ser gravada ou impressa.

A partir do momento que uma variável é criada, não é necessário mais se referenciar ao seu conteúdo, e sim ao seu nome. O nome de uma variável é um identificador único que segue duas regras regras: Máximo de 10 caracteres. O AdvPl não impede a criação de uma variável de memória cujo nome contenha mais de 10 caracteres, porém apenas os 10 primeiros serão considerados para a localização do conteúdo armazenado. Portanto se forem criadas duas variáveis cujos 10 primeiros caracteres forem iguais, como nTotalGeralAnual e nTotalGeralMensal, as referências a qualquer uma delas no programa resultarão o mesmo. Ou seja, serão a mesma variável:

 

Quando o conteúdo da variável nTotalGeralMensal é exibido, o seu valor será de 300. Isso acontece porque no momento que esse valor foi atribuido à variável nTotalGeralAnual, o AdvPl considerou apenas os 10 primeiros caracteres (assim como o faz quando deve exibir o valor da variável nTotalGeralMensal), ou seja, considerou-as como a mesma variável. Assim o valor original de 100 foi substituido pelo de 300.

Limitação de caracteres no nome. Os nomes das variáveis devem sempre começar por uma letra ou o caracter de sublinhado ( _ ). No restante, pode conter letras, números e o caracter de sublinhado. Qualquer outro caracter, incluindo espaços em branco, não são permitidos. O AdvPl permite a criação ilimitada de variáveis, dependendo apenas da memória disponível. A seguir estão alguns nomes válidos para variáveis:

E alguns inválidos:

O AdvPl não é uma linguagem de tipos rígidos para variáveis, ou seja, não é necessário informar o tipo de dados que determinada variável irá conter no momento de sua declaração, e o seu valor pode mudar durante a execução do programa. Também não há necessidade de declarar variáveis em uma seção específica do seu código fonte, embora seja aconselhável declarar todas as variáveis necessárias no começo, tornando a manutenção mais fácil e evitando a declaração de variáveis desnecessárias.

Para declarar uma variável deve-se utilizar um identificador de escopo, seguido de uma lista de variáveis separadas por vírgula (,). Um identificador de escopo é uma palavra chave que indica a que contexto do programa a variável declarada pertence. O contexto de variáveis pode ser local (visualizadas apenas dentro do programa atual), público (visualizadas por qualquer outro programa), entre outros. Os diferentes tipos de contexto de variáveis são explicados na documentação sobre escopo de variáveis. Considere as linhas de código de exemplo:

Se esta linha for executada em um programa AdvPl, ocorrerá um erro de execução com a mensagem “variable does not exist: nPercentual”, pois esta variável está sendo utilizada em uma expressão de cálculo sem ter sido declarada. Para solucionar este erro, deve-se declarar a variável previamente:

Neste exemplo, as variáveis são declaradas previamente utilizando o identificador de escopo local. Quando a linha de cálculo for executada, o erro de variável não existente não mais ocorrerá. Porém variáveis não inicializadas têm sempre o valor default nulo (Nil) e este valor não pode ser utilizado em um cálculo pois também gerará erros de execução (nulo não pode ser dividido por 100). A resolução deste problema é efetuada inicializando-se a variável através de uma das formas:

A diferença entre o último exemplo e os dois anteriores é que a variável é inicializada no momento da declaração. Nos dois primeiros exemplos, a variável é primeiro declarada e então inicializada em uma outra linha de código. O comando store existe apenas por compatibilidade com versões anteriores e outras linguagens xBase, mas é obsoleto. Deve-se utilizar o operador de atribuição (:= ou somente =). É aconselhável optar pelo operador de atribuição composto de dois pontos e sinal de igual, pois o operador de atribuição utilizando somente o sinal de igual pode ser facilmente confundido com o operador relacional (para comparação) durante a criação do programa.

Uma vez que um valor lhe seja atribuído, o tipo de dado de uma variável é igual ao tipo de dado do valor atribuído. Ou seja, uma variável passa a ser numérica se um número lhe é atribuído, passa a ser caracter se uma string de texto lhe for atribuída, etc. Porém mesmo que uma variável seja de determinado tipo de dado, pode-se mudar o tipo da variável atribuindo outro tipo a ela:

No programa de exemplo anterior, a variável xVariavel é utilizada para armazenar diversos tipos de dados. A letra “x” em minúsculo no começo do nome é utilizada para indicar uma variável que pode conter diversos tipos de dados, segundo a Notação Húngara (consulte documentação específica para detalhes). Este programa troca os valores da variável e exibe seu conteúdo para o usuário através da função alert. Essa função recebe um parâmetro que deve ser do tipo string de caracter, por isso dependendo do tipo de dado da variável xVariavel é necessário fazer uma conversão antes.

Apesar dessa flexibilidade de utilização de variáveis, deve-se tomar cuidados na passagem de parâmetros para funções ou comandos, e na concatenação (ou soma) de valores. Note a linha 20 do programa de exemplo. Quando esta linha é executada, a variável xVariavel contem o valor nulo. A tentativa de soma de tipos de dados diferentes gera erro de execução do programa. Nesta linha do exemplo, ocorrerá um erro com a mensagem “type mismatch on +”. Excetuando-se o caso do valor nulo, para os demais deve-se sempre utilizar funções de conversão quando necessita-se concatenar tipos de dados diferentes (por exemplo, nas linhas 07 e 17. Note também que quando uma variável é do tipo de dado lógico, ela pode ser utilizada diretamente para checagem (linha 10):

If xVariavel

é o mesmo que

If xVariavel = .T.

A declaração de variáveis para os demais tipos de dados, matrizes e blocos de código, é exatamente igual ao descrito até agora. Apenas existem algumas diferenças quanto a inicialização, que podem ser consultadas na documentação de inicialização de matrizes e blocos de código.

Tipos de Dados

O AdvPl não é uma linguagem de tipos rígidos (strongly typed), o que significa que variáveis de memória podem diferentes tipos de dados durante a execução do programa. Variáveis podem também conter objetos, mas os tipos primários da linguagem são:

Numérico

Lógico

Caracter

Data

Matriz (Array)

Bloco de Código

Numérico

O AdvPl não diferencia valores inteiros de valores com ponto flutuante, portanto pode-se criar variáveis numéricas com qualquer valor dentro do intervalo permitido. Os seguintes elementos são do tipo de dado numérico:

2 43.53 0.5 0.00001 1000000

Uma variável do tipo de dado numérico pode conter um número de dezoito dígitos incluindo o ponto flutuante, no intervalo de 2.2250738585072014 E-308 até 1.7976931348623158 E+308.

Lógico

Valores lógicos em AdvPl são identificados através de .T. ou .Y. para verdadeiro e .F. ou .N. para falso (independentemente se os caracteres estiverem em maiúsculo ou minúsculo).

Caracter

Strings ou cadeias de caracteres são identificadas em AdvPl por blocos de texto entre aspas duplas (“) ou aspas simples (‘):

“Olá mundo!” ‘Esta é uma string’ “Esta é ‘outra’ string”

Uma variável do tipo caracter pode conter strings com no máximo 1 Mb, ou seja, 1048576 caracteres.

A declaração de conteúdos string em Advpl não possui caracteres de “escape” para a declaração de strings. Uma string iniciada com aspas simples deve terminar com aspas simples. Caso seja necessário inserir uma aspa simples em uma string, você pode delimitá-la com aspas duplas; e vice-versa. Caso seja necessário declarar uma string que, ao mesmo tempo contenha aspas simples ou duplas, você deve realizar uma concatenação. Por exemplo:

“Esta é “+'”‘+”uma”+'”‘+” string ‘diferente'”

Data

O AdvPl tem um tipo de dados específico para datas. Internamente as variáveis deste tipo de dado são armazenadas como um número correspondente a data Juliana.

Variáveis do tipo de dados Data não podem ser declaradas diretamente, e sim através da utilização de funções específicas como por exemplo ctod que converte uma string para data.

Matriz (Array)

Matrizes são um tipo de dado especial. É a disposição de outros elementos em colunas e linhas. O AdvPl suporta matrizes uni ou multidimensionais. Os elementos de uma matriz são acessados através de índices numéricos iniciados em 1, identificando a linha e coluna para quantas dimenões existirem.

Uma matriz pode conter no máximo 100000 elementos, independentemente do número de dimensões. Matrizes devem ser utilizadas com cautela, pois se forem muito grandes podem exaurir a memória do servidor.

Bloco de Código

O bloco de código é um tipo de dado especial. É utilizado para armazenar instruções escritas em AdvPl que poderão ser executadas posteriormente.

Variáveis Estáticas

Variáveis estáticas funcionam basicamente como as variáveis locais, mas mantêm seu valor através da execução. Variáveis estáticas devem ser declaradas explicitamente no código com o identificador STATIC.

O escopo das variáveis estáticas depende de onde são declaradas. Se forem declaradas dentro do corpo de uma função ou procedimento, seu escopo será limitado àquela rotina. Se forem declaradas fora do corpo de qualquer rotina, seu escopo é todo o arquivo de programa. Neste exemplo, a variável nVar é declarada como estática e inicializada com o valor 10:

Quando a função Filha é executada, nVar ainda existe mas não pode ser acessada. Diferente de variáveis declaras como LOCAL ou PRIVATE, nVar continua a existir e mantem seu valor atual quando a execução da função Pai termina. Entretanto, somente pode ser acessada por execuções subseqüêntes da função Pai.

Variáveis Locais

Variáveis locais são pertencentes apenas ao escopo da função onde foram declaradas. Devem ser explicitamente declaradas com o identificador LOCAL, como no exemplo:

Neste exemplo, a variável nVar foi declarada como local e atribuída com o valor 10. Quando a função Filha é executada, nVar ainda existe mas não pode ser acessada. Quando a execução da função Pai terminar, a variável nVar é destruída. Qualquer variável com o mesmo nome no programa que chamou a função Pai não é afetada.

Variáveis locais são criadas automaticamente cada vez que a função onde forem declaradas for ativada. Elas continuam a existir e mantêm seu valor até o fim da ativação da função (ou seja, até que a função retorne o controle para o código que a executou). Se uma função é chamada recursivamente (por exemplo, chama a si mesma), cada chamada em recursão cria um novo conjunto de variáveis locais. A visibilidade de variáveis locais é idêntica ao escopo de sua declaração. Ou seja, a variável é visível em qualquer lugar do código fonte em que foi declarada. Se uma função é chamada recursivamente, apenas as variáveis locais criadas na mais recente ativação são visíveis.

A declaração de variáveis locais dentro de uma função deve preceder qualquer comando interno ou declaração de outros tipos de variáveis (Private ou Public) da função caso contrário será gerado um erro de compilação.

Exemplo:

Variáveis Privadas

A declaração é opcional para variáveis privadas. Mas podem ser declaradas explicitamente com o identificador PRIVATE.

Adicionalmente, a atribuição de valor a uma variável não criada anteriormente automaticamente cria a variável como privada. Uma vez criada, uma variável privada continua a existir e mantem seu valor até que o programa ou função onde foi criada termine (ou seja, até que a função onde foi criada retorne para o código que a executou). Neste momento, é automaticamente destruída. É possível criar uma nova variável privada com o mesmo nome de uma variável já existente. Entretanto, a nova (duplicada) variável pode apenas ser criada em um nível de ativação inferior ao nível onde a variável foi declarada pela primeira vez (ou seja, apenas em uma função chamada pela função onde a variável já havia sido criada). A nova variável privada irá esconder qualquer outra variável privada ou pública (veja a documentação sobre variáveis públicas) com o mesmo nome enquanto existir. Uma vez criada, uma variável privada é visível em todo o programa enquanto não for destruída automaticamente quando a rotina que a criou terminar ou uma outra variável privada com o mesmo nome for criada em uma subfunção chamada (neste caso, a variável existente torna-se inacessível até que a nova variável privada seja destruída). Em termos mais simples, uma variável privada é visível dentro da função de criação e todas as funções chamadas por esta, a menos que uma função chamada crie sua própria variável privada com o mesmo nome. Por exemplo:

Neste exemplo, a variável nVar é criada como privada e inicializada com o valor 10. Quando a função Filha é executada, nVar ainda existe e, diferente de uma variável local, pode ser acessada pela função Filha. Quando a função Pai terminar, nVar será destruída e qualquer declaração de nVar anterior se tornará acessível novamente.

 

Variáveis Públicas

Pode-se criar variáveis públicas dinamicamente no código com o identificador PUBLIC. As variáveis públicas continuam a existir e mantêm seu valor até o fim da execução.

É possível criar uma variável privada com o mesmo nome de uma variável pública existente. Entretanto, não é permitido criar uma variável pública com o mesmo nome de uma variável privada existente. Uma vez criada, uma variável pública é visível em todo o programa onde foi declarada até que seja escondida por uma variável privada criada com o mesmo nome. A nova variável privada criada esconde a variável pública existente, e esta se tornará inacessível até que a nova variável privada seja destruída. Por exemplo:

Neste exemplo, nVar é criada como pública e inicializada com o valor 10. Quando a função Filha é executada, nVar ainda existe e pode ser acessada. Diferente de variáveis locais ou privadas, nVar ainda existe após o término da a execução da função Pai.

Diferentemente dos outros identificadores de escopo, quando uma variável é declarada como pública sem ser inicializada, o valor assumido é falso (.F.) e não nulo (nil).

 

Inicializando Matrizes

Algumas vezes o tamanho da matriz é conhecido previamente. Outras vezes o tamanho da matriz só será conhecido em tempo de execução.

Se o tamanho da matriz é conhecido Se o tamanho da matriz é conhecido no momento que o programa é escrito, há diversas maneiras de implementar o código.

Este código preenche a matriz com uma tabela de quadrados. Os valores serão 1, 4, 9, 16 … 81, 100. Note que a linha 07 se refere à variável aX, mas poderia também trabalhar com aY ou aZ. O objetivo deste exemplo é demonstrar trÊs modos de criar uma matriz de tamanho conhecido no momento da criação do código.

Na linha 02 a matriz é criada usando aX[10]. Isto indica ao AdvPl para alocar espaço para 10 elementos na matriz. Os colchetes [ e ] são utilizados para indicar o tamanho necessário. Na linha 03 é utilizada a função array com o parâmetro 10 para criar a matriz, e o retorno desta função é atribuído à variável aY. Na linha 03 é efetuado o que se chama “desenhar a imagen da matriz”. Como pode-se notar, existem dez 0´s na lista encerrada entre chaves ({}). Claramente, este método não é o utilizado para criar uma matriz de 1000 elementos. O terceiro método difere dos anteriores porque inicializa a matriz com os valores definitivos. Nos dois primeiros métodos, cada posição da matriz contém um valor nulo (Nil) e deve ser inicializado posteriormente. A linha 07 demonstra como um valor pode ser atribuído para uma posição existente em uma matriz especificando o índice entre colchetes. Se o tamanho da matriz não é conhecido Se o tamanho da matriz não é conhecido até o momento da execução do programa, há algumas maneiras de criar uma matriz e adicionar elementos a ela. O exemplo a seguir ilustra a idéia de criação de uma matriz vazia (sem nenhum elemento) e adição de elementos dinamicamente.

 

A linha 02 utiliza os colchetes para criar uma matriz vazia. Apesar de não ter nenhum elemento, seu tipo de dado é matriz. Na linha 03 a chamada da função array cria uma matriz sem nenhum elemento. Na linha 04 está declarada a representação de uma matriz vazia em AdvPl. Mais uma vez, estão sendo utilizadas as chaves para indicar que o tipo de dados da variável é matriz. Note que {} é uma matriz vazia (tem o tamanho 0), enquanto {Nil} é uma matriz com um único elemento nulo (tem tamanho 1). Porque cada uma destas matrizes não contem elementos, a linha 07 utiliza a função aadd para adicionar elementos sucessivamente até o tamanho necessário (especificado por exemplo na variável nSize).

 

Matrizes

Matrizes, ou arrays, são coleções de valores. Ou, de uma maneira mais fácil de entender, uma lista. Uma matriz pode ser criada através de diferentes maneiras. Consulte a documentação sobre Inicialização de Matrizes para maiores detalhes.

Cada item em uma matriz é referenciado pela indicação de sua posição numérica na lista, iniciando pelo número 1. O exemplo a seguir declara uma variável, atribui uma matriz de três elementos a ela, e então exibe um dos elementos e o tamanho da matriz:

O AdvPl permite a manipulação de matrizes facilmente. Enquanto que em outras linguagens como C ou Pascal é necessário alocar memória para cada elemento de uma matriz (o que tornaria a utilização de “pointeiros” necessária), o AdvPl se encarrega de gerenciar a memória e torna simples adicionar elementos a uma matriz, utilizando a função aAdd:

Matrizes como Estruturas

Uma característica interessante do AdvPl é que uma matriz pode conter qualquer coisa: números, datas, lógicos, caracteres, objetos, etc. E ao mesmo tempo. Em outras palavras, os elementos de uma matriz não precisam ser necessariamente do mesmo tipo de dado, em contraste com outras linguagens como C e Pascal.

Esta matriz contem uma string, um número e um valor lógico. Em outras linguagens como C ou Pascal, este “pacote” de informações pode ser chamado como um “struct” (estrutura em C, por exemplo) ou um “record” (registro em Pascal, por exemplo). Como se fosse na verdade um registro de um banco de dados, um pacote de informações construído com diversos campos. Cada campo tendo um pedaço diferente de dado.

Suponha que no exemplo anterior, o array aFunct1 contenha informações sobre o nome de uma pessoa, sua idade e sua situação matrimonial. Os seguintes #defines podem ser criados para indicar cada posição dos valores dentro da matriz:

E considere mais algumas matrizes para representar mais pessoas:

Os nomes podem ser impressos assim:

Agora, ao invés de trabalhar com variáveis individuais, pode-se agrupá-las em uma outra matriz, do mesmo modo que muitos registros são agrupados em uma tabela de banco de dados:

Que é equivalente a isso:

aFuncts é uma matriz com 3 linhas por 3 colunas. Uma vez que as variáveis separadas foram combinadas em uma matriz, os nomes podem ser exibidos assim:

A variável nCount seleciona que funcionário (ou que linha) é de interesse. Então a constante FUNCT_NOME seleciona a primeira coluna daquela linha.

Cuidados com Matrizes

Matrizes são listas de elementos, portanto memória é necessária para armazenar estas informações. Como as matrizes podem ser multidimensionais, a memória necessária será a multiplicação do número de itens em cada dimensão da matriz, considerando-se o tamanho do conteúdo de cada elemento contido nesta. Portanto o tamanho de uma matriz pode variar muito.

A facilidade da utilização de matrizes, mesmo que para armazenar informações em pacotes como descrito anteriormente, não é compensada pela utilização em memória quando o número de itens em um array for muito grande. Quando o número de elementos for muito grande deve-se procurar outras soluções, como a utilização de um arquivo de banco de dados temporário. Não há limitação para o número de dimensões que uma matriz pode ter, mas o número de elementos máximo (independentes das dimensões onde se encontram) é de 100000.