Вы находитесь на странице: 1из 53

Programao Bsica em AdvPl ( padro xBase/CodBase ) Curso 1

A linguagem AdvPl um Super-Conjunto da linguagem padro xBase/CodBase e evoluiu partir do padro do Clipper 5.3 e das bibliotecas do FiveWin. Ela comporta quase todos os comandos e funes utilizados no Clipper 5.3 e no FiveWin. O AdvPl a liguagem utilizada pela Microsiga para o desenvolmento do ERP e no desenvolvimento de rotinas especficas nas empresas usurias desse sistema. Os elementos sintticos que formam as instrues de programao em AdvPl podem ser divididas nos seguintes grupos: instrues, comandos, diretivas, funes, classes, mtodos, constantes , campos e operadores. Instrues e Comandos: As instrues so elementos fixos da linguagem, que lidam com afirmaes e com o controle do programa; c := IF( a > b , a , b ) While ( ( cAlias )->( !Eof() ) //... mais instrues ( cAlias )->( dbSkip() ) End While nLoops := 10 For nLoop := 1 To nLoops //... mais instrues Next nLoop ... J os comandos, representam o conjunto de tipos de instrues da linguagem que no so nem rotinas nem chamadas de funo e nem atribuies. Replace cCampo1 With Conteudo do Campo 1,; cCampo2 Whit Conteudo do Campo 2

Obs.: Como padro, um caractere de final de linha encerra uma instruo. Para continuar na outra linha, use o caractere de ponto-e-vrgula.

Use cTable New Via DBFCDX Copy To cNewTable Via DBFCDX

Totas as instrues so fixadas em determinada verso da linguagem e, todos os comandos so convertidos pelo pr-processador em chamadas de funes e atribuies. Onde: Replace cCampo1 With Conteudo do Campo 1,; cCampo2 Whit Conteudo do Campo 2

ser convertido pelo pr-processador em : _FIELD->cCampo1 := "Conteudo do Campo1"; _FIELD->cCampo2 := "Conteudo do Campo2 Use cTable New Via DBFCDX ser convertido pelo pr-processador em : dbUseArea( .T., "DBFCDX", "cTable",, if(.F. .OR. .F., !.F., NIL), .F. ) Copy To cNewTable Via DBFCDX ser convertido pelo pr-processador em : __dbCopy( "cNewTable", { },,,,, .F., "DBFCDX" ) No AdvPl so permitidas vrias instrues numa linha ( separando-as com um caracter de ponto-e-vrgula ) como em: i := 0 ; j := 1 ; a := c ; d := 99 //... Esse recurso interessante ao criar macros do compilador e blocos de codificao, mas se utilizado em todo lugar prejudica a legibilidade do programa. Comentrios: O compilador Advpl ignora as linhas em branco e a endentao. Ambas tornam o programas mais legvel e, devemos abrir mo deste recurso para tornar os programas mais legveis. Os comentrios tambm so ignorados pelo compilador. Na realidade, as linhas em branco, a identao e os comentrios nos programas so tratados pelo pr-processador. No AdvPl programa: so permitidas as seguintes formas de se comentar um

//Para efetuar o comentrio em uma linha. ( Tudo o que estiver direita //das ( // ) ser considerado como um comentrio.
Obs.: Pode-se utilizar o sinal de * ou && no inicio da frase para se efetuar o comentrio de uma nica linha. Deve-se escolher uma por padro e ser coerente.

/* Para efefuar o comentrio em mais de uma linha. ( Tudo o que estiver entre ( /* e */ ) ser considerado como comentrio. */
Obs.: para facilitar a utilizao do modelo de comentrio acima utilize /*/ para abrir o comentrio e /*/ para garantir que um comentrio nunca deixar de ser fechado.

altamente recomendvel que as rotinas importantes e de utilidade sejam comentadas com um bloco de abertura. Devero ser especificados: o propsito da rotina, os tipos de parmetros e, se for uma funo que retorna algo, o valor de retorno. /*/ Funo Autor Data Verso SomaUm( nLast ) nLast /*/
Obs.: A melhor documentao de um programa o prprio programa ( desde que bem escrito e seguindo-se as regras que foram estabelecidas )

: : : :

SomaUm( nLast ) Marinaldo de Jesus 18/02/2005 1

-> Retorna valor de nLast incrementado de 1 -> Valor onde dever ser somado um

Constantes: Constantes so Valores predefinidos que no mudam durante o programa. Elas podem ser strings de caracteres, nmeros, valores lgicos, blocos de codificao, arrays ( vetores ). Ex.: pressione uma tecla para continuar 15 1970.15 .T. { || MyTest() } {Domingo,Segunda,Tera,Quarta,Quinta,Sexta,Sbado} No Advpl, as constantes numrias so sempre decimais. No so suportados outros sistemas de nmeros nem notao cientfica.

No h diferena entre caracteres isolados a e strings Isso uma string. Uma string simplesmente um ou mais caracteres. Para definir uma constante do tipo string maior que uma linha, continue-a com um ponto e vrgula e junte as partes com o operador + Ex.: esta string ser continuada+; na prxima linha

No AdvPl no existem constantes de datas. Ser necessrio contru-las partir de uma string de caracteres e com a funo CToD() como no exemplo: Ex.: Ctod( 18/02/2005) O resultado ser uma data, mas ela no ser realmente uma constante. Pois o sistema na execuo a avaliar. As constantes so sempre avaliadas no momento da compilao. Comandos vs Literais: Alguns comandos em AdvPl exigem argumentos literais. Uma literal uma sequancia de caracteres interpretada literalmente. No ser possvel usar uma constante ou expresso no lugar da literal. Ex.: Use cTable No exemplo acima, cTable uma literal. Ela interpretado literalmente, no como uma varivel. O pr-compilador ir transformar a Literal em uma string de caracter da seguinte forma: dbUseArea( .F.,, "cTable",, if(.F. .OR. .F., !.F., NIL), .F. ) Para que cTable no seja considerado como uma literal mas sim como uma varivel coloque-a entre parenteses como em: Use ( cTable ) Que o pr-processador converter em: dbUseArea( .F.,, ( cTable ),, if(.F. .OR. .F., !.F., NIL), .F. )

Variveis: No Advpl, as regras para a nomeao de identificadores so parecidas com as das linguagens tpicas de computadores. A nomeao poder ser iniciada com uma letra ou um caractere de sublinhado e podero conter letras, sublinhados e dgitos. Para manter compatibilidade com o legado foi mantida a limitao de dez primeiros caracteres como significativos. Por isso, deve-se ter muito cuidado com nomes extensos. Se for utilizar nomes extensos lembre-se que atualmente apenas os dez primeiros caracteres sero considerados mas, se declarar uma varivel com nome extenso, utilize-a da forma como foi declarada para que, se um dia a limitao de dez caracteres foi modificada, no seja necessrio estar modificando os programas. A restrio em dez caracteres pode ser fonte de defeitos ardilosos. Por exemplo, as seguintes variveis: cSNome_do_Pai cSNome_do_Avo Sero consideradas idnticas. A truncagem dos identificadores longos conseqncia do tamanho fixo dos nomes nas tabelas mantidas pelo compilador e pelo pr-processador, portanto os nomes completos dos identificadores so passados sem alterao para o pr-processador. Tenha cuidado, esse tipo de erro muito difcil de se depurar. O estilo de codificao recomendada usar letras masculas para destacar partes significativas de um identificador ao inves de usar sublinhados para serpar-las. Dessa forma ser possvel criar identificadores menores, sem afetar a legibilidade. Como em: cSNomeDoPai cSNomeDoAvo Apesar dos identificadores acima terem mais de dez caracteres, os 10 mais significativos no so conflitantes. O primeiro ser considerado como cSNomeDoPa e o segundo como cSNomeDoAv
Lembre-se: Somente os dez primeiros caracteres do nome de uma varivel so significativos.

Uma varivel tem os seguintes atributos: Tipo Tamanho Durao Visibilidade

Tipo: O Advpl suporta os seguintes Tipos: Array Bloco Caractere ( Caractere isolado ou string at 1Mb ) Data Lgico Memo Numrico Objeto Diferentemente das linguagens como C, C++, Java, etc..., as variveis em Advpl no so prototipadas. Seus tipos so definidos no momento aps a atribuio de valores. Para testar o tipo de uma varivel pode-se utilizar a funo Type() ou ValType(). Type() para variveis pblicas e privadas declaradas em outras instncias do programa e ValType() para variveis que pertencem mesma instncia. Ex.: Function Test1() Local aTest Private aTest1 := {} Test2( aTest ) Return( NIL )

Function Test2( aTest ) IF ( Type( aTest1 ) == A )//A varivel deve estar entre aspas IF ( ValType( aTest ) == A ) aTest := aTest1 Else Alert( A variavel aTest nao eh um Array ) EndIF EndIF Return( NIL ) Antes de Atribuir um valor a uma varivel, seu tipo indefinido (U). Se a varivel for declarada como Private, Local ou Static, seu valor, na declarao, ser NIL. As variveis declaradas como Public recebem automaticamente a atribuio de um valor lgico .F.. As variveis s obtero o seu valor real na atribuio. Converso de variveis: No Advpl possvel converter os tipos usando uma funo ou expresso condicional.

De /Pa r a Ca r a c te r e

D a ta

L g ic o Nu m r ic o B lo c o

A rray

C a ra c t e re

<c V C T oD ( < c > ) ou S T oD ( < c d> ) > $ " Tt" a l( < c > )

_ _ E x e c M a c ro (< c b > ) o u & (c b )

Da ta

D T o C (< d > ) o u D T o S ( < d > )

V a l(D t o S (< d > ))

L g ic o IF( < l> , " .T." , ".F." ) S tr ( < n > ) o u S tr Z e r o ( < n > ) o u Nu m r ic Tr a n s f o r m ( < n > ,< c P> ) o B lo c o G e tC B S o u r c e ( < b > ) O b je to A rray Cla s s D a ta A r r ( < a > )

Tamanho: Faz-se necessrio observar o tamanho de variveis de caracteres e numricas uma vez que um problema difcil de analisar a comparao de variveis de tamanhos diferentes; os espaos de preenchimento podem ser significativos.:

Variveis Caractere Ex.: Local cUsuario Local cUser Local lEqual := Administrador := Administrador

lEqual := ( cUsuario == cUser ) No exemplo acima lEqual ir retornar .F. uma vez que o contedo armazenado na varivel cUsuario ( apesar de parecer igual ) diferente do contedo da varivel cUser. O que diferencia o contedo entre uma varivel e outra so os espaos direita. Para resolver o problema na comparao teremos que abir mo de uma funo que remova os espaos direita ( RTrim( ) ). Ex.: Local cUsuario Local cUser Local lEqual := Administrador := Administrador //Removendo //direita espaos a

lEqual := ( cUsuario == RTrim( cUser ) ) ou lEqual := ( cUsuario == AllTrim( cUser ) )

//Removendo espaos //direita e esquecerda

Nos dois exemplos acima lEqual ir ser igual .T. Se ao invs de usarmos o operador de comparao de igualdade == (exatamente igual ) utilizssemos o operador = ( que pode ser utilizado tanto para comparao de igualdade como para atribuio neste caso depende do contexto ) teramos: Ex.: Local cUsuario Local cUser Local lEqual := Administrador := Administrador

lEqual := ( cUsuario = cUser ) No Exemplo acima, lEqual ser .T. pois no fizemos uma comparao exata.
8

No AdvPl possvel obter o tamanho de uma varivel do tipo caractere ou array utilizando a funo Len( <uVar> ). Ex.: Local aArray1 Local aArray2 Local cUsuario Local cUser Local nArray1 Local nArray2 Local nUsuario Local nUser := { 1 , 2 , 3 } := Array( 10 ) := Administrador := Administrador := Len( aArray1 ) //3 := Len( aArray2 ) //10 := Len( cUsuario ) //13 := Len( cUser ) //16

Variveis Numricas: O Advpl, na leitura de uma varivel numrica, atribui, automaticamente, dez posies antes da casa decimal, a despeito do seu tamanho atual. Portanto, esse tamanho passvel de ser aumentado. J em relao ao nmero de posies depois do ponto decimal determinado quando voc o atribui a ela. Assim, se para montar um Get, tivermos o seguinte bloco de programa: Local nValor := 15.1270 @ 10 , 10 Say Digite um novo valor: Get nValor nValor ter 4 posies aps o ponto decimal e duas posies antes do ponto decimal ( conforme valor atribudo varivel ); portanto o Get usa 7 colunas da tela: 2 para ates do ponto decimal, uma para o prprio ponto decimal e quatro depois dele. Ao inicializar variveis numricas para uso em um Get, certifique-se de atribuir a elas o nmero correto de casas decimais ou utilize picture como em: Local nValor := 15.12 @ 10 , 10 Say Digite um novo valor: Get nValor Picture @R 999.99999 Apesar de nValor ter sido declarado como tendo apenas 2 posies aps o ponto decimal e ele ainda possuir dez posies antes do ponto, ao delimitarmos a digitao da informao definindo a Picture teremos ( para digitao ): 3 posies antes do ponto decimal, 5 posies aps o ponto decimal ( alm do prprio ponto ) totalizando 9 o nmero de colunas no Get para a visualizao e manuteno do valor. Obs.: Ao contrrio das variveis do Tipo Array e Caractere, que utilizam a funo Len() para retornar o seus tamanhos, em AdvPl no existe funo ou operador que retorne o tamanho de uma varivel do tipo numrica. Mas nada impede de que ela seja desenvolvida pelo prprio usurio do sistema.

10

Operadores e Expresses:
O Advpl suporta os seguintes operadores binrios:

** * +

^ / -

Que agrupados em ordem de precedncia ( prioridade, a mais alta primeiro ). Os operadores ** e ^ tm ambos a prioridade mais alta. Os operadores *, / e % tem nveis de prioridades iguais, assim como os operadores + e -. Os operadores *, / e % tem uma precedncia maior do que + e -. O Advpl avalia os operadores, de igual precedncia, da esquerda para a direita.
Operador Descrio ** e ^ * / % + Exponenciao Multiplicao Diviso Modulo Soma Subtrao

Operadores Unrios: O Advpl suporta um sinal negativo ( o chamado operador unrio). Ele tem a maior prioridade entre os operadores aritimticos. Por Exemplo: -1+2 resulta 1 e no -3.

11

O Advpl suporta os operadores de incrementos e decrementos unrios ++ e --. O Operador ++ acrescenta 1 a seu operando, -- subtrai 1. Ambos tm parceiros nas instrues regulares de atribuies. Exemplo: nContador++ idntico a nContador := nContador + 1 Entretanto, a vantagem desses operadores unrios o fato de serem concisos e de poderem ser utilizados para deferir a operao at depois de processado o valor. Por Exemplo, para carregar um array de campo CPnome de cada registro de um banco de dados chamado Alunos, poderamos escrever: Local acPNomes Local nContador := 1 Use Alunos acPNomes := Array( RecCount() ) While ( Alunos )->( !Eof() ) acPNomes[ nContador++ ] := ( Alunos )->CPNOME ( Alunos )->( dbSkip() ) End While O Subscrito do array usa o valor de nContador, depois acrescenta 1. Dizemos que o ++, neste caso, um operador ps-determinado. Voc pode us-lo como um operador de prefixos, em cujo caso o valor ser incrementado ANTES de ser usado. Por exemplo, a codificao anterior deixa o nContador com um valor superior em 1 unidade ao nmero de registros processados. Para deix-lo com o valor real, poderamos escrever: Local acPNomes Local nContador := 0 Use Alunos acPNomes := Array( RecCount() ) While ( Alunos )->( !Eof() ) acPNomes[ ++nContador] := ( Alunos )->CPNOME ( Alunos )->( dbSkip() ) End While No modo de prefixo, o computador aplica o incremento antes de usar o valor. Operadores Relacionais:

12

No Advpl so permitidos os seguintes operadores relacionais:

Operador Descrio < > <= >= = == != <> # $ Menor que Maior que Menor ou igual a Maior ou igual a Igual Exatamente igual No Igual ( ou diferente ) Diferente Diferente Contido em

Os operadores relacionais tm menos prioridade do que os aritmticos. Por exemplo: nI < nLimite 1 avalidado como: nI < ( nLimite 1 ) e no como: ( nI < nLimite ) 1

Conectivos Lgicos:

Conectivo Descrio .and. .or. e ou

13

possvel negar uma expresso lgica com .NOT. ou com o ponto de exclamao, que equivalente. No Advpl as expresses lgicas so avaliadas da esquerda para a direita ( parando assim que o resultado conhecido ). Esse processo chamado avaliao de curto-cirquito. Por exemplo: #DEFINE NLIMITE 10 Local aArr[NLIMITE ] Local nIndice := 1 Local nMax := 50 ... ... While ( ( nIndice <= NLIMITE ) .and. ( aArr[ nIndice ] < nMax ) ) Process() nIndice := nIndice + 1 //ou ++nIndice End While Quando o valor de nIndice for 11, a primeira expresso provocar a interrupo da avaliao. ( no exemplo, a segunda expresso no ser avaliada, caso contrrio ocorreria o erro conhecido como erro de subscrito O erro ocorreria porque aArr for definido com 10 elementos ( NLIMITE 10 ); se a segunda comparao fosse efetuada, teramos aArr[11 ] para um array de 10 elementos apenas. O Advpl avalia os operandos de operadores binrios, tanto aritmticos como relacionais, da esquerda para a direita. Na maioria dos casos a ordem no importante, mas: IF A() < B() Onde A() e B() so funes? O problema que A() e B() poderiam provocar efeitos colateriais. Se elas dependerem de uma varivel comum, a qual pode mudar ambas, a ordem de avaliao ter importncia. IF aArr1[ ++nI ] < aArr2[ nI++ ] Se ambos os lados tiverem efeitos colaterais, seria necessrio considerar a ordem de avaliao. Os operadores, em Advpl, so sobrecarregados. Isso significa que eles se aplicam a operandos de diversos tipos. Por exemplo, pode-se utilizar o operador + para somar nmeros, concatenar string e efetuar a aritmtica de datas. De modo
14

semelhante, podemos usar o operador <= para comparar datas, nmeros e strings. O Advpl suporta dois operadores de atribuies, = e := ( torna-se igual a). A diferena entre os dois que := retorna um valor, enquanto = sobrecarregado. Quando usado dentro de uma expresso, = efetua uma comparao; caso contrrio, ele uma atribuio. O operador := sempre efetua uma atribuio e ele retorna um valor no lado direito para que possamos us-lo dentro de uma expresso, como em: IF( nHandle := fOpen( ERROS.TXT ) ) != 0 A varivel nHandle obtm o valor retornado pela abertura do arquivo ERROS.TXT. Este valor , ento, comparado com 0. Poderamos obter o mesmo resultado com: nHandle := fOpen( ERROS.TXT ) IF ( nHandle != 0 ) ... Mas no modelo anterior uma instruo foi economizada. Na verso em linha inserimos a atribuio entre parnteses. Isso ocorreu porque o operador := tem menos precedncia que o operador !=, portanto nHandle receberia, de outra maneira, um valor lgico, resultante da comparao do valor retornado por fOpen() com 0. Uma boa prtica, ento, colocar entre parnteses as instrues de atribuio em linha, para evitar problemas de precedncia. O operador := associa da direita para a esquerda, portanto podemos escrecer: cPNome := cSnome := cCep := cTel := Space( 10 ) que o efeito sera identico a escrever: cPNome := ( cSnome := ( cCep := ( cTel := Space( 10 ) ) ) ) A vantagem do operador := que ele pode aparecer dentro de expresses. Um bom uso deste operador na determinao de causa de falha de uma complexa estrutura While. Por exemplo. While TeclaValid() .and. NaFaixa() ... End While IF TeclaValid()
15

.. EndIF Quando o loop terminar, precisamos verificar que condio provocou a sada dele; para tanto, precisamos chamar novamente uma funo demorada. Podemos reescrever usando atribuies em-linha, assim: While ( ( lTv := TeclaValid() ) .and. ( lNf := NaFixa() ) ) End While IF ( lTv ) .. EndIF Quando sairmos do Loop, no precisaremos chamar nenhuma das funes demoradas para determinar por que o loop terminou. O ltimo operador que examinaremos o $ ( contido em ). Use-o para verificar se uma string est contida em outra. Por exemplo, para constatar se a varivel de memria de caractere isolado cTipoPeca R, D, ou b, podemos escrever: IF cTipoPeca $ RDB ...
Para verificar se uma varivel de memria de dois caracteres PR, SP, ou AM, escreva:

IF cEstado $ PR_SP_AM ... Obs.: Os separadores _ so necessrios combinaes, como RS e PA que so estados vlidos. para invalidar

Dica: Use o operador $ ao invs de vrias condies separadas. Ex.: Do Case Case cEstado == PR ; Execute() Case cEstado == SP ; Execute() Case cEstado == AM ; Execute() EndCase Poderia ser escrito como IF ( cEstado $ PR_SP_AM )

16

Execute() EndIF

Expresses condicionais:
IIF() ou IF() uma expresso condicional. So abreviaes de uma simples seqncia de IF/ELSE/ENDIF. Por exemplo, para encontrar o mnimo de a e b, poderamos escrever: nMinimo := IIF( a < b , a , b ) ou nMinimo := IF( a < b , a , b ) que seria equivalente a escrever: IF ( a < b ) nMinimo := a Else nMinimo := b EndIF As vantagens de IIF() e/ou IF() que se acomodam em qualquer lugar em que uma expresso possa estar ( incluindo blocos de codificao ) e gera menos codificao.

Estruturas de controle:
Como acontece com a maioria das linguagens, o Advpl fornece instrues para suportar desvios e loops. As instrues de desvio permitem que seus programas selecionem entre alterantivas; as instrues de loops permitem executar um grupo de instrues repetidas vezes. Desvio: No Advpl existem dois mtodos de desvio: o bloco de instrues IF/ELSE/ELSEIF/ENDIF ou o bloco DO CASE/ENDCASE. ELSEIF apenas uma abreviao que evita a necessidade de se escrever outro IF/ENDIF. Os dois exemplos que se seguem so equivalentes: IF ( nX < 10 ) ... Else IF ( nX > 20 ) ... EndIF EndIF

17

ou IF( nX < 10 ) ... ElseIF ( nX > 20 ) ... EndIF Observe que, como o Advpl sempre insiste num ENDIF encerrando todo o bloco de IF, sempre saberemos a quem pertence um Else. Obs.: Ao testar vrias condies, prefervel uma instruo CASE a uma longa seqncia de IF/ELSEIFs. Na implementao das instrues CASE, o Advpl avalia uma condio de cada vez, at encontrar uma verdadeira. Em sequida ele avalia todas as intrues at o prximo CASE, ENDCASE ou OTHERWISE e, finalmente, salta para a instruo seguinte ao ENDCASE. A clusula OTHERWISE serve como ltima condio; ela localiza toda situao no correspondida por um CASE anterior. BEGIN SEQUENCE/END O par BEGIN SEQUENCE/END oferece outro mtodo de desvio. Use-o para delimitar um bloco de codificao; em sequida, a partir de qualquer local dentro dele, voc poder emitir uma instruo BREAK. Ele passar o controle para a instruo seguinte ao END. Exemplo: BEGIN SEQUENCE IF ... IF ... IF ... Break EndIF EndIF EndIF ... END SEQUENCE

18

BEGIN SEQUENCE pode ser aninhado; um BREAK passar o controle para o END mais prximo BEGIN SEQUENCE IF !(... ) BREAK ENDIF BEGIN SEQUENCE IF ... BREAK ELSEIF ... BREAK ENDIF IF ... BREAK ELSEIF ... BREAK ENDIF END SEQUENCE ... END SEQUENCE ... O uso bsico de BEGIN SEQUENCE manipular excees. Ele fornece um local adequado para saltar quando ocorre um erro. Podemos us-lo como um ponto de interrupo para a lgica profundamente aninhada. Por exemplo, vamos supor que precisemos testar trs condies, mas s possa testar uma aps o trmino com xito da anterior ( como uma seqncia de bloqueios de arquivos ). Usando o aninhamento tradicional escreveramos: IF <cond1 > ... ... IF <cond2> ... ... IF <cond3> ... ... ENDIF
19

ENDIF ENDIF Usando BEGIN SEQUENCE, poderamos escrever: BEGIN SEQUENCE IF !<cond1> BREAK ENDIF ... ... IF !<cond2> BREAK ENDIF IF !<cond3> BREAK ENDIF END SEQUENCE Pode-se, tambm, colocar uma instruo RECOVER dentro de um par BEGIN SEQUENCE/END. Quando seu programa executar, subseqentemente, uma instruo de interrupo, o controle ser transferido para a instruo RECOVER em vez de para o END.
Obs.: Tenha cuidado usar BEGIN SEQUENCE e BREAK; poderemos criar uma codificao ilegvel com o uso excessivo desse par.

Loops: O Advpl oferece os loops FOR e WHILE. A instruo FOR executa instrues at o NEXT correspondente durante um determinado nmero de vezes. Devemos informar a quantidade de vezes a iterar, como em Local nLoop Local nLoops := 10 For nLoop := 1 To nLoops ... ...

20

Next nLoop A inicializao da varivel para controle do Loop dever feita na declarao For nLoop :=, o NEXT incrementa-a automaticamente e, se ele no tiver ultrapassado seu limite, o controle retornar para a primeira instruo dentro do loop. O valor do incremente igual a 1, a menos que especificado de outra maneira com a opo STEP. Local nLoop Local nLoops := 100

For nLoop := 1 To nLoops Step 10 Next nLoop ou Local nLoop Local nLoops := 0 For nLoop := 100 To nLoops Step 10 Next nLoop Podemos usar os commandos EXIT e LOOP dentro do corpo do loop FOR. Exit encerra o Loop, transferindo o controle para a instruo depois do NEXT; o LOOP d um salto para o incio, reavalidando a condio.
Obs.: Use os dois comandos com cuidado; eles podem dificultar o entendimento e a manuteno dos loops.

O Loop for sempre reavalia a expresso limite a cada passagem. Por exemplo, em: Local nLoop For nLoop := 1 To RecCount() ... Next nLoop RecCount() ser sempre reavaliada a cada passagem do Loop, o que um disperdcio. Atribua o valor retornado per RecCount() uma varivel para melhorar a performance, como em: Local nLoop Local nLoops := RecCount()

21

For nLoop := 1 To nLoops Next nLoop J um Loop WHILE continua at que sua condio venha a ser falsa. A condio avaliada primeiro, para que o loop no possa ser inserido de modo algum. A avaliao da condio ser interrompida assim for conhecido o resultado.
Obs.: Apenas o necessrio de uma condio avaliado para se determinar o resultado.

Para executar o loop pelo menos uma vez, simulando as clusulas REPEAT... UNTIL ou DO ... UNTIL de outras linguagens, poderemos escrever: Local lContinue := .T. While ( lContinue ) ... IF <condicao de sada> lContinue := .F. EndIF End While Estaremos, essencialmente, testando a condio ao final do loop.
Obs.: No uso While .T. como uma condio de loop. melhor colocar a condio de sada na instruo. Desta maneira, poderemos ver claramente o que o loop est tentando alcanar.

Pr-Processador: O pr-processador um importante recurso no Advpl. Imagine-o como um programa que executa antes do compilador. Ele l o programa como entrada e gera um arquivo pr-processado como sada. O arquivo de sada, ento, serve como a entrada do compilador. Podemos incluir comandos do pr-processador, chamadas de diretivas, no programa de origem. O pr-processador as manipular; o compilador nunca as percebe, uma vez que ele s manipula o arquivo de sada. O pr-processador do Advpl tem os seguintes recursos: - constantes simblicas ou manifestas - arquivos include

22

- macros do compilador - compilao condicional - comandos definidos pelo usurio Constantes Simblicas: No Advpl possvel a definio de constantes simblicas para uso no processo de pr-compilao. Devemos abrir mo desse recurso para tornar os programas mas legveis e fcil de entender. Por Exemplo, no fragmento de codificao: IF ( nOpc == 1 ) Inclui() ElseIF ( nOpc == 2 ) Altera() ElseIF ( nOpc == 3 ) Exclui() EndIF S conseguiremos idenfificar o que nOpc representa se acompanharmos todo o processo ou se o programa estiver documentado. Mas, se utilizssemos constantes, teramos:
Obs.: Podemos definir constates com a diretiva do pr-processador #DEFINE.

#DEFINE INCLUI 1 #DEFINE ALTERA 2 #DEFINE EXCLUI 3 IF ( nOpc == INCLUI ) Inclui() ElseIF ( nOpc == ALTERA ) Altera() ElseIF ( nOpc == EXCLUI ) Exclui() EndIF No precisaramos avaliar todo o programa para saber a que se refere o valor de nOpc. E o cdigo fica muito mais fcil de interpretar. Na realidade INCLUI, ALTERA e EXCLUI, sero substitudos no processo de pr-compilao por 1 , 2 e 3 ( respectivamente ).
Regra: A traduo de #DEFINE feita pelo pr-processador sensvel a letras maisculas e minsculas. Dica: Defina todas as constantes em letras maisculas com a diretiva #DEFINE

23

Arqivos Include: As constantes simblicas s so conhecidas no arquivo de origem que as define. O pr-processador s conhece seus valores depois da traduo. Portanto se definirmos uma constante um um programa ela s valer durante a compilao deste programa. Para que uma constante possa valer para mais de um programa, devemos usar um recurso de incluso de arquivos atravs da diretiva #include. Ou seja, as constantes seriam includas em um arquivo parte e todo programa que fosse utilizar essas constantes teria a seguinte linha #include <nome do arquivo.ch>. Macros do compilador: Podemos visualizar uma macro do compilador como uma funo diretamente na codificao de origem do programa onde poderamos parametrizar uma seqncia de comandos, exatamente como uma funo. A diferena que as macros do pr-processador so substitudas diretamente no arquivo de origem ( elas no so chamadas do mesmo modo que uma funo ) . Por exemplo, poderamos escrever uma macro de pesquisa de mnimo como: #DEFINE MIN( a , b ) IIF( a < b , a , b ) Quando, subseqentemente, chamar-mos a MIN com: nMin := MIN( nY , nZ ) O pr-processador a substituir por sua definio como em: nMin := IIF( nY < nZ , nY , nZ ) Defina a macro usando parmetros formais, exatamente como uma funo. Quando o pr-processador a expandir, ele os substituir pelos verdadeiros prmetros (argumentos). Isto basicamente o que acontece quando voc chama uma rotina ou funo. A diferena que a substituio dos arqumentos ocorre no tempo do pr-processador com as macros do compilador, no durante a execuo. Compilao Condicional: A compilao condicional nos permite incluir ou excluir codificao baseada numa condio. O propsito usual manter diferentes verses de um programa, sem precisar separar cpias da codificao de origem. Por exemplo, poderamos ter uma vero utilizando o TopConNect e outra para CodBase,
24

As diretivas do pr-processador para a compilao condicional se parecem com as instrues IF/ELSE/ENDIF. O pr-processador inclui ou exclui codificao do arquivo de sada, baseado no resultado do teste. O teste, conseqentemente, atua como um filtro. O aspecto importante a observar que o pr-processador faz o teste, no o programa. Dessa forma, a codificao no-selecionada nem sequer existe no arquivo de sada. As diretivas verificam se um smbolo est definido ou se ele tem um valor especfico ( podemos definir um smbolo com a diretiva #DEFINE ). As diretivas do pr-processador so: #IFDEF #IFNDEF #ELSE #ENDIF - Verifica se um smbolo est definido - Verifica se um smbolo no est definido - Inclui algo se #IFDEF ou uma #IFNDEF falhar - Indica o final da codificao controlada por um #IFDEF, #IFNDEF ou #ELSE

Podemos definir um smbolo sem atribuir um valor a ele. Por Exemplo, podemos escrever: #DEFINE TOP E test-la, em seguida, com: #IFDEF TOP Neste caso, estamos apenas verificando se ela est definida, no checamos o seu valor; Observemos agora, como o pr-processador manipula um exemplo simples: #DEFINE TOP #IFDEF TOP Local cquery := SELECT * FROM... #ELSE ( cAlias )->( dbSeek( ... ) ) #ENDIF

25

Funes:
As funes, em Advpl, podem ser entendidas como um mecanismo de subrotinas. Elas sempre retornam um valor. Uma funo pode retornar qualquer tipo de dados ( array, numrico, string, objeto, lgico... ) e, se nenhum tipo de retorno for definido a funo retornar NIL. Elas retornam seu valor como uma expresso aps a instruo RETURN ( Obs.: a rotina chamadora da funo no precisa, necessariamente, usar o valor de retorno ). O uso adequado das funes possibilita a grande reutilizao da codificao. As funes so definidas uma s vez dentro de um programa, mas podem ser chamadas vrias vezes. Elas podem ser definidas em qualquer ordem e voc pode chamar uma antes de defini-la. Todas as funes so armazendas no Repositrio de Objetos. E Podem ser declaradas como: Pblicas: Podem ser chamadas em qualquer programa: Funes Reservadas ( Microsiga ) Function na Microsiga Template Function Project Function -> Reservado para desenvolvimento no ERP -> Para a criao de Templates -> Para a criao de Projetos

Funes Comuns ( Microsiga / Clientes usurios ) User Function Web Function Html Function Estticas: -> Para criao de funes de usurio -> Para criao de funes para uso na Web -> Para criao de funes para uso na Web

Podem ser chamadas apenas no programa onde foram declaradas ( Static )

Static Function
Obs.: Consulte o Header ( arquivo de cabealho ) Protheus.ch ( \mp8\include\ para verificar os detalhes sobre o padro de converso das delcaraes acima nas chamadas de funo.

Parmetros:

26

No Advpl, a forma de passagem de parmetros para as funes colocando os nomes dos parmetros entre parnteses e delimitando-os por vrgula. Este mtodo conhecido como lista de parmetros formais. Como em: Function Teste( a , b , c ) User Function Teste( a , b , c ) Onde a, b, c so os parmetros formais da funo. O escopo destes parmetros Local ( tem visibilidade e durao enquanto durar a execuo da funo ).

Programao Bsica em AdvPl ( padro xBase/CodBase )


Funes/Parmetros Ao fazer uma chamada a uma funo valores so fornecidos para os parmetros. Esses valores podem assumir a forma de constantes, variveis, campos, elementos de arrays ou expresses, e eles so citados como parmetros reais ou argumentos de chamada. Uma chamada de funo pode, tambm, omitir argumentos para qualquer nmero de parmetros, deixando um vazio onde o argumento deveria estar. Ser necessrio incluir as vrgulas delimitadoras na lista de argumentos, exceto para quaisquer argumentos omitidos no final da lista. Isto assegura no existir ambigidade sobre quais parmetros foram deixados em branco. Quaisquer argumentos omitidos de uma chamada de funo sero tratados exatamente como se o valor NIL tivesse sido realmente passado. No h como distinguir, dentro da definio de funo, entre o argumento omitido e um valor NIL passado explicitamente. A chamada: User Function Test( nS , nE , nI , nD ) Return U_Test( nRow , , 2 ) funo U_Test(), conforme definida acima, resultar no corpo da funo sendo executada como se os parmetros formais tivessem sido inicializados assim: nS nE nI nD := nRow := NIL := 2 := NIL

27

Envio Por Referncia ou por Valor: Um aspecto importante do envio de parmetros para as funes se eles so passados por referncia ou por valor. O mtodo de envio de um argumento depender do mtodo de chamada; ele no pode ser determinado no ponto em que a prpria funo definida. Quando os parmetros so passados por referncia, a funo recebe ponteiros para os verdadeiros parmetros, e quaisquer mudanas que ela introduzir neles sero imediatamente refletidas em seus valores: User Function Test1() Local nVar nVar := 3 U_Test2( @nVar ) //Apos o retorno de U_Test2() nVar ter o contedo igual 1 Return( NIL ) User Function Test2( nVar1 ) nVar1 := 1 Return( NIL ) Para efetuarmos a passagem por referncia devemos prefixar a varivel com o operador de referncia @. Se passarmos um parmetro por valor, o valor inicializar a varivel local ou privada associada com o parmetro formal, mais quaisquer mudanas introduzidas neste valor pela funo desaparecero. O verdadeiro parmetro no alterado pela operao da funo. User Function Test1() Local nVar nVar := 3 U_Test2( nVar ) //Apos o retorno de U_Test2() nVar ter o contedo igual 3 Return( NIL )
28

User Function Test2( nVar1 ) nVar1 := 1 Return( NIL ) A chamada a U_Test2( nVar ) determina o envio de parmetros por valor, pois o operador de referncia @ no foi utilizado. Os campos de banco de dados so excees. Eles sempre so passados por valor, independentemente do estilo de chamada; caso contrrio, toda vez que o parmetro formal fosse mudado, seria necessrio atualizar o banco de dados. Mas isso s valido se o campo do banco de dados estiver prefixado pelo alias. Exemplo: dbSelectArea( SRA ) //Seleciona a rea de trabalho SRA U_Teste1( @SRA->RA_MAT ) //ir gerar erro durante a compilao U_Teste1( @RA_MAT ) //ir gerar erro na execuo U_teste1( @_Field->RA_MAT ) //ir gerar erro na execuo SRA->( U_teste1( @RA_MAT ) ) //ir gerar erro na execuo Recomendao: Para que no ocorra erro de execuo, sempre prefixe o campo com o alias correspondente para que seja possvel identificar se um campo de tabela est sendo passado por referncia. Chamando Funes Indiretamente: Em Advpl possvel efetuar a chamada de funes indiretamente usando o operador de macros. Este procedimento lhe permitir usar aplicativos controlados por dados. Pode-se usar um bando de dados ( exemplo do SX3 ) para armazenar uma seqncia de funes a serem chamadas, e depois us-lo para gerar telas, efetuar validao de campos, iniciar o contedo de campos, etc. Para passar uma funo como parmetro, passe o nome da funo como uma string de caracteres ou como uma chamada de funo dentro de um bloco de codificao. Para cham-la, use o operador de macros & ou a funo __ExecMacro() quando estiver passando uma string de caracteres ou Eval() para avaliar o bloco de cdigo. Exemplo: cExecTeste := U_TESTE() &( cExecTeste )

29

ou cExecTeste := U_TESTE() __ExecMacro ( cExecTeste ) ou ainda bExecTeste := { || U_TESTE() ) Eval( bExecTeste )//Executa a funo U_TESTE() Recursividade: As funes e rotinas podem ser recursivas, isto , podem chamar a si mesmas. Cada chamada recebe uma nova cpia das variveis Local e Private e de quaisquer parmetros. Quando uma funo retorna, os valores anteriores so restabelecidos. Obs.: No so alocadas novas cpias de variveis STATIC. Exemplo:
User Function SaveArray( uArray , cFileName , nErr ) Local cValTypeuArray Local lSaveArray Local aArray Local nfHandle Begin Sequence IF !( cValTypeuArray $ "A/O" ) Break EndIF IF ( cValTypeuArray == "O" ) aArray := ClassDataArr( uArray ) Else aArray := uArray EndIF lSaveArray := FileCreate( cFileName , @nfHandle , @nErr ) IF !( lSaveArray ) Break EndIF SaveArr( nfHandle , aArray ) fClose( nfHandle ) End Sequence Return( lSaveArray ) //Aqui chamamos a funo para salvar o Array := ValType( uArray ) := .F.

//Executa a funo U_TESTE() //Executa a funo U_TESTE()

30

Static Function SaveArr( nfHandle , aArray ) Local cElemType Local uCntSave Local nLoop Local nLoops nLoops := Len( aArray ) uCntSave := ( "A" + StrZero( nLoops , 10 ) ) fWrite( nfHandle , uCntSave ) For nLoop := 1 To nLoops cElemType := ValType( aArray[ nLoop ] ) IF ( cElemType $ "A/O" ) //Aqui efetuamos a chamada recursiva IF ( cElemType == "A" ) SaveArr( nfHandle , aArray[ nLoop ] ) Else SaveArr( nfHandle , ClassDataArr( aArray[ nLoop ] ) ) EndIF Else IF ( cElemType == "B" ) uCntSave := GetCBSource( aArray[ nLoop ] ) ElseIF ( cElemType == "C" ) uCntSave := aArray[ nLoop ] ElseIF ( cElemType == "D" ) uCntSave := Dtos( aArray[ nLoop ] ) ElseIF ( cElemType == "L" ) uCntSave := IF( aArray[ nLoop ] , ".T." , ".F." ) ElseIF ( cElemType == "N" ) uCntSave := Transform( aArray[ nLoop ] , RetPictVal( aArray[ nLoop ] ) ) EndIF uCntSave := ( cElemType + StrZero( Len( uCntSave ) , 5 ) + uCntSave ) fWrite( nfHandle , uCntSave ) EndIF Next nLoop Return( NIL )

Instrues de Declaraes: Uma instruo de declarao uma instruo para um compilador. Neste seo estamos interessados nas seguintes declaraes: LOCAL PRIVATE PUBLIC STATIC

31

LOCAL, PRIVATE, PUBLIC e STATIC declaram variveis.


Obs.: Para evitar ambigidade entre campos e variveis Private com o mesmo nome recomendado a prefixao da varivel Private com M->[Nome da Varivel ] para identificar que uma varivel de memria e/ou prefixar o campo com o respectivo Alias usando o operador -> como em: [Alias]->[ Campo da Tabela ]. Obs.: Melhor do que declarar uma varivel com o Mesmo nome do campo declarar a varivel utilizando a notao Hngara Modificada onde as variveis so prefixadas com a letra minscula correspondente ao seu tipo ( Em Ingls ). Tipo: "N"umeric "C"haracter "B"lock "D"ate "L"ogic "O"ject "U"ndefined Exemplo: nNumber cChar bBlock dDate lOk oObj uVar

Lembrando que em Advpl as Variveis no so prototipadas o tipo da varivel s ser conhecido apos a atribuio de algum valor a ela e que para testar o tipo podemos utilizar as funes ValType( <uVar> ) ou Type( <cVar> ). Onde: ValType avalia o contedo da varivel passada como parmetro para verificar o seu tipo, e Type(), primeiro, "macro-executa" a varivel para obter seu contedo e de pois o avalia. Sendo assim, para usar a funo Type() necessrio que a varivel seja do Tipo Private ou Public

LOCAL, PRIVATE: Essas duas instrues declaram variveis para uso subseqente. Entretanto, existem diferenas importantes entre os tipos de variveis que elas criam. PRIVATE declara variveis visveis na rotina na qual voc as declara e tambm em qualquer rotina que voc chame a partir do ponto da declarao. User Function Test() Private nI U_Test1() Return( NIL ) User Function Test1() //aqui poderemos enxergar e fazer uso da varivel nI criada em //U_Test() Return( NIL )

32

A instruo LOCAL declara variveis somente visveis na rotina na qual foram declaradas; dessa forma: User Function Test() Local nI U_Test1() Return( NIL ) User Function Test1() //aqui no poderemos enxergar e nem fazer uso da varivel nI criada //em U_Test() Return( NIL ) O problema com as Private: As variveis Private representam um dos maiores obstculos para a criao de rotinas modulares e confiveis. O principal problema que elas no so privadas de modo algum e isto ocasiona srios problemas. Por exemplo, examine o seguinte: User Function Test() Private nI := 0 Private aTest[10] While ( ++nI <= 10 ) Test1() //nI ser sempre 4 aqui End While Return( NIL ) Static Function Test1() nI := 0 While ( ++nI <= 3 ) aTest[ nI ] := nI End While Return( NIL ) No fragmento de cdigo acima, a funo Test1() usa uma varivel nI para o controle do Loop, mas como a funo Test(), sua chamadora, j possui uma
33

varivel nI, Test1() usa esta ltima. O resultado que, no retorno a Test(), nI contm o valor 4, fazendo com que o programa fique em loop infinito. Uma soluo para o problema seria declarar a varivel nI como private em Test1(). Desta forma a varivel nI criada em Test() ser preservada no retorno de Test1(). A melhor soluo, seria criar nI como Local, tanto em Test() como em Test1(). User Function Test() Local nI := 0 Local aTest[10] While ( ++nI <= 10 ) Test1( @aTest ) End While Return( NIL ) Static Function Test1( aTest ) Local nI := 0 While ( ++nI <= 3 ) aTest[ nI ] := nI End While Return( NIL )
Obs.: Muito cuidado ao usar variveis Private no Programa. Isso pode causar probemas muito difceis de depurar. Use Variveis Locais e abra mo da referncia para obter retorno. Garanta que todas as Variveis utilizadas no Programa tenham sido previamente declaradas.

STATIC e PUBLIC: Estas duas instrues tambm declaram variveis. PUBLIC declara variveis que ficam visveis durante todo o programa. Os problemas que examinamos com relao as variveis Private tambm ocorrem com as variveis Public, contudo com mais abrangncia. Em vez de isolados a rotinas chamadas a partir de uma funo, os problemas ocorrem na aplicao inteira. As variveis STATIC podem ajudar a solucionar este problema. Existem dois tipos de variveis static: internas e externas. A diferena est na abrangncia. As variveis static em nvel de arquivo abrangem o arquivo: elas ficam visveis em qualquer funo ou rotina dentro do arquivo de programa no qual esto

34

declaradas. As variveis static declaradas dentro de uma funo ( static internas ) tem escopo local, exatamente como as variveis local.
Obs.: No Protheus, as variveis Static criadas partir do menu, sero liberadas sempre que se voltar para o menu. J as Static criadas durante a entrada do sistema sero mantidas enquanto o sistema estiver sendo usado.

A caracterstica principal da varivel Static que ela pode sair do escopo e no conseguirmos mais acess-la, mas, na prxima vez que em que entrarmos no programa ou funo que a declarou ela ter o mesmo valor da ltima vez em que o programa ou funo foi executado. Exemplo: TEST1.PRG Static nVar := 0 //Static em nvel de arquivo ( externa ) User Function A() Alert( Str( nVar ) ) //nVar ser zero aqui... nVar := 10 Return( NIL ) User Function B() Alert( Str( nVar ) ) //nVar ser 10 aqui... Return( NIL ) User Function C() Static nVar //Static em nvel de funo ( interna ) DEFAULT nVar := 0 Return( ++nVar ) TEST2.PRG User Function Test() Local nLoop Local nLoops Local nRetC U_A() //aqui acessando a varivel Static Externa U_B() //aqui acessando a varivel Static Externa //nVar no est disponvel em Test2()

35

nLoops := 15 For nLoop := 1 To nLoops nRetC := U_C() //aqui acessando a varivel Static Interna Alert( Str( nRetC ) )//nVar em U_C ser incrementada a cada //iterao do Loop Next nLoop Return( NIL )

LOCAL, STATIC e MACROS: As variveis Local e Static no so mantidas na tabela de smbolos de execuo, portanto, os programas que as utilizam executam com mais velocidade e menos memria. Uma conseqncia disto, contedo, que no poderemos acess-las a partir de expanso de macro. O operador de macros & ou a internal function __ExecMacro() compilam a codificao, durante o processo de execuo, partir de uma string de caracteres contendo uma expresso. Se a expresso citar uma varivel, o sistema na execuo precisar encontr-la na tabela de smbolos. O problema com as Locai e Static, evidentemente, que o sistema, por no armazen-las na tabela de smbolos, nas as encontra fazendo com que ocorra erro durante a execuo do sistema se foram expandidas por macro. Por exemplo: nVar := 1 cVar := nVar Alert( Str( &( cVar ) ) //ou Alert( Str( __ExecMacro( cVar ) ) Se nVar for uma varivel Local, o programa falhar. Observe que cVar pode ser uma varivel Local ou Static; mas s que qualquer coisa contida na string da expresso no pode ser uma Local nem uma Static. Uma forma de contornar isso seria usando blocos de codificao para poder referir-se a varivel Local ou Static definidas em outro programa ou funo.
Nota: No possvel referir-se a varivel Local ou Static dentro de uma expanso de macro.

A funo Type() usa macros internamente. Use a funo Type() para determinar o tipo de uma varivel. Envie o nome da varivel entre aspas, como em: nVar := 1 cType := Type( nVar )

36

Alert( cType ) //N O problema que, se nVar for uma varivel Local ou Static, a expanso interna da macro no a encontrar na tabela de smbolos e a funo Type() retornar U, indicando que a varivel no est definida. Existe uma funo semelhante, a ValType(). As principais diferenas entre ValType() e Type() so que ValType() realmente opera com variveis Local e Static e no preciso enviar os parmetros de ValType() entre aspas, como em: Local nVar := 1 Local cType := ValType( nVar ) Alert( cType ) //N Pelo fato de no ser preciso colocar o parmetro de ValType() entre aspas, a varivel citada precisa ser declarada, caso contrrio ocorrer erro durante a execuo. Uma varivel pode ser declarada incluindo-a numa lista de parmetros formais, seja como Private, atribuindo-se um valor a ela, seja como uma instruo de declarao explcita. Se ela no for declarada obteremos um erro de execuo por estar tentando passar uma varivel no definida para uma funo. Observe, entretanto, que ValType() ainda retornar U se enviarmos a ela uma varivel declarada, mas indefinida como em: Local nVar Local cType := ValType( nVar ) Alert( cType ) //U Isso ocorre porque as variveis, Private, Local e Static declaradas e no inicializadas tem sempre um valor NIL. Portanto, estamos passando, na realidade, NIL para ValType() e ela retornar um U. Blocos de Codificao: Os Blocos de Codificao ou CodeBlock so variveis que utilizamos para armazenar fragmentos de codificao do programa. Esses fragmentos podem ser passados para funes que iro execut-los. Criamos um bloco de codificao partir da seguinte sintaxe: bVar := { || Teste() }

37

O caractere { significa inicio do bloco de codificao e o caractere }, fim do bloco de codificao. As duas barras verticais ( || ) delimitam a lista de parmetros formais do bloco de codificao. A varivel bVar pode ser Local, Static, Private ou Public; no faz diferena. Ela s contm um fragmento de codificao e quando avaliado ele chama a funo Test() Para avaliar ou executar um bloco de cdigo, use a funo Eval(), como em: nResult := Eval( bVar ) esta chama a funo Test(). Como toda funo Eval() retorna um resultado ( que o ltimo elemento do bloco de cdigo ) Ex.: bRetVal nResult := {|nVar1,nVar2,nVar3|nVar2:=nVar1+nVar2, nVar2*nVar3} := Eval( bRetVal , 1 , 2 , 3 )

//Aps avaliar o bloco, Eval retornar para nResult 9 que o resultado //da operao definida no bloco de cdigo. Parmetros de Blocos de Codificao: As barras verticais utilizadas na montagem do Bloco de Codificao so as delimitadoras das especificaes dos parmetros formais e o que caracteriza a Montagem de um bloco de cdigo, se elas no forem utilizadas o sistema interpretar que o valor que est sendo atribudo varivel um array e no um Bloco de Cdigo. A definio e uso desses parmetros opcional mas as barras no. \ Exemplo: bVar := { | nVar1 , nVar2 | nVar1 + nVar2 } nRetult := Eval( bVar , 10 , 20 ) Quando Eval avaliar o bloco de codificao nVar1 ter o contedo 10 e nVar2 ter o contedo 20. Como em uma funo, os parmetros devero ser passados na seqncia em que foram declarados. Outro exemplo poderia ser: bVar := { |nVar1,nVar2,cVar3,aVar4| U_Teste(nVar1,nVar2,cVar3,aVar4)} Eval( bVar , 10 , , C , {} ) Neste caso, Eval, na avaliao do bloco de codificao, ir efetuar a chamada funo U_Teste() com passagem de parmetros. Observe que o segundo parmetro no foi passado, sendo assim, U_Teste() receber, respectivamente: 10, NIL, C e {} que sero os contedos reais das variveis nVar1, nVar2, cVar3 e aVar4.

38

Estruturas de Dados usando Arrays: Podemos dizer que em Advpl os Arrays so tratados como referncias ou ponteiros. Eles podem ser utilizados para armazenar estruturas de dados complexas como outros arrays, dados numricos, strings, objetos e at blocos de codificao. Arrays como Referncia: Ao declarar um array de nElementos, na verdade, estamos criando duas estruturas de memria. O Advpl cria uma estrutura para conter a prpria varivel e depois uma seqncia de estruturas para armazenar cada elemento do Array. Imagine uma varivel contendo um array como separada dos prprios elementos do array. Pense na varivel como se estivesse apontando ou citando os elementos. Presumindo-se a seguinte declarao: Local aNomes[4] Poderamos afirmar que aNomes refere-se ao primeiro elemento do Array. 1 2 3 4

aNomes No Advpl podemos fazer com que outra varivel Array faa referncia ( ou aponte ) para uma outra varivel Array. Neste caso elas sero equivalentes. Ex.: Local aNomes[4] Local aVar aVar := aNomes 1 2 3 4

aNomes
39


aVar Qualquer alterao efetuada nos em aNomes ser automaticamente refletida em aVar e vice-e-versa. Podemos utilizar o operador == para determinar se duas variveis do tipo Array se referem a um mesmo Array. Este operador no compara os elemento por elemento do Array; em vez disso, ele retorna verdadeiro se elas se referirem mesma Array; caso contrrio, falso. Por Exemplo: Local aNomes Local aNomesNovos Local aVar aNomes aNomesNovos aVar := { P.A.Cabral , Santos Dumont } := { P.A.Cabral , Santos Dumont } := aNomes

aVar == aNomes // .T. (apenas porque referem-se ao mesmo ponteiro) aNomes == aNomesNovos //.F. aVar == aNomesNovos //.F. Embora os elementos dos Arrays aNomes e aNomesNovos contenham os mesmos valores, eles so arrays distintas ( cada um ocupando uma referncia diferente na memria ) e, por isso, o operador == retorna falso.
Nota: O operador == uma sobreposio. Se for utilizado com string de caracteres, ele retornar verdadeiro se as duas strings foram exatamente iguais. Se for utilizado com duas variveis arrays, ele retornar verdadeiro se elas se referirem aos elementos do mesmo array.

Existem duas funes, desenvolvidas em AdvPl, que comparam dois arrays, elemento por elemento, e retorna .T. se forem iguais ou .F. caso contrrio. ArrayCompare() e fCompArray() Usando o mesmo exemplo acima, se utilizarmos ArrayCompare() ou fCompArray() teremos: Local aNomes Local aNomesNovos Local aVar aNomes aNomesNovos aVar := { P.A.Cabral , Santos Dumont } := { P.A.Cabral , Santos Dumont } := aNomes

40

ArrayCompare( aVar , aNomes ) ArrayCompare(aNomes , aNomesNovos ) fCompArray( aVar , aNomes ) fCompArray (aNomes , aNomesNovos )

// .T. //.T. // .T. //.T.

Neste caso ArrayCompare() e fCompArray() retornaro sempre .T. uma vez que os elementos no Array so iguais. Arrays como Parmetro: Ao escrever uma funo ns a definimos em termos de parmetros. Chamamos esses parmetros de Parmetros Formais da funo. Quando chamamos a funo, fornecemos os parmetros que denominamos Parmetros Reais. Quando passamos um Array como parmetro, o Advpl faz o parmetro formal apontar para o mesmo local indicado pelo parmetro real. Dessa forma, poderemos mudar os elementos do Array fazendo atribuies a seus parmetros. Se o parmetro real tiver um escopo Public ou Private, ele ficar tambm visvel dentro da rotina chamada. Portanto, poderemos usar o operador == para verificar se eles realmente apontam para o mesmo array. Exemplo: Private aNomes := { P.A.Cabral , Santos Dumont } Test( aNomes ) Function Test( aFormal ) Return( aFormal == aNomes ) //.T.(apenas porque //mesmo ponteiro) se referem ao

P.A.Cabral

Santos Dumont

aNomes

aFormal Podemos, concluir, a partir disto, que os array so passados por referncia; acima de tudo, podemos mudar seus elementos. Entretanto, precisamos distinguir entre a prpria varivel array e os elementos a que ela se refere. Ao passar um

41

array, como demonstrado, estamos passando uma referncia aos elementos do array; conseqentemente, a rotina receptora poder alter-los por meio de referncia. Entretanto, ela pode mudar a prpria referncia? Ela pode fazer a referncia apontar para outra array? Examinemos o seguinte fragmento de programa: Local aNomes := { P.A.Cabral , Santos Dumont } U_Test( aNomes ) Alert( aNomes[1] ) // P.A.Cabral User Function Test( aFormal ) Alert( aFormal[1] ) // P.A.Cabral aFormal := { Einstein , Leibiniz } Return( NIL ) A funo U_Test() atribui seu parmetro formal, aFormal, a um novo Array, mas isto no afeta o que aNomes indica. Ela simplesmente cria um novo Array e faz aFormal apontar para ele; aNomes permanece inalterado. como se tivssemos atribudo um tipo de dado diferente a aFormal aNomes continua inalterado no retorno. Disso podemos concluir que os Arrays so passados por valor! No podemos mudar o parmetro. Parece confuso mas: a varivel array passada por valor. Isto significa que no podemos mudar aquilo para o qual ela estiver apontando. Entretanto, como as variveis de arrays so simples referncias para os arrays, podemos mudar o contedo dos elementos do array, uma vez que sua rotina recebe uma referncia a eles. Passando Arrays por Referncia: Verificamos que as variveis do tipo Array so realmente passadas por valor. Mas podemos, tambm, pass-las por referncia. Para que isso acontea, temos que preceder o parmetro real com o smbolo @. Dessa forma, poderemos mudar os elementos do array como antes, e a rotina continuar recebendo uma referncia aos elementos do array. Mas, neste caso, a diferena que, agora, podemos mudar aquilo a que o array se refere, uma vez que a prpria varivel array foi passada por referncia. Podemos ilustrar isso da seguinte forma: Local aNomes := { P.A.Cabral , Santos Dumont }

42

U_Test( @aNomes ) Alert( aNomes[1] ) // Einstein User Function Test( aFormal ) Alert( aFormal[1] ) // P.A.Cabral aFormal := { Einstein , Leibiniz } Return( NIL ) Se quisermos que a rotina chamada nunca altere o valor do array original, podemos forar que ela sempre receba uma cpia do array ao invs do prprio array. Isso pode ser feito utilizando a funo aClone() da seguinte forma: Local aNomes := { P.A.Cabral , Santos Dumont } U_Test( aClone( aNomes ) ) Alert( aNomes[1] ) // P.A.Cabral User Function Test( aFormal ) Alert( aFormal[1] ) // P.A.Cabral aFormal[1] := Einstein Alert( aFormal[1] ) //Einstein Return( NIL ) A funo aClone() para uma cpia do Array criando uma nova referncia. Dessa forma, garantimos, na funo chamadora a integridade do array mas, na funo chamada podemos alterar o contedo da maneira que quisermos. Para exemplificar melhor a passagem por referncia, tomemos como exemplo o seguinte fragmento de cdigo que referncia uma varivel do tipo numrica. Local nVar := 1 Local cVar U_Test( @nVar ) cVar := Str( nVar ) Alert( cVar ) //Aqui nVar ter o valor 3

43

User Funcion Test( nFormal ) nFormal := 3 Return( NIL ) Passamos nVar por referncia para a funo U_Test(); portanto, no retorno, nVar foi alterada. Um aspecto importante a observar que, dentro da funo U_Test(), no sabemos se nFormal foi passado por referncia ou por valor. Apenas fizemos a atribuio. Este processo diferente em linguagens como C, que no suportam envio por referncia. Na linguagem C, se quisermos permitir que uma rotina altere um parmetro, precisaremos passar o endereo do parmetro. Conseqentemente, a rotina receptora receber um ponteiro para a verdadeira varivel e a modificar por vias indiretas. Neste caso a rotina receptora precisa saber como o parmetro foi passado ( o que no acontece em Advpl ). Em Advpl ocorre uma via indireta implcita quando fazemos uma atribuio a um parmetro formal que a rotina chamadora passa por referncia. Isso poder ser visualizado se imaginarmos que a codificao em Advpl recebesse um ponteiro para um parmetro real e, toda vez que mudssemos um parmetro passado por referncia, estaramos alterando o parmetro real. O compilador ou o sistema, na execuo, estaro implementando a via indireta. Mas como isso se aplica aos arrays passados por referncia? J vimos que os arrays so implementados como ponteiros; portanto, ao passar um array por referncia, na verdade, estamos passando um ponteiro para um ponteiro. O Advpl manipula a via indireta quando acessamos os elementos, mas, como recebemos uma referncia varivel do array, podemos mudar aquilo para o qual o array aponta, alm do seu contedo. Elementos de Arrays como Parmetros: Em Advpl os elementos do array so sempre passados por valor. Para podermos modificar um elemento do array passado como parmetro, devemos preceder a varivel com o operador de referncia @, conforme fragmento de cdigo abaixo. Local aTeste[1] aTeste[1] := "Valor Antigo" Alert( aTeste[1] ) //Valor Antigo aTeste( aTeste[1] ) Alert( aTeste[1] ) //Valor Antigo aTeste( @aTeste[1] ) Alert( aTeste[1] ) //Novo Valor

44

Static function aTeste( cTeste ) cTeste := "Novo Valor" Return( NIL )

Arrays Multidimensionais: Examine a seguinte declarao de um array bidimensional: Local aValores[ 3 , 2 ] /*/ que tambm pode ser declarado como Local aValores := Array( 3 , 2 ) Local aValores := { { NIL , NIL } , { NIL , NIL } , { NIL , NIL } } Local aValores := { Array( 2 ) , Array( 2 ) , Array( 2 ) } /*/ No Advpl essa(s) declarao(es) sero implementadas como um array de trs elementos do tipo array e cada array com dois elementos ( onde os valores iniciais so NIL ) Podemos testar da seguinte forma ValType( aValores ) Len( aValores ) ValType( aValores[1] ) ) Len( aValores[1] ) aValores[1,1] ValType( aValores[1,1] ) //A //3 //A //2 //NIL //U

Conforme demonstrado acima, aValores uma referncia a um Array de Trs elementos, em que cada elemento uma referncia outra array de dois elementos. Cada elemento de aValores simplesmente outro array e podemos test-lo dessa forma. Por exemplo, para localizar aValores[1] passe-o para a funo aScan(), como em: nPos := aScan( aValores[1] , { |x| x == Naldo } )
45

Para classificar aValores[2], passe-o para aSort() com: aSort( aValores[2] ) ou aSort( aValores[2] , NIL , NIL , { |x,y| x < y } ) Abaixo segue a estrutura de memria de um array bidimensional.

aValor es

1/1

1/2

2/1

2/2

3/1

3/2

possvel referenciar um elemento do array aValores como aValores[1,1] ou aValores[1] mas, aValores[ , 1 ], apesar de no ocasionar erro durante a compilao gerar erro durante a execuo. Isso significa que no podemos classificar, localizar ou selecionar colunas diretamente de um array bidimensional.
Nota: No Advpl os Arrays Multidimensionais so implementados como Arrays de Arrays.

Processando Array com aEval():

46

aEval() uma internal function que utilizada para processar variveis do tipo Array. Em sua forma mais simples usa dois parmetros, um Array e um bloco de codificao. aEval() simplesmente faz um Loop em cada elemento do Array, passando um de cada vez, como um parmetro para o Bloco de Codificao. Se o Array contm 5 elementos, aEval() avalia o bloco 5 vezes. Como exemplo, a seguinte chamada a aEval() mostra no Console do Server cada elemento do Array.
Local aArray := {Marinaldo,Paulo Lira,Paulo Martins,Sergio,Henry } aEval( aArray , { |cElem| ConOut( cElem ) } )

Observamos que aEval() est avaliando o bloco de codificao, passando um parmetro por vez. Denominamos o parmetro como cElem, mas ele pode ser atribudo com qualquer nome. aEval() , realmente, uma funo muito simples. Poderamos at criar a nossa. Por exemplo: User Function aEval( aArray , bEval ) Local nLoop Local nLoops nLoops := Len( aArray ) For nLoop := 1 To nLoops Eval( bEval , aArray[ nLoop ] ) Next nLoop Return( NIL ) E se usarmos a nossa funo para processar um array teramos o mesmo resultado de aEval().
Local aArray := {Marinaldo,Paulo Lira,Paulo Martins,Sergio,Henry } U_aEval( aArray , { |cElem| ConOut( cElem ) } )

Na prtica usaremos a funo interna ( bem mais rpida ), mas tivemos uma idia de como ela pode ser implementada em Advpl. Buscando um Array Bidimensional: Existem vrias funes de manipulao de Array. Uma delas a aScan() que serve para efetuar pesquisa em um Array e retorna o numero do elemento do Array ( ndice ) se a pesquisa foi satisfeita e Zero (0) caso contrrio. Essa funo poder ser utilizada tanto em Arrays unidimensionais como em Arrays
47

Bidimensionais. aScan() recebe quatro parmetros: O primeiro o Array a ser pesquisado, o segundo um Bloco de Cdigo com as instrues para pesquisa, o terceiro a posio inicial ( ndice ou elemento inicial ) para pesquisa e o quarto a posio final para pesquisa ( ndice ou elemento final ). O primeiro parmetro o Array a ser pesquisado obrigatrio, j do segundo ao quarto so opcionais. O segundo parmetro deve ser passado quando o array a ser pesquisado for um array Multidimensional ou Bidimensional e, quando o Bloco de Codificao for passado aEval() se comportar de maneira diferente do usual. Em vez de aScan() comparar internamente elementos do Array com o que estamos procurando, ela permitir que o bloco passado como parmetro faa isso. aScan(), como aEval(), faz um Loop em cada elemento do Array passando cada elemento por sua vez para o bloco de codificao. O bloco de codificao, passado como parmetro, deve retornar um valor lgico, indicando se o elemento que aScan() acabou de passar era o que estvamos procurando; .T. ( true = verdadeiro ) significa que o atual elemento o que estamos procurando; .F. ( false = falso ) significa que no . Se a avaliao do Bloco de Cdigo for verdadeira, aScan() interrompe a busca e retorna o nmero do atual elemento. Se a avaliao do Bloco de Cdigo for falsa, aScan() salta para o prximo elemento e efetua a avaliao do Bloco mais uma vez. aScan() prossegue seu processo de busca at terminar todos os elementos, em cujo caso retornar um Zero (0), ou at a avaliao do Bloco retornar verdadeiro. aScan() semelhante a aEval(), exceto pelo fato de no existir uma maneira de interromper aEval() prematuramente. Exemplos: Local aStru := {; { SNome , C , 10 , 0 },; { PNome , C , 10 , 0 },; { Ender1 , C , 30 , 0 },; { Ender2 , C , 40 , 0 } ; } Local bExemplo := { |uElem| ConOut( uElem ) } aEval( aStru[1] , bExemplo ) /*/ Neste Caso, processamos apenas os elementos do sub-array aStru[1] e Teremos a seguida sada no Console do Server: SNome C 10 0 /*/

48

Para processar todos os elementos do array poderamos faz-lo da seguinte forma: Local aStru := {; { SNome , C , 10 , 0 },; { PNome , C , 10 , 0 },; { Ender1 , C , 30 , 0 },; { Ender2 , C , 40 , 0 } ; } Local bExemplo := { |uElem| ConOut( uElem ) } Local nLoop Local nLoops //Utilizando um Loop For/Next nLoops := Len( aStru ) For nLoop := 1 To nLoops aEval( aStru[ nLoop ] , bExemplo ) Next nLoop //Ou aEval de forma recursiva aEval( aStru , { |aElem| aEval( aElem , bExemplo ) } ) /*/ Neste Caso, processamos Todos os elementos do Array e seus sub-arrays. Teremos, dessa forma, a seguida sada no Console do Server: SNome C 10 0 PNome C 10 0 Ender1 C 30 0 Ender2 C 40 0 /*/

49

Quando usarmos um Bloco de Codificao com aScan(), ela passara cada elemento por vez para o bloco. Se esse elemento for outra Array o parmetro formar conter uma referncia para esse Array. O bloco poder ento usar um s subscrito com seu parmetro para acessar determinado elemento. Por exemplo, o seguinte fragmento de cdigo localiza o nmero do elemento do Array que descreve o campo PNome Local aStru := {; { SNome { PNome { Ender1 { Ender2 , C , 10 , 0 },; , C , 10 , 0 },; , C , 30 , 0 },; , C , 40 , 0 } ;

} //Monto bloco para comparao exata Local bAscan := { |aCpo| Upper( AllTrim( aCpo[1] ) ) == PNOME } Local nPos nPos := aScan( aStru , bAscan ) //nPos ir retornar 2 IF ( nPos > 0 ) ConOut( "A Posicao eh:" + Str( nPos ) ) ConOut( "Conteudo:" + aStru[ nPos , 1 ] ) Else ConOut( "Elemento nao encontrado" ) EndIF

Classificando um Array Unidimensional e Bidimensional: Da mesma forma que podemos passar um bloco para aScan(), tambm podemos passar um bloco para aSort(). A funo aSort() classifica o Array passada por parmetro, na ordem ascendente, mas, em substituio, podemos passar para ela um bloco de codificao para fazer a comparao. Quando fizermos isso, em vez de aSort() comparar os elementos internamente, ela passar para o Bloco de Codificao os dois elementos necessrios para a comparao. A avaliao do Bloco, ento, retornar verdadeiro se os dois elementos estiverem classificados em ordem; caso contrrio, retornar falso. Quando o Bloco retornar falso, aSort() trocar os dois elementos. importante observar que aSort() passa o elemento com o nmero mais baixo do Array como primeiro parmetro do bloco; desta forma, saberemos quem quem! igualmente importante notar que no teremos conhecimento dos dois elementos que estaremos comparando; s saberemos que o primeiro parmetro um elemento com nmero menor do que o segundo. aSort() assegura a classificao do Array, determina os elementos a comparar e sabe quando a classificao termina.
50

Para classificar um Array Unidimensional em ordem ascendente a passagem do bloco opcional. Mas, para orden-lo em ordem descendente, teremos passar o bloco informado a regra para a classificao: Exemplos de classificao de Array unidimensional Local aNomes := {; Vinicius,; Paulo Lira,; Marinaldo,; Srgio,; Paulo Martins,; Andre,; Valeria,; Joo,; Adriana; } Local aSvNomes := aClone( aNomes ) Local bSort //Efetua a cpia do array

//Mostrando as informaes do Array no Console ConOut( Lista de Nomes nao ordenados ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) //Ordenando o Array Unidimensional em ordem ascendente sem //utilizar o Bloco de Codificao: aSort( aNomes ) //Mostrando as informaes do Array no Console ConOut( Lista de Nomes ordenados sem Bloco ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) aNomes := aClone( aSvNomes ) //Restaurando aNomes //Mostrando as informaes do Array no Console ConOut( Lista de Nomes nao ordenados ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) //Monta Bloco para Classificao em Ordem Ascendente bSort := { |x,y| x < y } aSort( aNomes , NIL , NIL , bSort )
51

//Mostrando as informaes do Array no Console ConOut( Lista de Nomes ordenados ( asc ) utilizando Bloco ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) //Monta Bloco para Classificao em Ordem Descendente bSort := { |x,y| x > y } aSort( aNomes , NIL , NIL , bSort ) //Mostrando as informaes do Array no Console ConOut( Lista de Nomes ordenados ( desc ) utilizando Bloco ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) aNomes := aClone( aSvNomes ) //Restaurando a Nomes //Mostrando as informaes do Array no Console ConOut( Lista de Nomes nao ordenados ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) //Ordenando o Array em ordem Ascendente considerando apenas //alguns elementos aSort( aNomes , 1 , 3 , { |x,y| x < y } ) //Mostrando as informaes do Array no Console ConOut( Lista de Nomes Ordenados. Elem 1 a 3 ) ConOut( ) aEval( aNomes , { |cNome| ConOut( cNOme ) } ) ConOut( ) Exemplos de classificao de Array Bidimensional: Local aStru := {; { "SNome" { "PNome" { "Ender1" { "Ender2" } Local aSvStru Local bSort := aClone( aStru ) //Efetua a cpia do array , "C" , 10 , 0 },; , "C" , 10 , 0 },; , "C" , 30 , 0 },; , "C" , 40 , 0 } ;

/Mostrando as informaes do Array no Console ConOut( "Lista nao ordenada" )


52

ConOut( ) aEval( aStru , { |aElem| ConOut( aElem[1] ) } ) ConOut( ) //Definindo bloco para ordenacao ascendente bSort := { |x,y| x[1] < y[1] } //Ordenando Array aSort( aStru , NIL , NIL , bSort ) //Mostrando as informaes do Array no Console ConOut( "Lista ordenada ascendente" ) ConOut( "" ) aEval( aStru , { |aElem| ConOut( aElem[1] ) } ) ConOut( "" ) //Definindo bloco para ordenacao descendente bSort := { |x,y| x[1] > y[1] } //Ordenando Array aSort( aStru , NIL , NIL , bSort ) //Mostrando as informaes do Array no Console ConOut( "Lista ordenada descendente" ) ConOut( ) aEval( aStru , { |aElem| ConOut( aElem[1] ) } ) ConOut( )

53

Вам также может понравиться