Resposta rápida
- Atribuição
- use
:=; o=sozinho também atribui, mas é ambíguo. - Texto
- compare com
==. O=compara só até o tamanho do lado direito enquantoSET EXACTestiver desligado. - Diferente
!=,<>e#são a mesma coisa, e seguem a regra do=, não a do==.- Lógica
.And.e.Or.param de avaliar assim que o resultado está decidido.- Parâmetro opcional
Default cVar := valorpreenche só o que chegouNil.
Nesta página6 seções
Operador parece o assunto mais tranquilo da linguagem, e por isso aparece em tanto bug de produção. Os tipos envolvidos estão em Tipos de dados em AdvPL.
Atribuição e incremento
| Operador | Efeito | Equivale a |
|---|---|---|
:= | atribui | — |
+= | soma e atribui | nX := nX + n |
-= | subtrai e atribui | nX := nX - n |
*= | multiplica e atribui | nX := nX * n |
/= | divide e atribui | nX := nX / n |
++ / -- | soma ou subtrai 1 | nX := nX + 1 |
O = também atribui quando está sozinho numa linha, mas dentro de uma
expressão ele compara; := tira a dúvida. O += funciona com texto:
Local cLog := ""
Local nLidos := 0
nLidos++
cLog += "Produto processado: " + cValToChar(nLidos) + CRLFAritméticos e concatenação
| Operador | Efeito | Exemplo | Resultado |
|---|---|---|---|
+ | soma | 7 + 3 | 10 |
- | subtração | 7 - 3 | 4 |
* | multiplicação | 7 * 3 | 21 |
/ | divisão | 7 / 2 | 3.5 |
** ou ^ | potência | 2 ** 10 | 1024 |
% | resto da divisão | 7 % 2 | 1 |
O % resolve “a cada mil registros”: If nLidos % 1000 == 0.
Em texto, + concatena como está. O - também concatena, mas leva os espaços
do fim do operando da esquerda para o fim do resultado: "PGV " - "001" dá
"PGV001 ". Na prática, AllTrim() explícito lê melhor.
Comparação: onde mora o bug
| Operador | Teste |
|---|---|
== | exatamente igual |
= | igual (em texto, depende de SET EXACT) |
!=, <>, # | diferente — três grafias para o mesmo operador |
<, <= | menor, menor ou igual |
>, >= | maior, maior ou igual |
$ | o texto da esquerda está contido no da direita |
Com número, = e == dão o mesmo resultado. Com texto, não. O = herda a
regra do Clipper: com SET EXACT desligado, que é o padrão, ele compara
caractere a caractere só até acabar o texto da direita. O comando existe
na base do compilador — em stdwin.ch, traduzido para
Set( _SET_EXACT,... ).
Local cCod := "PGV001"
Local lOk := .F.
lOk := (cCod = "PGV") //.T. -- só 3 caracteres comparados
lOk := ("PGV" = cCod) //.F. -- agora o lado direito é o mais longo
lOk := (cCod = "") //.T. -- nada à direita para comparar
lOk := (cCod == "PGV") //.F. -- comparação exataA terceira linha é a que mais custa: If cFiltro = "" parece testar se o
filtro está vazio e é verdadeira para qualquer conteúdo. Para vazio, use
Empty().
Como o = depende de uma configuração que qualquer rotina pode ter alterado,
o seguro é == em todo texto.
O == tem a sua armadilha: considera os espaços. Campo chega com o tamanho do
dicionário, e B1_COD tem 15 posições, então SB1->B1_COD == "PGV001" é falso
com o produto certo posicionado. Compare com AllTrim(SB1->B1_COD) ou complete
o literal com PadR("PGV001", Len(SB1->B1_COD)).
SELECT X3_CAMPO, X3_TIPO, X3_TAMANHO
FROM SX3010
WHERE X3_CAMPO = 'B1_COD'
AND D_E_L_E_T_ = ' 'O $ diferencia maiúsculas de minúsculas e é a forma curta de testar uma
lista: If SB1->B1_TIPO $ "PA|PI". O separador evita casar o fim de um código
com o começo do outro.
Lógicos e curto-circuito
| Operador | Resultado .T. quando |
|---|---|
.And. | os dois lados são verdadeiros |
.Or. | pelo menos um lado é verdadeiro |
.Not. ou ! | o operando é falso |
A avaliação vai da esquerda para a direita e para quando o resultado está
decidido: num .And. com o primeiro lado falso, o segundo não roda; num .Or.
com o primeiro verdadeiro, também não. O próprio padrão conta com isso para
economizar avaliação cara: uma guarda de tipo barata evita gastar uma
chamada mais pesada quando o dado já reprovou no primeiro teste. Em
AjRetParam(), uma das condições é
ValType(aRet) == "A" .AND. Len(aRet) == Len(aParamBox): se aRet não fosse
array, o Len() do lado direito nem chegaria a ser avaliado.
A consequência prática é a ordem dos testes:
// Guarda de tipo primeiro, acesso depois
If ValType(aItens) == "A" .And. Len(aItens) > 0 .And. aItens[1] > 0
//...
EndIf
// Teste barato primeiro, consulta cara depois
If lIntegra .Or. U_PGV002(cPedido)
//...
EndIfO inverso também vale: função com efeito colateral à direita de um .And.
pode não executar. Se precisa rodar sempre, chame antes e guarde o retorno. A
guarda de tamanho em array está em Arrays em AdvPL.
Default: valor para parâmetro opcional
Parâmetro que não foi passado chega Nil. O Default resolve isso em uma
linha, logo depois das declarações Local:
User Function PGV001(cFilPed, nDias, lMostra)
Local aArea := GetArea()
Default cFilPed := xFilial("SC5")
Default nDias := 30
Default lMostra := !IsBlind()
//...
RestArea(aArea)
Return NilNão é operador da linguagem, é comando do pré-processador: em protheus.ch,
Default x := v vira x := If( x == nil, v, x ). Logo, só age sobre
Nil — string vazia, zero e .F. passam intactos —, e o fonte precisa incluir
protheus.ch. Sobre Local, veja
Escopo de variáveis em AdvPL.
Precedência
Quando não há parênteses, a ordem de avaliação é:
- incremento e decremento prefixados (
++nX); - operadores de texto;
- aritméticos — potência, depois
*,/e%, depois+e-; - relacionais (
==,<,$…); - lógicos —
.Not., depois.And., depois.Or.; - atribuição;
- incremento e decremento pós-fixados (
nX++).
O que morde é o 5: .And. vem antes de .Or., então lA .Or. lB .And. lC
é lido como lA .Or. (lB .And. lC). Se a regra era “(A ou B) e C”, o código
aceita registro que não devia. Parêntese não custa nada.
Próximo passo da série: Condições e laços em AdvPL.
Se uma regra de negócio sua depende de uma comparação que ninguém sabe explicar, fale com a gente.
Referências
TDNtdn.totvs.com
TDN — AdvPL (espaço TEC)
Continue por aqui
- Vídeo
DesenvolvimentoTutorial
Tipos de dados em AdvPL: as oito letras do ValType e como converter entre elas
Tipos de dados em AdvPL: as oito letras do ValType, como o AdvPL identifica cada tipo, como converter sem surpresa e o que a declaração com As garante.