Resposta rápida
- Decidir
If/ElseIf/Else/EndIfouDo Case/Case/Otherwise/EndCase; nos dois, o primeiro teste verdadeiro vence e os demais são ignorados.- Decidir numa linha
IIf(condição, valorSeVerdadeiro, valorSeFalso), para escolher um valor, não para executar lógica.- Repetir sabendo quantas vezes
For nX := 1 To n [Step k]... Next.- Repetir até uma condição mudar
While condição... EndDo, e o corpo precisa fazer a condição mudar.- Desviar dentro do laço
Exitsai do laço;Looppula para a próxima volta.
Nesta página8 seções
Este é o sexto texto da série. Os testes que aparecem aqui usam os operadores de Operadores em AdvPL, e os laços percorrem as estruturas de Arrays em AdvPL.
If: a decisão que você vai escrever mil vezes
Exemplo: faixa de desconto por valor do pedido.
If nValor >= 10000
nDesc := 10
ElseIf nValor >= 5000
nDesc := 5
ElseIf nValor >= 1000
nDesc := 2
Else
nDesc := 0
EndIfA ordem dos testes é a regra de negócio. Um pedido de 12 mil satisfaz as três
condições, mas só a primeira executa. Inverta a ordem — >= 1000 no topo — e
todo pedido acima de mil ganha 2%, sem erro nenhum de compilação.
A condição precisa ser lógica. If cTipo com cTipo caractere não é “se
tiver conteúdo”: é erro de execução. Teste o que você quer dizer —
!Empty(cTipo), cTipo == "N". Os tipos estão em
Tipos de dados em AdvPL.
Do Case: a mesma coisa, mais legível com muitos ramos
Do Case
Case nValor >= 10000
nDesc := 10
Case nValor >= 5000
nDesc := 5
Case nValor >= 1000
nDesc := 2
Otherwise
nDesc := 0
EndCaseO comportamento é o do If encadeado: os Case são avaliados de cima para
baixo, o primeiro verdadeiro executa e o bloco termina — não existe “cair” para
o próximo, como no switch de outras linguagens, e não é preciso break.
Otherwise é opcional e faz o papel do Else.
Escolher entre os dois é questão de leitura: com muitos ramos, Do Case
deixa as condições alinhadas na margem. Do Case/EndCase é a forma
herdada do Clipper, de onde vem boa parte da sintaxe do AdvPL, e por isso
ainda é a mais comum em código legado do próprio padrão.
IIf: escolher um valor, não executar lógica
cSituacao := IIf(nSaldo > 0, "Em aberto", "Quitado")IIf() é função da própria linguagem: recebe a condição e dois valores e
devolve um deles. Serve para montar texto, escolher um parâmetro, preencher um
campo numa linha só.
Dois cuidados. IIf dentro de IIf é um Do Case escondido, que o próximo
a mexer no fonte vai ter que desmontar. E não coloque nos ramos chamadas que
gravam, travam registro ou mostram mensagem: se um ramo faz alguma coisa,
ele pertence a um If.
For/Next: quando você sabe quantas voltas
nTotal := 0
For nX := 1 To Len(aItens)
nTotal += aItens[nX, 2]
Next nXO nome da variável depois do Next é opcional, mas em laço aninhado ele
documenta qual laço fecha ali.
Step muda o incremento. O uso mais útil é o laço de trás para frente, que o
padrão usa, por exemplo, na função CTB276AtuPer(), para
ajustar diferença de rateio na última linha que comporta:
For nX := Len(aItens) To 1 Step -1
If aItens[nX, 2] <= 0
aDel(aItens, nX)
aSize(aItens, Len(aItens) - 1)
EndIf
Next nXRemovendo elementos, andar de trás para frente é o que evita pular posição: cada remoção desloca só o que já foi visitado.
While/EndDo: quando a parada depende do dado
O caso clássico é percorrer os itens de um pedido na SC6 pelo índice 1
(C6_FILIAL+C6_NUM+C6_ITEM+C6_PRODUTO):
Local nTotPed := 0
SC6->(DbSetOrder(1))
SC6->(DbSeek(xFilial("SC6") + cPedido))
While SC6->(!Eof()) .And. SC6->C6_FILIAL == xFilial("SC6") ;
.And. SC6->C6_NUM == cPedido
nTotPed += SC6->C6_VALOR
SC6->(DbSkip())
EndDoA condição tem três partes e todas importam. !Eof() para no fim da tabela;
filial e número param no último item deste pedido — sem eles, o laço segue
somando os pedidos seguintes até o fim do arquivo. Como esse laço move o
ponteiro da SC6, quem chama costuma esperar a área restaurada: isso é
GetArea e RestArea. E se a pergunta
for só “quanto soma”, uma
consulta SQL resolve sem laço.
Confira no seu ambiente:
SELECT C6_NUM, COUNT(*) AS ITENS, SUM(C6_VALOR) AS TOTAL
FROM SC6010
WHERE D_E_L_E_T_ = ' '
GROUP BY C6_NUMO laço infinito
While SC6->(!Eof()) .And. SC6->C6_NUM == cPedido
If SC6->C6_VALOR <= 0
Loop
EndIf
nTotPed += SC6->C6_VALOR
SC6->(DbSkip())
EndDoParece certo. O primeiro item com valor zerado prende a thread para sempre:
Loop volta para o teste sem passar pelo DbSkip(), e a condição continua
verdadeira. Não há erro nem log — a tela congela, ou o job roda até alguém
derrubar a conexão no monitor.
A regra prática: em While, o avanço vem antes de qualquer Loop. Ou o
DbSkip() sobe para antes do Loop, ou o teste inverte e o acúmulo fica
dentro do If:
While SC6->(!Eof()) .And. SC6->C6_NUM == cPedido
If SC6->C6_VALOR > 0
nTotPed += SC6->C6_VALOR
EndIf
SC6->(DbSkip())
EndDoO mesmo vale para contador: While nTent < 3 sem nTent++ no corpo não
termina nunca.
Exit e Loop
Exit encerra o laço mais interno e segue para a linha depois do Next ou do
EndDo — é o jeito limpo de parar uma busca quando o item já foi achado (para
busca simples em array, aScan() resolve em uma linha). Loop pula o resto do corpo e
vai para a próxima volta; no For, o incremento acontece normalmente, então o
risco de laço infinito é só do While.
Com laços aninhados, Exit sai apenas do laço onde está. Se o achado precisa
encerrar os dois, use uma variável lógica e teste-a no laço de fora.
Qual usar
| Situação | Estrutura |
|---|---|
| Dois ou três caminhos | If/ElseIf/Else |
| Muitos caminhos com testes diferentes | Do Case |
| Escolher um valor numa expressão | IIf() |
| Percorrer array ou repetir n vezes | For/Next |
| Remover itens de array | For... Step -1 |
| Percorrer registros até mudar a chave | While/EndDo com DbSkip() |
A estrutura do fonte onde tudo isso mora está em Estrutura de um fonte AdvPL, e o escopo das variáveis dos exemplos, em Escopo de variáveis em AdvPL.
Laço travando thread em produção? Fale com a gente.
Referências
TDNtdn.totvs.com
TDN — AdvPL, documentação da linguagemTDNtdn.totvs.com
TDN — IIf
Continue por aqui
- Vídeo
DesenvolvimentoTutorial
Arrays em AdvPL: índice em 1, aDel que não encolhe e cópia por referência
Como criar, crescer, procurar, ordenar e remover itens de um array em AdvPL, por que aDel precisa de aSize e por que atribuir um array não copia nada.