Introdução
O intuito desse documento é explicar o uso da linguagem de programação AdvPL, a sua estrutura, os seus comandos e as suas funções na Linha Microsiga Protheus.
Essas informações permitirão que os usuários ou profissionais de informática conheçam a linguagem AdvPL e se capacitem no desenvolvimento de programas e funcionalidades que rodarão na Linha Microsiga Protheus
Os capítulos estão estruturados para que o leitor incremente o seu conhecimento gradualmente.
AdvPL - A linguagem do Microsiga Protheus
Uma das aplicações mais frequentes e úteis dos computadores e dos sistemas de informação é o armazenamento, o controle e o processamento de bases de dados. Uma linguagem de programação permite que esses dados sejam recuperados, processados, transformados em outras informações por meio de cálculos, gravados e mostrados aos usuários por meio de consultas ou relatórios.
Um dos sistemas de informação mais utilizados atualmente nas empresas é o ERP (Enterprise Resource Planning). Por meio de um ERP, todas as áreas de uma empresa são integradas, compartilhando as suas informações e permitindo maior agilidade e precisão na geração e na transmissão de informações entre as áreas e departamentos da empresa. Por exemplo, quando a área de Faturamento emite uma Nota Fiscal, automaticamente o sistema gera os respectivos títulos à receber na área Financeira.
O AdvPL é uma linguagem de programação completa para o desenvolvimento de aplicações no ERP Protheus, desenvolvido e comercializado pela TOTVS. A sua origem é baseada nas linguagem do padrão xBase.
O AdvPL é uma linguagem muito poderosa, flexível e completa. Por meio dela, é possível desenvolver novas aplicações para a Linha Microsiga Protheus, além de adaptar alguns processos às necessidades de cada empresa.
Com o AdvPL é possível desenvolver aplicações para:
- Criar, relacionar e organizar um conjunto de dados;
- Manipular os dados por meio de operações de inclusão, alteração e exclusão. Além disso, é possível visualizá-los de forma seletiva e de acordo com as especificações dos usuários;
- Realizar operações matemáticas, lógicas e de decisão com os dados, gerando novos dados ou extraindo informações;
- Criar telas para a manutenção e a visualização dos dados pelo usuário;
- Criar relatórios para a visualização dos dados pelo usuário;
- Permitir a interação do usuário com as aplicações por meio de páginas Web e de e-mails.
Com a linguagem AdvPL é possível trabalhar com diversos bancos de dados, tanto pagos quanto gratuito, como Microsoft SQL Server, Oracle, IBM DB2, IBM Informix, Postgres, MySql, entre outros.
Variáveis
Durante o processamento de um programa existem determinadas informações que necessitam ser armazenadas na memória do computador, para serem utilizadas à medida que as operações são executadas. Estas informações são armazenadas por meio de nomes identificadores, denominados variáveis, pois podem ter o seu conteúdo modificado durante o fluxo do processamento.
Portanto, uma variável é qualquer referência ou valor armazenado temporariamente na memória do computador por meio de um nome, e cujo conteúdo pode variar durante o processamento.
O nome da variável pode conter letras, algarismos ou o símbolo sublinhado. Pode ser formado por até 10 caracteres, sendo que o primeiro deve, obrigatoriamente, ser uma letra. Para tornar o programa mais claro e legível, uma das convenções das boas práticas de programação é iniciar o nome da variável com a letra que representa o tipo de dado que ela conterá. Essa convenção é explicada e aprofundada no item correspondente às Boas práticas de programação, contido nessa documentação.
Durante a execução de uma aplicação, é possível atribuir um mesmo nome para diferentes variáveis, desde que elas estejam em rotinas diferentes. Entretanto, recomenda-se enfaticamente que essa prática seja evitada. Caso contrário, o risco de ambiguidades dentro do programa será alto, impedindo que o compilador gere um código eficiente. Além disso, o programa poderá ficar tão confuso que, provavelmente, até o programador que o criou terá dificuldade para entendê-lo.
Para evitar ambiguidades e permitir que o compilador gere um código otimizado, referências a campos das tabelas de dados devem ser feitas explicitamente precedendo o seu nome com o operador de alias ( -> ), conforme exemplo abaixo:
// Nome do campo que armazena a quantidade do item da Nota Fiscal de Saída (Venda)
SD2->D2_QUANT
Esta sintaxe refere-se a um campo de uma tabela aberta na área de trabalho e designada pelo alias SD2.
Desta forma, qualquer ambiguidade é explicitamente evitada e uma variável poderá possuir, por exemplo, o mesmo nome de um campo de uma tabela de dados.
O AdvPL permite a definição de diferentes tipos de variáveis, conforme a natureza do dado armazenado nela. Além disso, as variáveis são organizadas em classes que determinam como a variável é armazenada, por quanto tempo ela estará ativa e onde, dentro de um programa, ela poderá ser utilizada (será visível). Cada classe de variável possui um comando específico que declara o seu nome e a cria durante a execução do programa.
Declaração de Variáveis
Declarar uma variável significa informar ao compilador os nomes e as classes das variáveis que existirão no programa. Com base nas declarações, a identificação já é realizada na fase de compilação, antes da execução das instruções e da aplicação.
Variáveis podem ser declaradas por meio dos seguintes comandos:
A declaração é obrigatória para as variáveis das classes estática (STATIC) e local (LOCAL) e opcional para as classes privada (PRIVATE) e pública (PUBLIC). Porém, é recomendável sempre declarar as variáveis, independentemente da sua classe, para que o programa fique fácil de entender, otimizado e livre de ambiguidades.
Os comandos de declaração de variáveis não são executáveis. Ou seja, não desencadeiam nenhuma operação. Eles devem ser inseridos no programa antes de qualquer comando executável, pois o compilador precisa conhecer o nome e a classe das variáveis antes de executar qualquer operação com elas.
Como a declaração de variáveis é resolvida durante a fase de compilação, ela abrange certas unidades do código-fonte: programas (arquivos .PRW), rotinas/funções e blocos de código. Note que algumas dessas unidades estão incluídas em outras: rotinas/funções estão incluídas dentro de programas e blocos de código estão incluídos dentro de rotinas/funções ou de outros blocos de código.
Desta forma, uma declaração de variável que ocorra dentro de uma função aplica-se apenas nesta função e em qualquer bloco de código que ela contenha. Já uma declaração de variável que ocorra dentro de um programa (arquivo .PRW), antes do comando de definição das outras funções (FUNCTION), aplica-se a todas as funções nele contidos.
Uma declaração feita numa função interna a outra pode possuir variáveis com o mesmo nome. Neste caso, a declaração interna prevalece sobre a externa apenas durante a execução da função interna. Por exemplo, uma variável declarada num programa (função mais externa) pode possuir o mesmo nome que uma variável declarada em uma função contida neste programa. Durante a execução desta função interna, a variável declarada nela com o mesmo nome da declarada no programa poderá ter o seu valor modificado, sem que isso afete a outra variável que pertence ao programa. Ao término da função interna, a sua variável é descartada e a variável do programa permanece com o seu valor original.
Escopo/Visibilidade de variáveis
Durante a execução de um programa, uma variável passa a existir, ou é criada, quando uma parte da memória interna do computador é alocada para armazenar o seu valor. Algumas variáveis são automaticamente criadas pelo AdvPL, enquanto outras devem ser explicitamente declaradas pelo programador, conforme a necessidade do seu processamento.
Uma vez criada, uma variável continua a existir, contendo ou não um valor, até que a porção de memória alocada para ela seja apagada ou cancelada. Algumas variáveis são canceladas automaticamente pelo AdvPL, enquanto outras devem ser canceladas explicitamente pelo programa. Todavia, algumas variáveis nunca são canceladas durante a execução de uma aplicação. O tempo de existência de uma variável é muito importante para efeito de programação, sendo conhecido como tempo de vida.
A visibilidade refere-se às condições sob as quais uma variável pode ou não ser acessada por uma rotina durante a sua execução. Se a variável puder ser acessada, ela será visível àquela rotina.
Classe de variáveis
O AdvPL permite a declaração e a criação de 4 classes de variáveis, que se diferenciam de acordo com a sua visibilidade e tempo de vida dentro da aplicação. As classes de variáveis são:
- Locais
- Estáticas
- Privadas
- Públicas
Variáveis Locais
Variáveis locais devem ser explicitamente declaradas pelo comando LOCAL. As variáveis dessa classe são visíveis apenas para a função onde estão declaradas, e são descartadas após o término da sua execução.
Por exemplo, se declararmos variáveis locais numa função denominada Teste1(), elas poderão ser utilizadas somente por esta função, pois são visíveis apenas para ela, sendo descartadas quando a função for finalizada.
FUNCTION Teste1()
LOCAL nNum := 12.97
LOCAL cNome := "LAERCIO"
LOCAL aMatriz := { 5, 5 }
// Instruções
Teste2()
RETURN .T.
Neste exemplo foram declaradas 3 variáveis locais: uma numérica, um caracter e um array. Essas variáveis serão visíveis apenas para a função Teste1(). Quando for executada a função Teste2(), invocada pela função Teste1(), as variáveis nNum, cNome e aMatriz ainda existirão, mas serão inacessíveis ou invisíveis para ela. Quando a execução da função Teste2() for finalizada e se retornar para a função Teste1(), as variáveis poderão ser utilizadas novamente. Contudo, assim que a execução da função Teste1() for finalizada, as variáveis serão descartadas.
Por outro lado, se existirem variáveis com os mesmos nomes na rotina que invocou a função Teste1(), nada lhes acontecerá. Ou seja, as variáveis da função principal que invocou a função Teste1 continuarão vivas e com o mesmo valor original.
As variáveis locais são criadas automaticamente cada vez que as rotinas nas quais foram declaradas são executadas. Elas existirão e manterão os dados nelas armazenados até a finalização destas rotinas. Além disso, caso uma rotina seja invocada recursivamente, cada execução da mesma criará um novo conjunto de variáveis locais. Apenas o conjunto de variáveis locais mais recente será visível para a rotina.
Variáveis Estáticas
Variáveis estáticas funcionam de maneira semelhante às variáveis locais. Contudo, ao contrário das locais, as estáticas são mantidas após o término da execução da função que as declarou. Além disso, podem ser iniciadas no momento da declaração. Para declarar uma variável da classe estática utiliza-se o comando STATIC.
A abrangência das variáveis estáticas está limitada à função nas quais foram declaradas. Se forem declaradas dentro de uma função, a sua abrangência limita-se àquela função. Se forem declaradas fora de uma função, o seu escopo será aplicado em todo o programa (arquivo .PRW).
Por exemplo, se variáveis estáticas forem declaradas numa função denominada Teste3(), estas serão visíveis somente para essa função. Porém, elas não serão descartadas ao término da função Teste3(), como ocorre com as variáveis locais.
FUNCTION Teste3()
STATIC nNum := 1000
// Instruções
Teste4()
RETURN .T.
Neste exemplo, a variável nNum é declarada estática e iniciada com o valor numérico 1000. Quando a função Teste4() for executada, invocada pela Teste3(), a variável nNum continuará existindo, mas não será visível para ela. Porém, ao contrário do que ocorre com as variáveis locais, a variável nNum continuará existindo mesmo depois que a execução da função Teste3() for finalizada. Apenas as execuções subsequentes da função Teste3() poderão acessar novamente a variável.
As variáveis estáticas são criadas automaticamente, durante a compilação, antes da execução do programa. Elas continuam a existir e armazenar os seus valores durante toda a execução do programa, não sendo descartadas.
Um valor inicial pode ser atribuído às variáveis estáticas por meio do próprio comando STATIC. Caso contrário, elas serão automaticamente iniciadas com o valor NIL (ausência de dados).
Variáveis Privadas
Ao contrário das variáveis locais e estáticas, as variáveis privadas não precisam ser explicitamente declaradas. Contudo, as variáveis privadas poderão ser criadas pelo comando PRIVATE.
Caso seja atribuído um valor em uma variável inexistente, ou seja, não declarada, a mesma será automaticamente considerada como uma variável privada.
Quando uma variável privada é criada pelo comando PRIVATE e não é iniciada com nenhum valor, o AdvPL automaticamente lhe atribui o valor NIL (ausência de dados).
Uma variável privada é visível para a rotina que a criou e para todas as rotinas invocadas por ela. Uma vez criada, uma variável privada continua a existir e retém o seu valor até que a rotina que a criou seja finalizada, quando então será automaticamente descartada.
Uma variável privada pode ser criada com o mesmo nome de outra variável pública ou privada já existente, desde que isso ocorra em uma rotina de menor nível do que a rotina que criou a primeira variável homônima. Neste caso, a variável pública ou privada da rotina de maior nível ficará oculta e inacessível até que a nova variável privada seja descartada.
Portanto, uma variável privada é visível para a rotina que a criou e para todas as outras rotinas invocadas por esta. Caso uma rotina de menor nível, invocada pela rotina que criou a variável, crie uma nova variável privada com o mesmo nome, a primeira se tornará inacessível até que a segunda seja descartada. O exemplo a seguir ilustra essa condição:
FUNCTION Teste5()
PRIVATE nNum := 200
// Instruções
Teste6()
RETURN .T.
Neste exemplo, a variável privada nNum é criada na função Teste5(), sendo atribuído o valor numérico 200. Quando a função Teste6() é executada, a variável nNum continua existindo e, ao contrário das variáveis locais, é acessível. Quando a função Teste5() for finalizada, a variável privada nNum será descartada. Caso exista uma rotina de nível superior, que invocou a função Teste5(), e que eventualmente também tenha criado uma variável privada com o mesmo nome, esta se tornará acessível.
Variáveis Públicas
Variáveis públicas são criadas durante a execução dos programas pelo comando PUBLIC. Uma vez criadas, podem ser acessadas por todos os programas, existindo e armazenando valores até o final da execução da aplicação.
Variáveis públicas podem ser criadas em qualquer rotina. Ao contrário das variáveis locais e privadas, as variáveis públicas não são descartadas ao final da rotina na qual foram criadas e o seu conteúdo pode ser acessado por qualquer rotina que compõem a aplicação desenvolvida. Com isso, evita-se a necessidade da passagem de parâmetros entre rotinas. Quando uma variável é declarada sem ser iniciada com um valor, o AdvPL lhe atribui automaticamente o valor lógico falso (.F.).
O AdvPL permite a criação de uma variável privada com o mesmo nome de uma variável pública. Neste caso, a variável privada oculta temporariamente a variável pública, que se torna inacessível até que a variável privada homônima seja descartada. Contudo, não é possível a criação de uma variável pública com o mesmo nome de uma variável privada já existente. O exemplo abaixo ilustra a criação de uma variável pública:
FUNCTION Teste7()
PUBLIC nNum := 300
// Instruções
Teste8()
RETURN .T.
Neste exemplo, a variável pública nNum é criada e iniciada com o valor numérico 300 na função Teste7(). Quando a função Teste8() é executada, a variável nNum continua existindo e pode ser acessada por ela. Ao final da função Teste7(), a variável nNum ainda continua existindo e o seu conteúdo pode ser acessado e alterado por qualquer outra rotina da aplicação.
Tipos de Variáveis
Com o propósito da utilização em expressões, cada dado no AdvPL é identificado como um tipo. Dessa forma, para cada variável, constante, função ou campo de tabela é associado um tipo de dado, que depende da forma como cada um destes itens foi criado. Por exemplo, o tipo de dado de uma variável depende do valor nela armazenado, o tipo de dado de uma função depende do valor por ela fornecido e o tipo de dado de um campo depende da estrutura da tabela de dados correspondente.
Os tipos de dados possíveis na linguagem AdvPL são:
- Caracter
- Memo
- Data
- Numérico
- Lógico
- NIL (ausente)
- Array
- Bloco de Código
Caracter
Quando os valores armazenados formam cadeias de tamanho fixo, compostas por dígitos numéricos (0 - 9), letras alfabéticas(a - z ou A - Z) ou símbolos especiais (@, +, *, -, /, %, $, &, etc.), trata-se de um dado do tipo caracter.
Um dado do tipo caracter é formado por uma cadeia contendo de zero à 65.535 (64 Kbytes) caracteres da tabela ASCII. O conjunto completo de dados tipo caracter do AdvPL corresponde aos códigos ASCII 0 (nulo), 32 à 126 (caracteres imprimíveis) e 128 à 255 (caracteres gráficos).
Os valores ou constantes armazenados devem ser circunscritos por um par de delimitadores (abre e fecha), que pode ser aspas (" ") ou apóstrofos (' ').
O valor vazio para esse tipo de dado é o caracter ASCII 0 (nulo). Para representá-lo deve-se utilizar dois delimitadores contíguos. Ou seja, "" ou ''.
Dados tipo caracter são ordenados de acordo com o valor do seu código da Tabela ASCII.
Exemplos de dados do tipo caracter:
"O AdvPL é uma poderosa linguagem de programação"
"A taxa de juros é de 0,99 % ao mês, condicionada ao valor mínimo de R$ 100,00 da prestação"
Memo
Um dado tipo memo é equivalente ao dado tipo caracter. A única diferença é que um dado tipo memo não tem tamanho definido. Essa característica é possível porque os dados desse tipo são gravados em arquivos de dados.
O código que identifica o dado tipo memo é gravado no arquivo de dados proprietário da informação, como o Cadastro de Clientes, de Fornecedores, etc. Esse código identificador possui 10 posições e é responsável por identificar o conteúdo da informação gravado no arquivo de dados SYP.
No AdvPL, a função padrão MSMM() é responsável pela gravação e recuperação de dados do tipo memo.
Esse tipo de dado é importante para os casos em que a informação não pode se restringir em um determinado tamanho. Por exemplo, observações, laudos, etc.
Data
Este tipo de dado representa datas do calendário e permite a realização de várias operações com elas, como, por exemplo, a obtenção da diferença entre duas datas, em número de dias, e a obtenção de uma nova data após a soma ou a subtração por um determinado número de dias.
Esse tipo de dado armazena os valores com 8 posições, constituídas por 8 dígitos numéricos de 0 até 9, intercalados por barras "/", no formato 99/99/9999.
Para a definição de dados tipo data é obrigatória a utilização da função CTOD(), que transforma dados tipo caracter em dados tipo data. Por exemplo:
// Trata datas no formato dia/mês/ano
dNascim := CTOD("14/05/2012")
Com exceção dos campos tipo data das tabelas de dados, a única forma de se definir um dado tipo data é por meio da função CTOD().
Tanto os formatos americano quanto brasileiro são representados da seguinte forma:
- O símbolo dd representa o dia. Dependendo do mês, deve estar entre 01 e 31.
- O símbolo mm representa o mês. Deve estar entre 01 e 12.
- O símbolo ss representa o século do ano. Deve estar entre 00 e 29.
- O símbolo aaaa representa o ano. Deve se um ano válido.
Nos Bancos de Dados, o armazenamento das datas é feito no formato texto: ssaammdd. O software de interface entre o Protheus e os Bancos de Dados, chamado de Top Connect, converte essa informação de texto para data, conforme a configuração vigente no Protheus, quando a aplicação necessita da informação.
Na Linha Microsiga Protheus, a exibição da data pode ser configurada para o formato americano, representado pelos símbolos mm/dd/aa ou mm/dd/aaaa, ou para o formato adotado no Brasil, representado por dd/mm/aa ou dd/mm/aaaa.
Como padrão, o AdvPL trabalha com datas do século XX, suprimindo a sua indicação. Isto é, a indicação do século, representada pelo símbolo ss, não é apresentada.
Para que o AdvPL os aceite, os dados do tipo data devem ser datas válidas, compreendidas entre 01/01/0100 e 31/12/2999. O valor vazio para os dados tipo data é uma data em branco ou uma data vazia, que deve ser representada pelos comandos CTOD(""), CTOD(SPACE(8)) ou CTOD(" / / "). A função CTOD aceita somente datas válidas como argumento. Caso seja apresentada uma data inválida como argumento, o comando a converterá numa data nula.
Exemplos de datas inválidas:
- 32/12/2012
- 23/13/2012
- 23/12/0099
- 29/02/2012
Numérico
Quando um dado possui na sua composição somente algarismos numéricos de 0 até 9, os sinais + (mais) ou - (menos) e o ponto decimal, ele é do tipo numérico. Este tipo de dado destina-se à realização de cálculos matemáticos.
Por meio deste tipo de dado, o AdvPL é capaz de processar valores numéricos entre 10-308 e 10308. Um arquivo de dados pode armazenardados numéricos com no máximo 32 posições (30 algarismos numéricos, o sinal + ou - e o ponto decimal), com até 16 casas decimais de precisão garantida. Portanto, podemos ter um número com 32 posições inteiras, sem casas decimais, até um número com 15 posições inteiras, o ponto decimal e 16 casas decimais.
Um dado numérico não pode ser delimitado por nenhum símbolo. Caso contrário, será considerado caracter. O valor vazio ou nulo para um dado numérico é o valor zero ( 0 ).
O valor numérico no AdvPL segue o formato americano: o ponto decimal é representado pelo ponto ( . ) e a separação dos valores inteiros pela vírgula ( , ). Segue alguns exemplos baseados em valores monetários:
- 43.53 (quarenta e três reais e cinquenta e três centavos)
- 0.05 (cinco centavos)
- 1,000,000.00 (um milhão)
- 1.815 (um real e oitocentos e quinze milésimos)
Porém, no momento de exibir a informação para o usuário, seja na tela ou em relatório, é possível converter o valor numérico para o formato adotado no Brasil, onde o ponto decimal é representado pela vírgula ( , ) e a separação dos valores inteiros pelo ponto ( . ). Vejamos os mesmos exemplos acima no formato adotado no Brasil:
- 43,53 (quarenta e três reais e cinquenta e três centavos)
- 0,05 (cinco centavos)
- 1.000.000,00 (um milhão)
- 1,815 (um real e oitocentos e quinze milésimos)
Lógico
Um dado do tipo lógico representa apenas dois estados possíveis, chamados de valores booleanos: falso ou verdadeiro. O seu conteúdo poderá ser apenas:
- .T. ou .t. (True ou Verdadeiro)
- .F. ou .f. (False ou Falso)
Os dados lógicos são, obrigatoriamente, delimitados por pontos, que equivalem aos delimitadores dos dados tipo caracter. Por exemplo:
- lRetorno := .T. // Verdadeiro
- lRetorno := .F. // Falso
NIL ou valores nulos
O tipo NIL permite a manipulação de variáveis declaradas, mas não iniciadas com a atribuição de algum valor. A representação deste tipo de dado é a sigla NIL, sem delimitadores.
Pode-se interpretar NIL como sendo ausência de dado, pois quando um valor não for atribuído para uma variável, array, elemento de array ou parâmetro esperado de uma função, o AdvPL automaticamente atribuirá o valor NIL.
A única classe de variáveis para a qual não se pode atribuir o valor NIL são os campos das tabelas de dados.
Array e Vetores
No AdvPL, matrizes e vetores podem ser considerados como um tipo de dado porque uma referência a uma matriz pode ser atribuída a uma variável, fornecida por uma função ou passada como argumento. Uma referência é um endereço da memória do computador no qual se localiza uma matriz. Devido às suas particularidades, trataremos esse tipo de dado mais adiante em um capitulo especifico.
Bloco de Código/CodeBlock
São trechos de código compilado que, assim como os arrays, podem ser considerados como um tipo de dado porque uma referência a um bloco de código pode ser atribuída a uma variável, fornecida por uma função ou passada como argumento. Uma referência é um endereço da memória do computador no qual se localiza um bloco de código. Devido às suas particularidades, trataremos esse tipo de dado mais adiante em um capitulo especifico.
Array e Vetores
Um array pode ser definido como um conjunto de dados relacionados, chamados de elementos, armazenados com um mesmo nome e identificados e acessados por meio de um índice numérico. Em cada um destes elementos podem ser armazenados qualquer tipo de dados, exceto campos memo. Inclusive, um elemento de um array pode conter uma referência para outro array ou bloco de código.
Cada um dos elementos de um array é identificado pelo nome do mesmo e, entre colchetes, pelo seu índice. Por exemplo, considere um array chamado Tabela, formado por 5 linhas e 8 colunas, conforme esquematizado a seguir:
Esquema do Array Tabela |
---|
1,1 | 1,2 | 1,3 | 1,4 | 1,5 | 1,6 | 1,7 | 1,8 |
2,1 | 2,2 | 2,3 | 2,4 | 2,5 | 2,6 | 2,7 | 2,8 |
3,1 | 3,2 | 3,3 | 3,4 | 3,5 | 3,6 | 3,7 | 3,8 |
4,1 | 4,2 | 4,3 | 4,4 | 4,5 | 4,6 | 4,7 | 4,8 |
5,1 | 5,2 | 5,3 | 5,4 | 5,5 | 5,6 | 5,7 | 5,8 |
O array Tabela é classificado como uma matriz de duas dimensões 5 x 8, pois possui cinco linhas e oito colunas. No esquema são apresentados os índices que identificam cada um dos seus elementos. Por exemplo, o elemento localizado na terceira linha da quinta coluna é identificado pelo índice 3,5. O elemento localizado na segunda linha da oitava coluna é identificado pelo índice 2,8. E assim por diante. Os índices são sempre números inteiros.
No AdvPL, um array é acessado por meio de uma referência. Ou seja, um endereço da memória no qual o array está localizado e por meio do qual pode ser acessado. Quando um array é criado, atribui-se um nome para esta referência, que passa a identificar o array.
Matrizes Multidimensionais
Matrizes multidimensionais são implementadas no AdvPL por meio de conjuntos de submatrizes unidimensionais intercaladas. Nesta arquitetura, cada elemento de uma matriz pode conter um dado ou então uma referência para outra matriz. Vejamos o exemplo abaixo:
aFuncion := { "Marcos", 42, .T. } // Array unidimensional com as seguintes informações: Nome do funcionário, Idade e indicação se é CLT
Porém, um dos elementos pode ser as informações dos dependentes do funcionário. Nesse caso, a estrutura do array seria:
aFuncion := { "Marcos", 42, .T., aDepend }
Onde a estrutura do array aDepend seria Nome do Dependente, Grau de parentesco, Idade, Sexo:
aDepend := { { "Cláudia", 1, 35, "F" };
{ "Rian", 2, 7, "M" } }
Essa arquitetura do AdvPL permite que matrizes multidimensionais possam ser assimétricas. Isto acontece quando uma das matrizes intercaladas possui um número diferente de elementos das outras matrizes do mesmo nível ou da matriz na qual ela está inserida. Suponhamos que seja incluído o CPF no array de dependentes:
aDepend := { { "Cláudia", 1, 35, "F", "27847307849" };
{ "Rian", 2, 7, "M", "16668978119" } }
Neste exemplo, o array aFuncion contém 4 elementos em sua estrutura, enquanto o array aDepend contém 5 elementos.
Ao invés de referenciar outro array, as informações dos dependentes também poderiam ser gravadas diretamente no array aFuncion, como um dos seus elementos:
aFuncion := { "Marcos", 42, .T., { { "Cláudia", 1, 35, "F", "27847307849" },;
{ "Rian", 2, 7, "M", "16668978119" } } }
Criação de Arrays
A declaração dos arrays é realizada pelos mesmos comandos das variáveis dos demais tipos de dados: PRIVATE, PUBLIC, LOCAL e STATIC.
O array pode ser criado com um tamanho definido ou não. Caso não seja criado com um tamanho pré-definido, as suas dimensões são definidas durante a execução do programa. Vejamos o exemplo abaixo:
aExemp1 := { 10, 20, 30, 40, 50 }
aExemp2 := { "Clipper", aExemp1[3] + aExemp1[5], SQRT(a[1]), 1 + aExemp1[2] }
O exemplo abaixo é exatamente equivalente ao anterior:
LOCAL aExemp1[5]
LOCAL aExemp2[4]
aExemp1[1] := 10
aExemp1[2] := 20
aExemp1[3] := 30
aExemp1[4] := 40
aExemp1[5] := 50
aExemp2[1] := "Clipper"
aExemp2[2] := aExemp1[3] + aExemp1[5]
aExemp2[3] := SQRT(aExemp1[1])
aExemp2[4] := 1 + aExemp1[2]
Um array pode ser utilizado em qualquer ponto de um programa, inclusive como um elemento de outro array. Desta forma, pode-se especificar um array multidimensional:
aExemp3 := { { 1, 2, 3 }, { "A", "B", "C" }, { .F., DATE(), .F. } }
Caso os elementos do array fossem mostrados para o usuário na tela, teríamos os seguintes valores:
MSGALERT(aExemp3[1, 1]) // Resultado: 1
MSGALERT(aExemp3[2, 1]) // Resultado: "A"
MSGALERT(aExemp3[3, 3]) // Resultado: .F.
A função ARRAY() é usada para criar arrays com dimensões definidas. As dimensões do array a ser criado são especificadas como argumentos da função ARRAY(). Exemplo:
LOCAL aTabela := ARRAY(5, 10)
Nesse exemplo, criou-se um array com 5 linhas e 10 colunas.
Um array vazio é definido como sendo uma matriz sem elementos. Para criar um array vazio utiliza-se a seguinte instrução:
Os arrays vazios são úteis quando não se sabe, a princípio, o número de elementos que eles possuirão. Posteriormente, poderão ser utilizadas as funções AADD() e ASIZE() para mudar a sua estrutura, adicionando o número de elementos que forem necessários para o processamento da rotina.
Para testar se um array existe ou foi criado, utiliza-se a função VALTYPE(), que fornecerá "A" caso o seu argumento for um array. Por exemplo:
LOCAL aTabela := { }
IF VALTYPE(aTabela) == "A"
MSGALERT("A variável é um array")
ENDIF
Para testar se um array está vazio, ou seja, não possui elementos, utiliza-se a função EMPTY(), conforme exemplo abaixo:
IF EMPTY(aTabela)
MSGALERT("O array está vazio")
ENDIF
O número de elementos de um array pode ser determinado por meio da função LEN():
LOCAL aTabela[5, 8]
MSGALERT(LEN(aTabela))
A mensagem acima mostrará que o array aTabela possui 5 elementos.
Repare que a função LEN() fornece apenas o número de elementos da primeira dimensão do array aTabela. O motivo é que no AdvPL as matrizes multidimensionais são, na verdade, compostas por submatrizes unidimensionais intercaladas, contidas ou referenciadas por uma matriz principal. Ou seja, pode-se interpretar que o array aTabela possui uma dimensão com cinco elementos, onde cada um dos elementos desta dimensão contém uma referência a uma submatriz de oito elementos, formando, desta maneira, uma matriz de duas dimensões 5 x 8. É por este motivo que a função LEN(), quando aplicada ao array aTabela, fornece como resultado apenas cinco elementos.
Para determinar o número de elementos das submatrizes, ou seja, da segunda dimensão do array aTabela, deve ser aplicada a função LEN() ao primeiro elemento da matriz principal que contém a primeira submatriz, conforme o exemplo abaixo:
MSGALERT(LEN(aTabela[1]))
Nesse caso, a mensagem mostrará que o primeiro elemento possui 8 colunas.
Caso a matriz avaliada esteja vazia, a função LEN() fornece o valor zero.
Elementos de Arrays
Para acessar um elemento de um array deve-se identificá-lo pelo nome do array e, em seguida, entre colchetes, pelos seus índices. Por exemplo:
PRIVATE aTabela[5,8] // Cria a matriz aTabela
Quando um array é criado sem os valores definidos, cada um dos seus elementos tem o valor NIL atribuído. Esse valor persiste até que os elementos sejam iniciados com outros valores. O exemplo abaixo demonstra a atribuição de diversos dados em alguns elementos do array aTabela.
aTabela[1,1] := 100 // Equivale à aTabela[1][1] := 100
aTabela[2][3] := "AdvPL"
aTabela[5,4] := DATE()
aTabela[2][2] := .T.
aTabela[3,3] := aTabela[1,1] * 2
aTabela[4,4] := SQRT(aTabela[1,1])
Os índices que identificam os elementos de um array são sempre números inteiros e iniciam com o número 1. Os índices atribuídos aos elementos de um array devem ser compatíveis com as suas dimensões e com o número de elementos de cada dimensão. Caso isto não ocorra, será gerado um erro durante a execução do programa. Por exemplo:
A atribuição acima causará um erro, pois o array aTabela possui apenas 5 linhas.
O AdvPL também permite a criação e a iniciação de um array com base na estrutura de uma tabela de dados e das informações de uma pasta de arquivos contidas no servidor. Os respectivos comandos (DBSTRUCT e DIRECTORY) estão descritos no tópico Funções.
A função AFILL() é especialmente destinada à iniciação de elementos de arrays de uma única dimensão com um determinado dado. Por exemplo:
LOCAL aVetor[20]
AFILL(aVetor, SPACE(10), 1, 10) // Inicia os 10 primeiros elementos do array unidimensional aVetor com dez espaços em branco.
AFILL(aVetor, 0, 11, 20) // Inicia os 10 últimos elementos do array unidimensional aVetor com o valor numérico zero.
Os elementos de um array podem ser iniciados com o resultado de qualquer expressão válida do AdvPL e com qualquer tipo de dado, incluindo blocos de código e referências a outros arrays. Uma vez iniciados, os elementos de um array podem ser utilizados como se fossem variáveis.
Como no AdvPL pode-se atribuir uma referência de um array para um elemento de outro array, a estrutura do array já existente pode ser alterada dinamicamente durante a execução do programa. Por exemplo:
PRIVATE aTabela[5,5] // O array foi criado com 5 linhas e 5 colunas.
aTabela[1, 1] := { 10, 20, 30, 50, 80 } // A estrutura do array foi alterada em tempo de execução.
Caso o conteúdo do elemento aTabela[1, 1, 5] fosse mostrado, apareceria o conteúdo 80.
Entretanto, caso se tentasse mostrar o conteúdo dos elementos abaixo, ocorreria erro, pois não existem esses elementos na estrutura do array:
aTabela[1, 2, 1]
aTabela[2, 1, 1]
Analisando esse exemplo, as três dimensões são válidas apenas para o elemento aTabela[1, 1], pois este passou a conter uma referência à outro array com uma dimensão. Este poderoso recurso do AdvPL permite que se explore recursos incríveis de programação. Contudo, como a estrutura de um array pode mudar dinamicamente, deve-se tomar cuidado com a sua utilização, pois existe o risco de se perder totalmente o controle de uma aplicação, tornando-a confusa e suscetível à erros.
Utilização de arrays como parâmetros de rotinas
Arrays podem ser passados como argumentos para funções. Quando a passagem é realizada por meio da sintaxe das funções, os arrays são, por definição, passados por valor. Isto significa que uma cópia da referência ao array é passada ao parâmetro que será recebido pela função invocada. Nessa situação, qualquer alteração realizada nos elementos do array pela função invocada se refletirá automaticamente no array original. Por exemplo:
LOCAL aLista[20] // O array é criado e todos os elementos recebem o valor NIL
AFILL(aLista, 1, 1, 10) // Atribui o valor 1 aos 10 primeiros elementos
AFILL(aLista, 2, 11, 20) // Atribui o valor 2 aos 10 últimos elementos
MSGALERT(aLista[1]) // Mostra o valor 1
MSGALERT(aLista[11]) // Mostra o valor 2
Dobro(aLista) // Executa a função Dobro()
MSGALERT(aLista[1]) // Mostra o valor 2
MSGALERT(aLista[11]) // Mostra o valor 4
// Função Dobro()
FUNCTION Dobro(aMat)
LOCAL nElem := 0
FOR nElem := 1 TO LEN(aMat)
aMat[nElem] := aMat[nElem] * 2
NEXT nElem
RETURN NIL
Neste exemplo, quando a função Dobro() é executada, uma cópia da referência ao array aLista é passada para o parâmetro aMat. A função Dobro() multiplica todos os elementos do array aLista por dois. Quando a função Dobro() é finalizada, a referência aMat é descartada, mas as alterações efetuadas nos valores dos elementos do array aLista são mantidas.
Um Array também pode ser fornecido como resultado de funções. Este recurso permite que uma função, que normalmente fornece apenas um único valor como resultado, forneça múltiplos valores através dos elementos de um array e pelo comando RETURN. Por exemplo:
aTabela := CriaTb(5, 10, SPACE(10))
// Função CriaTB, que alimenta o array de acordo com o número de linhas e colunas, e o conteúdo.
FUNCTION CriaTb(nLinhas, nColunas, cConteudo)
LOCAL aTab[nLinhas, nColunas]
FOR nElem1 := 1 TO nLinhas
FOR nElem2 := 1 TO nColunas
aTab[nElem1, nElem2] := cConteudo
NEXT nElem2
NEXT nElem1
RETURN aTab
Funções e operadores especiais para a manipulação de arrays
O operador duplo igual ( == ) é usado para comparar dois arrays, verificando se eles apontam para a mesma referência. Por exemplo:
LOCAL aTabela1 := { 1, 2, 3 }
LOCAL aLista := { 1, 2, 3 }
LOCAL aTabela2 := aTabela1 // Atribui a referência do array aTabela1 para aTabela2
IF aTabela1 == aTabela2
// Resultado: .T. (verdadeiro)
MSGALERT(“Os arrays aTabela1 e aTabela2 têm a mesma referência.”)
ENDIF
IF ! ( aTabela1 == aLista )
// Resultado: .F. (falso)
MSGALERT(“A referência dos arrays aTabela1 e aLista são diferentes”)
ENDIF
O AdvPL possui duas funções para mudar as dimensões de um array já existente:
- ASIZE( <array>, <elementos> )
A função ASIZE() redefine um array ( <array> ) de acordo com um novo número de elementos ( <elementos> ). Caso o novo número de elementos seja superior ao antigo, novos elementos serão adicionados, atribuindo-se o valor NIL para eles. Caso o novo número de elementos seja inferior ao antigo, os elementos que estiverem acima desse número serão removidos.
- AADD( <array>, <expressão> )
A função AADD() adiciona um novo elemento no array ( <array> ) especificado, atribuindo-lhe o resultado da expressão ( <expressão> ) especificada. O novo elemento será o último do array.
Para inserir ou eliminar elementos de um array já existente, o AdvPL possui duas funções. Ao contrário das funções AADD() e ASIZE(), estas funções não alteram as dimensões do array, mas apenas o conteúdo dos seus elementos.
- ADEL( <array>, <nº do elemento> )
A função ADEL() elimina o elemento do array ( <array> ) definido pelo número do elemento ( <nº do elemento> ). Todos os elementos localizados após o elemento eliminado são movidos uma posição para a esquerda. Ao último elemento é atribuído o valor NIL.
- AINS( <array>, <nº do elemento> )
A função AINS() insere um elemento no array ( <array> ) na posição definida pelo argumento <nº do elemento>. Ao novo elemento é atribuído o valor NIL. Os elementos localizados após o elemento inserido são movidos uma posição para a direita e o último elemento do array será destruído.
Para efetuar operações de cópia de arrays, o AdvPL possui duas funções:
Essa função copia um conjunto ou todos os elementos de um array origem para um array destino já existente. Caso o array origem contiver subarrays, o array destino conterá apenas referências a elas, e não cópias reais.
Essa função duplica um array origem inteiro para um novo array destino, criando um novo array e realmente copiando os valores dos elementos do array origem para os elementos do novo array.
A ordenação de elementos de arrays pode ser realizada automaticamente pela função ASORT(). Ela permite a ordenação ou classificação de todos os elementos de um array ou de um determinado conjunto deles. O critério de ordenação pode ser especificado por meio de um bloco de código.
Para localizar um elemento de um array contendo um determinado valor, utiliza-se a função ASCAN(). Ela realiza uma pesquisa nos elementos do array para localizar o elemento que contém um determinado valor especificado através de uma expressão ou bloco de código. Esta função pode pesquisar todos ou apenas um determinado conjunto dos elementos do array. Caso o valor desejado for encontrado, a função retorna o índice correspondente ao elemento. Caso contrário, o valor retornado será zero.
aFuncion := { "Marcos", 42, .T. } // Array unidimensional com as seguintes informações: Nome do funcionário, Idade e indicação se é CLT
Porém, um dos elementos pode ser as informações dos dependentes do funcionário. Nesse caso, a estrutura do array seria:
aFuncion := { "Marcos", 42, .T., aDepend }
Onde a estrutura do array aDepend seria Nome do Dependente, Grau de parentesco, Idade, Sexo:
aDepend := { { "Cláudia", 1, 35, "F" };
{ "Rian", 2, 7, "M" } }
Essa arquitetura do AdvPL permite que matrizes multidimensionais possam ser assimétricas. Isto acontece quando uma das matrizes intercaladas possui um número diferente de elementos das outras matrizes do mesmo nível ou da matriz na qual ela está inserida. Suponhamos que seja incluído o CPF no array de dependentes:
aDepend := { { "Cláudia", 1, 35, "F", "27847307849" };
{ "Rian", 2, 7, "M", "16668978119" } }
Neste exemplo, o array aFuncion contém 4 elementos em sua estrutura, enquanto o array aDepend contém 5 elementos.
Ao invés de referenciar outro array, as informações dos dependentes também poderiam ser gravadas diretamente no array aFuncion, como um dos seus elementos:
aFuncion := { "Marcos", 42, .T., { { "Cláudia", 1, 35, "F", "27847307849" },;
{ "Rian", 2, 7, "M", "16668978119" } } }
Criação de Arrays
A declaração dos arrays é realizada pelos mesmos comandos das variáveis dos demais tipos de dados: PRIVATE, PUBLIC, LOCAL e STATIC.
O array pode ser criado com um tamanho definido ou não. Caso não seja criado com um tamanho pré-definido, as suas dimensões são definidas durante a execução do programa. Vejamos o exemplo abaixo:
aExemp1 := { 10, 20, 30, 40, 50 }
aExemp2 := { "Clipper", aExemp1[3] + aExemp1[5], SQRT(a[1]), 1 + aExemp1[2] }
O exemplo abaixo é exatamente equivalente ao anterior:
LOCAL aExemp1[5]
LOCAL aExemp2[4]
aExemp1[1] := 10
aExemp1[2] := 20
aExemp1[3] := 30
aExemp1[4] := 40
aExemp1[5] := 50
aExemp2[1] := "Clipper"
aExemp2[2] := aExemp1[3] + aExemp1[5]
aExemp2[3] := SQRT(aExemp1[1])
aExemp2[4] := 1 + aExemp1[2]
Um array pode ser utilizado em qualquer ponto de um programa, inclusive como um elemento de outro array. Desta forma, pode-se especificar um array multidimensional:
aExemp3 := { { 1, 2, 3 }, { "A", "B", "C" }, { .F., DATE(), .F. } }
Caso os elementos do array fossem mostrados para o usuário na tela, teríamos os seguintes valores:
MSGALERT(aExemp3[1, 1]) // Resultado: 1
MSGALERT(aExemp3[2, 1]) // Resultado: "A"
MSGALERT(aExemp3[3, 3]) // Resultado: .F.
A função ARRAY() é usada para criar arrays com dimensões definidas. As dimensões do array a ser criado são especificadas como argumentos da função ARRAY(). Exemplo:
LOCAL aTabela := ARRAY(5, 10)
Nesse exemplo, criou-se um array com 5 linhas e 10 colunas.
Um array vazio é definido como sendo uma matriz sem elementos. Para criar um array vazio utiliza-se a seguinte instrução:
Os arrays vazios são úteis quando não se sabe, a princípio, o número de elementos que eles possuirão. Posteriormente, poderão ser utilizadas as funções AADD() e ASIZE() para mudar a sua estrutura, adicionando o número de elementos que forem necessários para o processamento da rotina.
Para testar se um array existe ou foi criado, utiliza-se a função VALTYPE(), que fornecerá "A" caso o seu argumento for um array. Por exemplo:
LOCAL aTabela := { }
IF VALTYPE(aTabela) == "A"
MSGALERT("A variável é um array")
ENDIF
Para testar se um array está vazio, ou seja, não possui elementos, utiliza-se a função EMPTY(), conforme exemplo abaixo:
IF EMPTY(aTabela)
MSGALERT("O array está vazio")
ENDIF
O número de elementos de um array pode ser determinado por meio da função LEN():
LOCAL aTabela[5, 8]
MSGALERT(LEN(aTabela))
A mensagem acima mostrará que o array aTabela possui 5 elementos.
Repare que a função LEN() fornece apenas o número de elementos da primeira dimensão do array aTabela. O motivo é que no AdvPL as matrizes multidimensionais são, na verdade, compostas por submatrizes unidimensionais intercaladas, contidas ou referenciadas por uma matriz principal. Ou seja, pode-se interpretar que o array aTabela possui uma dimensão com cinco elementos, onde cada um dos elementos desta dimensão contém uma referência a uma submatriz de oito elementos, formando, desta maneira, uma matriz de duas dimensões 5 x 8. É por este motivo que a função LEN(), quando aplicada ao array aTabela, fornece como resultado apenas cinco elementos.
Para determinar o número de elementos das submatrizes, ou seja, da segunda dimensão do array aTabela, deve ser aplicada a função LEN() ao primeiro elemento da matriz principal que contém a primeira submatriz, conforme o exemplo abaixo:
MSGALERT(LEN(aTabela[1]))
Nesse caso, a mensagem mostrará que o primeiro elemento possui 8 colunas.
Caso a matriz avaliada esteja vazia, a função LEN() fornece o valor zero.
Elementos de Arrays
Para acessar um elemento de um array deve-se identificá-lo pelo nome do array e, em seguida, entre colchetes, pelos seus índices. Por exemplo:
PRIVATE aTabela[5,8] // Cria a matriz aTabela
Quando um array é criado sem os valores definidos, cada um dos seus elementos tem o valor NIL atribuído. Esse valor persiste até que os elementos sejam iniciados com outros valores. O exemplo abaixo demonstra a atribuição de diversos dados em alguns elementos do array aTabela.
aTabela[1,1] := 100 // Equivale à aTabela[1][1] := 100
aTabela[2][3] := "AdvPL"
aTabela[5,4] := DATE()
aTabela[2][2] := .T.
aTabela[3,3] := aTabela[1,1] * 2
aTabela[4,4] := SQRT(aTabela[1,1])
Os índices que identificam os elementos de um array são sempre números inteiros e iniciam com o número 1. Os índices atribuídos aos elementos de um array devem ser compatíveis com as suas dimensões e com o número de elementos de cada dimensão. Caso isto não ocorra, será gerado um erro durante a execução do programa. Por exemplo:
A atribuição acima causará um erro, pois o array aTabela possui apenas 5 linhas.
O AdvPL também permite a criação e a iniciação de um array com base na estrutura de uma tabela de dados e das informações de uma pasta de arquivos contidas no servidor. Os respectivos comandos (DBSTRUCT e DIRECTORY) estão descritos no tópico Funções.
A função AFILL() é especialmente destinada à iniciação de elementos de arrays de uma única dimensão com um determinado dado. Por exemplo:
AFILL(aVetor, SPACE(10), 1, 10) // Inicia os 10 primeiros elementos do array unidimensional aVetor com dez espaços em branco.
AFILL(aVetor, 0, 11, 20) // Inicia os 10 últimos elementos do array unidimensional aVetor com o valor numérico zero.
Os elementos de um array podem ser iniciados com o resultado de qualquer expressão válida do AdvPL e com qualquer tipo de dado, incluindo blocos de código e referências a outros arrays. Uma vez iniciados, os elementos de um array podem ser utilizados como se fossem variáveis.
Como no AdvPL pode-se atribuir uma referência de um array para um elemento de outro array, a estrutura do array já existente pode ser alterada dinamicamente durante a execução do programa. Por exemplo:
PRIVATE aTabela[5,5] // O array foi criado com 5 linhas e 5 colunas.
aTabela[1, 1] := { 10, 20, 30, 50, 80 } // A estrutura do array foi alterada em tempo de execução.
Caso o conteúdo do elemento aTabela[1, 1, 5] fosse mostrado, apareceria o conteúdo 80.
Entretanto, caso se tentasse mostrar o conteúdo dos elementos abaixo, ocorreria erro, pois não existem esses elementos na estrutura do array:
aTabela[1, 2, 1]
aTabela[2, 1, 1]
Analisando esse exemplo, as três dimensões são válidas apenas para o elemento aTabela[1, 1], pois este passou a conter uma referência à outro array com uma dimensão. Este poderoso recurso do AdvPL permite que se explore recursos incríveis de programação. Contudo, como a estrutura de um array pode mudar dinamicamente, deve-se tomar cuidado com a sua utilização, pois existe o risco de se perder totalmente o controle de uma aplicação, tornando-a confusa e suscetível à erros.
Utilização de arrays como parâmetros de rotinas
Arrays podem ser passados como argumentos para funções. Quando a passagem é realizada por meio da sintaxe das funções, os arrays são, por definição, passados por valor. Isto significa que uma cópia da referência ao array é passada ao parâmetro que será recebido pela função invocada. Nessa situação, qualquer alteração realizada nos elementos do array pela função invocada se refletirá automaticamente no array original. Por exemplo:
LOCAL aLista[20] // O array é criado e todos os elementos recebem o valor NIL
AFILL(aLista, 1, 1, 10) // Atribui o valor 1 aos 10 primeiros elementos
AFILL(aLista, 2, 11, 20) // Atribui o valor 2 aos 10 últimos elementos
MSGALERT(aLista[1]) // Mostra o valor 1
MSGALERT(aLista[11]) // Mostra o valor 2
Dobro(aLista) // Executa a função Dobro()
MSGALERT(aLista[1]) // Mostra o valor 2
MSGALERT(aLista[11]) // Mostra o valor 4
// Função Dobro()
FUNCTION Dobro(aMat)
LOCAL nElem := 0
FOR nElem := 1 TO LEN(aMat)
aMat[nElem] := aMat[nElem] * 2
NEXT nElem
RETURN NIL
Neste exemplo, quando a função Dobro() é executada, uma cópia da referência ao array aLista é passada para o parâmetro aMat. A função Dobro() multiplica todos os elementos do array aLista por dois. Quando a função Dobro() é finalizada, a referência aMat é descartada, mas as alterações efetuadas nos valores dos elementos do array aLista são mantidas.
Um Array também pode ser fornecido como resultado de funções. Este recurso permite que uma função, que normalmente fornece apenas um único valor como resultado, forneça múltiplos valores através dos elementos de um array e pelo comando RETURN. Por exemplo:
aTabela := CriaTb(5, 10, SPACE(10))
// Função CriaTB, que alimenta o array de acordo com o número de linhas e colunas, e o conteúdo.
FUNCTION CriaTb(nLinhas, nColunas, cConteudo)
LOCAL aTab[nLinhas, nColunas]
FOR nElem1 := 1 TO nLinhas
FOR nElem2 := 1 TO nColunas
aTab[nElem1, nElem2] := cConteudo
NEXT nElem2
NEXT nElem1
RETURN aTab
Funções e operadores especiais para a manipulação de arrays
O operador duplo igual ( == ) é usado para comparar dois arrays, verificando se eles apontam para a mesma referência. Por exemplo:
LOCAL aTabela1 := { 1, 2, 3 }
LOCAL aLista := { 1, 2, 3 }
LOCAL aTabela2 := aTabela1 // Atribui a referência do array aTabela1 para aTabela2
IF aTabela1 == aTabela2
// Resultado: .T. (verdadeiro)
MSGALERT(“Os arrays aTabela1 e aTabela2 têm a mesma referência.”)
ENDIF
IF ! ( aTabela1 == aLista )
// Resultado: .F. (falso)
MSGALERT(“A referência dos arrays aTabela1 e aLista são diferentes”)
ENDIF
O AdvPL possui duas funções para mudar as dimensões de um array já existente:
- ASIZE( <array>, <elementos> )
A função ASIZE() redefine um array ( <array> ) de acordo com um novo número de elementos ( <elementos> ). Caso o novo número de elementos seja superior ao antigo, novos elementos serão adicionados, atribuindo-se o valor NIL para eles. Caso o novo número de elementos seja inferior ao antigo, os elementos que estiverem acima desse número serão removidos.
- AADD( <array>, <expressão> )
A função AADD() adiciona um novo elemento no array ( <array> ) especificado, atribuindo-lhe o resultado da expressão ( <expressão> ) especificada. O novo elemento será o último do array.
Para inserir ou eliminar elementos de um array já existente, o AdvPL possui duas funções. Ao contrário das funções AADD() e ASIZE(), estas funções não alteram as dimensões do array, mas apenas o conteúdo dos seus elementos.
- ADEL( <array>, <nº do elemento> )
A função ADEL() elimina o elemento do array ( <array> ) definido pelo número do elemento ( <nº do elemento> ). Todos os elementos localizados após o elemento eliminado são movidos uma posição para a esquerda. Ao último elemento é atribuído o valor NIL.
- AINS( <array>, <nº do elemento> )
A função AINS() insere um elemento no array ( <array> ) na posição definida pelo argumento <nº do elemento>. Ao novo elemento é atribuído o valor NIL. Os elementos localizados após o elemento inserido são movidos uma posição para a direita e o último elemento do array será destruído.
Para efetuar operações de cópia de arrays, o AdvPL possui duas funções:
Essa função copia um conjunto ou todos os elementos de um array origem para um array destino já existente. Caso o array origem contiver subarrays, o array destino conterá apenas referências a elas, e não cópias reais.
Essa função duplica um array origem inteiro para um novo array destino, criando um novo array e realmente copiando os valores dos elementos do array origem para os elementos do novo array.
A ordenação de elementos de arrays pode ser realizada automaticamente pela função ASORT(). Ela permite a ordenação ou classificação de todos os elementos de um array ou de um determinado conjunto deles. O critério de ordenação pode ser especificado por meio de um bloco de código.
Para localizar um elemento de um array contendo um determinado valor, utiliza-se a função ASCAN(). Ela realiza uma pesquisa nos elementos do array para localizar o elemento que contém um determinado valor especificado através de uma expressão ou bloco de código. Esta função pode pesquisar todos ou apenas um determinado conjunto dos elementos do array. Caso o valor desejado for encontrado, a função retorna o índice correspondente ao elemento. Caso contrário, o valor retornado será zero.
Bloco de código/CodeBlock
Blocos de código constituem um poderoso recurso do AdvPL. Eles permitem a exportação de pequenos pedaços de código executável de um lugar para outro dentro da aplicação. O AdvPL trata os blocos de código como valores de dados, permitindo assim que eles possam ser armazenados em variáveis, passados como argumentos para funções e usados como uma alternativa mais eficiente para o operador macro ( & ).
Definição
A sintaxe utilizada para a definição de um bloco de código é:
{ | [ <lista de argumentos> ] | <lista de expressões> }
Os argumentos ( <lista de argumentos> ) e as expressões ( <lista de expressões> ) devem ser separados por vírgulas. As expressões especificadas não podem conter comandos, estruturas de controle do fluxo de programação (WHILE ... ENDDO, IF ... ENDIF, etc.) ou declarações (LOCAL, PRIVATE, etc.).
As barras verticais ( | | ), que delimitam a lista de argumentos dos blocos de código, são obrigatórias, mesmo que não existam argumentos. A seguir, listamos alguns exemplos de blocos de código:
{ | | “Bloco sem argumentos” }
{ | x | x + 1 }
{ | a, b | Func1(a) + Func2(b) }
{ | i, j, k | Func(i), Func(j), Func(k) }
Como mencionado anteriormente, um bloco de código pode ser atribuído a uma variável ou elemento de array através do operador em linha ( := ).
Execução de Blocos de Código
A execução ou avaliação do resultado dos blocos de código é realizada pela função EVAL(), cuja sintaxe é:
EVAL( <bloco de código>, <lista de parâmetros> )
A função EVAL() executa o bloco de código especificado ( <bloco de código> ), passando-lhe os argumentos contidos na lista de parâmetros ( <lista de parâmetros> ). As expressões contidas no bloco de código são executadas e avaliadas uma por uma, da esquerda para a direita. O valor resultante do bloco de código será o resultado da última expressão. Por exemplo:
bCodBloc := { | nNum1, nNum2 | nNum1 * nNum2 }
MSGALERT( EVAL(bCodBloc, 2, 3) )
O resultado da expressão acima será 6.
Além da função EVAL(), as funções AEVAL() e DBEVAL() também executam blocos de código. Estas funções processam, respectivamente, arrays e arquivos de dados, executando o bloco de código especificado para cada elemento do array ou registro do arquivo de dados.
Utilização de Blocos de Código
Os blocos de código são particularmente utilizados na implementação de rotinas flexíveis, ou seja, que possam ser executadas independentemente do tipo de dado passado para elas.
O exemplo abaixo mostra a função Mensagem sem a utilização de blocos de código. Neste caso, a função depende do tipo de dado passado.
// Apresenta, por uma determinada quantidade de vezes, um dado na tela.
FUNCTION Mensagem(xDado, nVezes)
LOCAL nRepet := 0
IF VALTYPE(xDado) == “C”
// Dado do tipo caracter
xDado := xDado
ELSEIF VALTYPE(xDado) == “N”
// Dado do tipo numérico
xDado := LTRIM(STR(xDado))
ELSEIF VALTYPE(xDado) == “D”
// Dado do tipo data
xDado := DTOC(xDado)
ELSEIF VALTYPE(xDado) == “L”
// Dado do tipo lógico
xDado := IIF(xDado, “Verdadeiro”, “Falso”)
ELSE
// Outro tipo de dado
xDado := “Indefinido”
ENDIF
FOR nRepet := 1 TO nVezes
MSGALERT(xDado)
NEXT nRepet
RETURN
Para tornar a função Mensagem mais genérica, independentemente do tipo de dado passado, poderiam ser utilizados os seguintes blocos de código:
bCarCod := { | xDado | xDado }
bNumCod := { | xDado | LTRIM(STR(xDado)) }
bLogCod := { | xDado | IF(xDado, “Verdadeiro”, “Falso”) }
bDatCod := { | xDado | DTOC(xDado) }
Passando os blocos de código definidos acima, a função Mensagem poderia ter a seguinte forma:
// Apresenta, por uma determinada quantidade de vezes, um dado na tela.
FUNCTION Mensagem(xTexto, bBloco, nVezes)
LOCAL nRepet := 0
FOR nRepet := 1 TO nVezes
MSGALERT(EVAL(bBloco, xTexto))
NEXT nRepet
RETURN
Portanto, uma das utilizações básicas dos blocos de código é na passagem de parâmetros para funções que tratem genericamente os dados, tornando-os assim mais flexíveis e independentes, pois são executados de acordo com o bloco de código passado.
A utilização de blocos de código é um importante recurso da linguagem AdvPL. À medida que nos familiarizamos com esse recurso, ele torna-se mais simples e fácil de ser entendido e utilizado. Quando bem compreendido e utilizado, o bloco de código torna o código-fonte mais genérico, flexível e modular, aumentando a produtividade na manutenção e no desenvolvimento de novos programas.
Armazenamento de Blocos de Código
Blocos de código não podem ser armazenados diretamente em campos de arquivos de dados, a menos que sejam transformados em variáveis do tipo caracter. Para utilizá-los posteriormente, utiliza-se o operador macro ( & ), conforme exemplo abaixo:
FUNCTION Mensagem()
LOCAL bBloco := “”
// Grava o bloco de código no campo da Tabela SE2 (Título a Pagar) como uma variável caracter
SE2->E2_TEXTO := “ { |xDado| Func1(xDado) } ”
// Atribui o bloco de código à variável bBloco
bBloco := &(SE2->E2_TEXTO)
// Executa o bloco de código armazenado na variável bBloco
EVAL(bBloco, xDado)
RETURN NIL
Operadores
O AdvPL possui nove tipos de operadores, que permitem a construção de expressões sobre valores de dados, variáveis, campos de arquivos de dados e funções. Os nove tipos são:
- Caracteres
- Datas
- Matemáticos
- Relacionais
- Lógicos
- Atribuidores
- Incrementadores
- Especiais
- Macro
Alguns operadores são unários, ou seja, requerem apenas um operando, enquanto outros são binários, isto é, requerem dois operandos. Operandos são os dados, variáveis, campos ou funções sobre os quais os operadores atuam.
Operadores de Caracteres
Todos os operadores de caracteres são binários, ou seja, sempre atuam sobre duas expressões de dados tipo caracter. Existe apenas o operador mais ( + ), também conhecido como concatenação, que une duas cadeias de caracteres em uma única. Por exemplo:
cTexto1 := "AdvPL "
cTexto2 := " – Linguagem de Programação da TOTVS"
cTexto3 := cTexto1 + cTexto2
conout(cTexto3) // Resulta: "AdvPL - Linguagem de Programação da TOTVS"
Operadores de Datas
Dados tipo data podem ser somados ou subtraídos em termos de número de dias por meio dos operadores soma ( + ) e subtração ( - ). Uma adição de dias a uma data gera uma nova data no futuro, enquanto que uma subtração de dias gera uma nova data no passado. Por exemplo:
MSGALERT(date()) // Supondo que a data atual é 31/12/11, essa data é que será mostrada.
MSGALERT(date() + 1) // Resulta: 01/01/12
MSGALERT(date() – 1) // Resulta: 30/12/11
MSGALERT(1 + date()) // Resulta: 01/01/12
Uma subtração entre duas datas resulta num valor numérico representando o intervalo de dias entre elas. Por exemplo:
MSGALERT(CTOD("25/12/90") – CTOD("19/12/90")) // Resulta: 6. Ou seja, seis dias de intervalo.
MSGALERT(CTOD("19/12/90") – CTOD("25/12/90")) // Resulta: -6.
Operadores Matemáticos
Todos os operadores matemáticos, excetuando-se os sinais unários de positivo e negativo, são binários e operam sobre expressões ou dados numéricos, gerando outros valores numéricos como resultado. São os seguintes:
Operador | Operação Matemática |
+ | Adição ou unário positivo |
- | Subtração ou unário negativo |
* | Multiplicação |
/ | Divisão |
^ | Exponenciação |
% | Módulo (resto da divisão) |
O operador módulo calcula o resto da divisão de uma expressão numérica por outra. Assim, 4 % 3 resulta 1, por exemplo.
Nas operações de cálculo, a ordem normal de precedência para a execução das operações matemáticas é:
- Os operadores unários: + (positivo) ou (negativo);
- A exponenciação ( ^ );
- O módulo ( % ), a multiplicação ( * ) e a divisão ( / );
- A adição ( + ) e a subtração ( - ).
Os parênteses são utilizados para agrupar operações e alterar a precedência das operações. Por exemplo:
MSGALERT(4 * 2 + 1) // Resulta 9
MSGALERT(4 * (2 + 1)) // Resulta 12
Podem ser definidos tantos níveis de parênteses quantos forem necessários, desde que o seu balanceamento esteja correto. Ou seja, o número de abre parênteses deve ser idêntico ao número de fecha parênteses.
Operadores Relacionais
Os operadores relacionais são binários e geram resultados lógicos (verdadeiro ou falso) a partir da comparação entre duas expressões. Podem ser utilizados em expressões numéricas, caracter, data ou NIL. As expressões relacionadas devem ser obrigatoriamente do mesmo tipo de dado. Os operadores relacionais do AdvPL são:
Operador | Operação Relacional |
< | Menor que |
> | Maior que |
= | Igual a |
== | Exatamente igual a |
<> | Diferente de ou Não igual |
# | Diferente de ou Não igual |
!= | Diferente de ou Não igual |
<= | Menor ou igual a |
>= | Maior ou igual a |
$ | Comparação de subcadeia de caracteres |
Quando se relacionam duas expressões através destes operadores, a expressão da direita é comparada com a da esquerda. O resultado dessa comparação é um valor lógico verdadeiro (.T.) ou falso (.F.). Por exemplo:
nNum1 := 100
nNum2 := 200
MSGALERT(nNum1 > nNum2) // Resulta falso (.F.), pois 100 não é maior do que 200.
MSGALERT(nNum2 > nNum1) // Resulta verdadeiro (.T.), pois 200 é maior do que 100.
MSGALERT(nNum1 <> nNum2) // Resulta verdadeiro (.T.), pois 100 é diferente de 200.
MSGALERT("AdvPL" = "Adv") // Resulta verdadeiro (.T.)
MSGALERT("Apple" <> "Aple" // Resulta verdadeiro (.T.)
Os operadores relacionais possuem apenas uma ordem de precedência na qual as operações são realizadas: da esquerda para a direita.
O operador $ (subcadeia ou substring) opera apenas sobre expressões do tipo caracter. Ele compara a expressão da esquerda com a da direita, retornando verdadeiro se a expressão da esquerda está contida na da direita ou se as duas expressões são idênticas. Por exemplo:
cTextoA := "AdvPL"
cTextoB := "Programas"
cTextoC := " AdvPL - Linguagem para Geração de Programas da TOTVS"
MSGALERT(cTextoA $ cTextoB) // Resulta falso (.F.)
MSGALERT(cTextoA $ cTextoC) // Resulta verdadeiro (.T.)
MSGALERT(cTextoB $ cTextoC) // Resulta verdadeiro (.T.)
MSGALERT(cTextoC $ cTextoA) // Resulta falso (.F.)
No caso do operador igual ( = ) para expressões numéricas, caso os números comparados possuam diferença inferior a 0,00000005 ( 5 x 10-8 ), o retorno será verdadeiro (.T.).
O operador exatamente igual ( == ) atua de maneira idêntica ao operador igual ( = ) para expressões numéricas ou tipo data. No caso de expressões do tipo caracter, retornará verdadeiro apenas se a expressão da direita for exatamente idêntica à expressão da esquerda. Por exemplo:
cTextoA := "Adv"
cTextoB := "AdvPL"
MSGALERT(cTextoB = cTextoA) // Resulta verdadeiro (.T.)
MSGALERT(cTextoB == cTextoA) // Resulta falso (.F.)
Quando dois arrays são comparados, o operador duplo igual ( == ) retorna verdadeiro (.T.) se ambos possuem o mesmo conteúdo. Ou seja, se são idênticos, possuindo os mesmos elementos e valores.
Operadores Lógicos
Os operadores lógicos fornecem valores lógicos (falso / .F. ou verdadeiro / .T.) a partir da comparação de duas expressões lógicas, que são os seus operandos. Com exceção do operador .NOT. ou !, todos são binários. Os operadores lógicos do AdvPL são:
Operador | Operação Lógica |
.AND. | Conector lógico E |
.OR. | Conector lógico OU |
.NOT. ou ! | Conector lógico NÃO |
A ordem de precedência para os operadores lógicos é:
- Negação (.NOT. ou !)
- Conector E (.AND.)
- Conector OU (.OR.)
Os operadores lógicos relacionam duas expressões lógicas. Os exemplos abaixo demonstram alguns casos:
nNumA := 100
nNumB := 200
nNumC := 300
MSGALERT(nNumB > nNumA .AND. nNumC > nNumB) // Resulta verdadeiro (.T.)
MSGALERT(nNumB > nNumA .AND. nNumB = nNumC) // Resulta falso (.F.)
MSGALERT(nNumB > nNumA .OR. nNumB = nNumC) // Resulta verdadeiro (.T.)
O operador .NOT. ou ! (negação) é unário. Ou seja, atua sobre uma única expressão. Caso a expressão seja verdadeira, transformá-la-á em falsa, e vice-versa. Por exemplo:
lCont := .T.
MSGALERT( .NOT. lCont ) // Resulta falso (.F.)
MSGALERT( ! lCont ) // Resulta falso (.F.)
// Função para pesquisar o código do cliente
FUNCTION PesqCli(cCodigo)
dbSelectArea("SA1")
dbSetOrder(1)
dbSeek(cCodigo)
IF ! FOUND() // Se não foi encontrado
RETURN // Finaliza
ENDIF
Para compreender melhor o resultado dos operadores lógicos, verifique na tabela abaixo as situações nas quais é fornecido o resultado verdadeiro (.T.):
Operador | Resultado |
.AND. | Fornece verdadeiro (.T.) se ambos os operandos forem verdadeiros (.T.) |
.OR. | Fornece verdadeiro (.T.) se apenas um dos operandos for verdadeiro (.T.) |
.NOT. ou ! | Fornece verdadeiro (.T.) se o seu operando for falso (.F.) |
Operadores de Atribuição
Os operadores de atribuição relacionados abaixo atribuem valores ou dados para as variáveis, campos ou elementos de arrays:
Operador | Operação de Atribuição |
= | Atribuição normal |
:= | Atribuição em linha de instrução |
+= | Adição e atribuição em linha de instrução |
-= | Subtração e atribuição em linha de instrução |
*= | Multiplicação e atribuição em linha de instrução |
/= | Divisão e atribuição em linha de instrução |
^= | Exponenciação e atribuição em linha de instrução |
%= | Módulo e atribuição em linha de instrução |
Todos os operadores de atribuição são binários. Ou seja, requerem dois operandos. O primeiro deve ser obrigatoriamente o nome de uma variável e o segundo pode ser qualquer expressão válida, incluindo o valor NIL.
Para os operadores compostos, isto é, que realizam uma operação matemática e outra de atribuição, o primeiro operando (a variável à qual será atribuído o valor de uma expressão) deve ter sido previamente criado e ser do mesmo tipo de dado que o segundo operando.
A tabela abaixo relaciona os tipos de dados que podem ser operados por cada um dos operadores de atribuição.
Operador | Tipos de dados válidos |
= | Qualquer tipo de dado |
:= | Qualquer tipo de dado |
+= | Caracter, data, memo e numérico |
-= | Caracter, data, memo e numérico |
*= | Numérico |
/= | Numérico |
^= | Numérico |
%= | Numérico |
O operador igual ( = ) atribui um valor para uma variável ou campo de arquivo de dados. O primeiro operando deve ser o nome de um campo de um arquivo de dados ou o nome de uma variável. Por exemplo:
nNum = 100 // Atribui o valor 100 à variável nNum
cNome = "Laércio Cosentino" // Atribui a cadeia de caracteres à variável cNome
SA1->A1_NOME = cNome // Atribui o conteúdo da variável cNome ao campo do Cadastro de Clientes
O operador em linha ( := ) é semelhante ao operador igual ( = ). Contudo, ele pode ser utilizado dentro de qualquer comando ou função cuja sintaxe permita o uso de uma expressão. Por exemplo:
// Declara variáveis privadas e as inicializa
PRIVATE nNum1 := 100
PRIVATE nNum2 := 300
IF (total := nNum1 + nNum2) > 300 // Condição utilizando o operador em linha
SA1->A1_OBS := SPACE(30) // Grava-se 30 espaços em branco no campo do arquivo de dados
Os operadores de atribuição compostos realizam uma operação entre dois operandos e depois atribuem o resultado ao primeiro. A tabela abaixo esclarece o seu modo de funcionamento. Note que todos os operadores compostos são do tipo em linha.
Operador | Utilização | Operação |
+= | A += B | A := A + B |
-= | A -= B | A := A – B |
*= | A *= B | A := A * B |
/= | A /= B | A := A / B |
%= | A %= B | A := A / B |
^= | A ^= B | A := A ^ B |
Operadores de Incremento e Decremento
Os operadores de incremento são unários, ou seja, operam sobre um único operando do tipo numérico ou data. O operando deve ser o nome de uma variável e não pode ser um campo de arquivo de dados. O tipo de dado resultante da operação será idêntico ao do operando. A tabela abaixo relaciona estes operadores e a sua operação:
Operador | Utilização | Operação |
++ | A++ | A := A + 1 |
-= | A-- | A := A – 1 |
++ | ++A | A := A + 1 |
-= | --A | A := A – 1 |
O operador duplo mais ( ++ ) incrementa o seu operando em uma unidade, enquanto que o operador duplo menos ( -- ) decrementa o seu operando em uma unidade. Podem ser utilizados como prefixo ou como sufixo.
Utilizados como prefixo, alteram o operando antes de efetuar a atribuição. Utilizados como sufixo, eles alteram o operando depois de realizada a atribuição. Os exemplos abaixo ilustram algumas operações típicas com estes operadores.
nNum1 := 0
nNum2 := ++nNum1
MSGALERT(nNum1) // Resulta 1
MSGALERT(nNum2) // Resulta 1
nNum2 := 1
nNum4 := nNum2--
MSGALERT(nNum2) // Resulta 0
MSGALERT(nNum4) // Resulta 1
nNum5 := 2
MSGALERT(nNum5++ * nNum5) // Resulta 6
MSGALERT(nNum5) // Resulta 3
MSGALERT(nNum6 := 2)
MSGALERT(--nNum6 * nNum6) // Resulta 1
MSGALERT(nNum6) // Resulta 1
Operadores Especiais
A tabela abaixo relaciona os operadores especiais do AdvPL e o seu respectivo objetivo.
Operador | Objetivo |
( ) | Função ou agrupamento |
[ ] | Especifica um elemento de um array |
{ } | Definição de um array ou de blocos de código |
-> | Identificador de alias |
@ | Passagem de parâmetros por referência |
Além de participar da definição de funções, envolvendo a lista de parâmetros, os parênteses são utilizados em expressões para agrupar determinadas operações que devem ser avaliadas primeiramente. Os parênteses são utilizados para modificar e determinar a ordem na qual as operações deverão ser efetuadas. As operações que estiverem dentro dos parênteses mais internos serão executadas em primeiro lugar, e assim por diante, até a avaliação do valor final da expressão.
De forma geral, as operações são efetuadas dos parênteses mais internos para os mais externos, sendo inicialmente executadas as operações matemáticas, seguidas das comparações relacionais. Finalmente, são avaliados os conectores lógicos. Por exemplo, o resultado da expressão abaixo é verdadeiro (.T.).
lResult := ( 4 * ( 3 + 5 ) ) / 2 > 100 .OR. ( 5 * ( 3 + 4 ) ) / 2 < 100
Na execução de funções, os parênteses são utilizados após o nome identificador da respectiva rotina, envolvendo a lista de argumentos. Por exemplo:
MSGALERT(SQRT(nNum1)) // Mostra na tela a raiz quadrada do número contido na variável nNum1
MSGALERT("Este é um teste!") // Mostra uma mensagem na tela
MSGALERT(DATE()) // Mostra na tela a data atual
Os colchetes ( [ ] ) são utilizados em referências a elementos de arrays. Por exemplo:
PRIVATE aTeste[10, 10] // Declara o array aTeste com 10 linhas e 10 colunas
aTeste[1, 1] := 100 // Atribui o valor 100 ao primeiro elemento do array aTeste
MSGALERT(aTeste[1, 1]) // Mostra na tela o valor desse elemento do array. Resulta 100.
As chaves ( { } ) são utilizadas para criar e referenciar matrizes literais, com apenas uma dimensão. Os elementos da matriz devem estar entre as chaves, separados por vírgulas. As chaves também são utilizadas na criação de blocos de código. Por exemplo:
PRIVATE aMatriz := { 10, 20, 30, 40, 50, 98 } // Cria um array com apenas uma dimensão
MSGALERT(aMatriz[3]) // Apresenta o valor 30 na tela
bBloco := { | nDado, nCoef | nDado * nCoef } // Cria um bloco de código com dois argumentos
O identificador de alias ( -> ) é utilizado para fazer uma referência explícita a um campo de um arquivo de dados. Se o nome de uma área de trabalho (alias) preceder este operador, ele pode ser seguido por um nome de campo do arquivo de dados correspondente ou por qualquer expressão válida que contenha referências ao arquivo de dados aberto na área de trabalho, cujo alias foi especificado. Por exemplo:
SA1->A1_NOME := cNome // Refere-se ao campo A1_NOME da tabela SA1 (Cadastro de Clientes)
Finalmente, o operador de passagem de parâmetros por referência ( @ ) é usado precedendo argumentos de funções. É um operador unário que opera como prefixo de nomes de variáveis, indicando que o argumento está sendo passado por referência, ao invés de por valor, como é o normal, para a função invocada. Por exemplo:
// Executa (Invoca) a função Pesquisa, passando os argumentos cCod e cCodF por referência.
Pesquisa(@cCod, @cCodF, "Códigos Desejados", "999")
Outro exemplo de passagem de parâmetros por referência com a utilização do operador @:
FUNCTION Main()
LOCAL nA := 0
LOCAL nB := 1
LOCAL nC := 2
LOCAL nX := 0
nX := Calculo(@nA, nB, nC)
MSGALERT(nX) // Resulta 6
MSGALERT(nA) // Resulta 3
// O valor da variável nA foi alterado porque ela foi passada por referência à função Calculo()
RETURN NIL
// Função de cálculo
FUNCTION Calculo(nParam1, nParam2, nParam3)
LOCAL nRetorno := 0
nParam1 := nParam2 + nParam3
nRetorno := nParam1 * nParam2 * nParam3
RETURN nRetorno
O Operador Macro ( & )
O operador macro permite a compilação de expressões durante a execução dos programas e a substituição do nome de variáveis ou expressões tipo caracter pelo seu conteúdo ou resultado. Sempre que o operador macro ( & ) for encontrado, a expressão será submetida ao compilador do AdvPL.
Uma expressão macro deve ser especificada através da seguinte sintaxe:
A expressão macro deve, obrigatoriamente, ser colocada entre parênteses. A operação macro é executada sobre o seu resultado, que deve ser do tipo caracter. Esse recurso possibilita que o conteúdo de campos de arquivos de dado e elementos de arrays sejam compilados e executados como parte do código do programa, desde que contenham uma expressão ou um bloco de código.
O operador macro ( & ) pode executar substituições de textos ou compilação de expressões durante a execução de um programa, dependendo de como for especificado. Por exemplo:
cMacro := "colega"
MSGALERT("Como vai " + &(cMacro) + "?") // Resulta: Como vai colega?
// Neste outro exemplo, será mostrado o nome do cliente. Por exemplo, o ponteiro do índice do arquivo
// está posicionado no registro Carlos Alberto Montagnini. Neste caso, a mensagem será:
// Como você está, Carlos Alberto Montagnini?
MSGALERT("Como você está, " + &(SA1->A1_NOME) + "?")
cMacro := "DTOC(DATE())"
MSGALERT( &(cMacro) ) // Resulta "22/05/12"
O operador macro não pode ser usado para substituir ou compilar as palavras-chave que identificam os comandos. Por exemplo:
cMacro := "PRIVATE"
&(cMacro) nJuros := 0 // Não é válido!!!
No caso de alias de arquivos de dados, não é necessário utilizar o operador macro ( & ). Basta utilizar o abre e fecha parênteses. Por exemplo:
Ordem de Precedência dos Operadores
Quando vários operadores distintos são utilizados em uma mesma expressão, a ordem de precedência para a execução das operações é:
Ordem | Operação |
1 | Chamadas a funções |
2 | Operadores especiais |
3 | Operador macro |
4 | Pré-incremento / Pré-decremento |
5 | Operadores de caracteres |
6 | Operadores matemáticos |
7 | Operadores relacionais |
8 | Operadores lógicos |
9 | Operadores de atribuição |
10 | Pós-incremento / Pós-decremento |
Os parênteses podem ser utilizados para modificar a ordem na qual as operações serão efetuadas. As operações que estiverem dentro dos parênteses mais internos serão executadas em primeiro lugar, e assim por diante, até a obtenção do resultado final da expressão que está sendo avaliada.
Dentro de uma mesma classe de operadores, com exceção dos operadores matemáticos e lógicos, as operações possuem a mesma precedência, sendo sempre executadas da esquerda para a direita. Entretanto, no caso particular de múltiplas atribuições em linha ( := ), as atribuições são executadas da direita para a esquerda.
Quando vários operadores matemáticos aparecem numa mesma expressão, a ordem de precedência seguida pelo AdvPL é:
Ordem | Operação |
1 | Os sinais unários positivo ( + ) e negativo ( - ) |
2 | Exponenciação ( ^ ) |
3 | Multiplicação ( * ), divisão ( / ) e módulo ( % ) |
4 | A adição e a subtração |
Assim como os operadores matemáticos, quando vários operadores lógicos aparecem numa mesma expressão, eles possuem a sua própria ordem de precedência:
Ordem | Operação |
1 | Operador de Negação ( .NOT. ou ! ) |
2 | Conector aditivo ( .AND. ) |
3 | Conector alternativo ( .OR. ) |
Embora o AdvPL defina automaticamente uma ordem de precedência para os seus operadores, é recomendável que se utilizem os parênteses para determinar a precedência, mesmo quando forem dispensáveis. Essa prática facilita a leitura e a compreensão dos programas.