SDK, é a sigla de Software Development Kit, ou seja, Kit de Desenvolvimento de Software ou Kit de de Desenvolvimento de Aplicativos.

A linha Microsiga Protheus disponibiliza um extenso SDK para desenvolvimento de processos de negócio no sistema.

A documentação deste SDK pode ser utilizada pelos desenvolvedores, parceiros, terceiros e clientes da linha Microsiga Protheus.


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.

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"

 

Os campos do tipo Caracter são limitados a 1MByte até as build 2012 e à 4MByte para as builds superiores à 2013 do TOTVS|Appserver.

 

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.

 

Os campos do tipo Memo são limitados a 1MByte até as build 2012 e à 4MByte para as builds superiores à 2013 do TOTVS|Appserver.


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)

 

Os campos do tipo Númerico são limitados a 15-16 digitos de precisão númerica e podem armazenar número entre 5*10**-324 e 1.7*10^308.


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.

 

O retorno lógico de uma expressão Nula ( Nil ) é Falsa (.F.)

 

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

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,11,21,31,41,51,61,71,8
2,12,22,32,42,52,62,72,8
3,13,23,33,43,53,63,73,8
4,14,24,34,44,54,64,74,8
5,15,25,35,45,55,65,75,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:

LOCAL aTabela := { }


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:

aTabela[6,1] := 1

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

Importante : Mesmo que os arrays aTabela1 e aLista possuam o mesmo número de elementos e conteúdo de cada elemento seja o mesmo entre os arrays, eles são arrays independentes; isto é; um array não é referencia do outro, então o operador de igualdade entre os arrays retorna .F. . Se o tipo de comparação desejada for efetivamente de conteúdo entre dois arrays, você deve inicialmente verificar se o tamanho de ambos usando a função LEN() com cada um deles, e caso o tamanho seja o mesmo, você deve verificar se os seus elementos são iguais, onde caso um elemento do array seja outro array ( array multi-dimensional ), eles devem ser comparados da mesma forma.

 

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:

  • ACOPY()

 

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.

  • ACLONE()

 

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:

LOCAL aTabela := { }

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:

aTabela[6,1] := 1

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

Importante : Mesmo que os arrays aTabela1 e aLista possuam o mesmo número de elementos e conteúdo de cada elemento seja o mesmo entre os arrays, eles são arrays independentes; isto é; um array não é referencia do outro, então o operador de igualdade entre os arrays retorna .F. . Se o tipo de comparação desejada for efetivamente de conteúdo entre dois arrays, você deve inicialmente verificar se o tamanho de ambos usando a função LEN() com cada um deles, e caso o tamanho seja o mesmo, você deve verificar se os seus elementos são iguais, onde caso um elemento do array seja outro array ( array multi-dimensional ), eles devem ser comparados da mesma forma.

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:

  • ACOPY()

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.

  • ACLONE()

 

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. 

Status do documentoConcluído
Data31/07/2014
Versão1.0
Versão anterior1.0
Autores

Marcos Cesar Pires Gomes

Revisores

Eduardo Perusso Riera

Ivan Pinheiro Cabral

Índice resumido
Índice