Estruturas de controle
Para fazer um trabalho real em um programa você tem que ser capaz de tomar decisões, iterar através de coleções de itens e executar ações repetidamente. Para isso, você precisa de estruturas de controle. Pony tem estruturas de controle que serão familiares aos programadores que utilizaram a maioria dos idiomas, como se, enquanto e para, mas no Pony, elas funcionam de forma ligeiramente diferente.
Condicionais
A mais simples estrutura de controle é o bom velho se. Ela permite realizar alguma ação somente quando uma condição é verdadeira. No Pony, é assim que parece:
if a > b then
env.out.print("a é maior")
end
Posso usar inteiros e ponteiros para a condição como posso em C? Não. No Pony se as condições devem ter tipo Bool, ou seja, são sempre verdadeiras ou falsas. Se você quiser testar se um número a não é 0, então você precisa dizer explicitamente a != 0. Esta restrição remove toda uma categoria de erros potenciais dos programas Pony.
Se você quiser algum código alternativo para quando a condição falhar, basta adicionar um outro:
if a > b then
env.out.print("a é maior")
else
env.out.print("a não é o maior")
end
Muitas vezes você quer testar mais de uma condição de uma só vez, dando-lhe mais de dois resultados possíveis. Você pode fazer um ninho de declarações, mas isso rapidamente se torna feio:
if a == b then
env.out.print("são os mesmos")
else
if a > b then
env.out.print("a é maior")
else
env.out.print("b maior")
end
end
Como alternativa, o Pony fornece a outra palavra-chave que combina um outro e um "se". Isto funciona como dizer "se" em outros idiomas e você pode ter quantas mais if quiser para cada if.
if a == b then
env.out.print("são os mesmos")
elseif a > b then
env.out.print("a é maior")
else
env.out.print("b maior")
end
Por que não posso simplesmente dizer "se" como faço em C? Por que a palavra-chave extra? A relação entre if e else em C, e outros idiomas similares, é ambígua. Por exemplo, a palavra-chave "se":
// código em C
if(a)
if(b)
printf("a e b\n");
else
printf("não a\n");
Aqui não é óbvio se o outro é uma alternativa ao primeiro ou ao segundo se. Na verdade, aqui o outro se relaciona com o if(b), portanto nosso exemplo contém um bug. O Pony evita este tipo de bug manipulando se e de outra forma e a necessidade de um outro se sai de lá.
Estruturas de controle são expressões
A grande diferença para as estruturas de controle entre o Pony e outros idiomas é que no Pony tudo é uma expressão. Em linguagens como C++ e Java se for uma declaração, não uma expressão. Isto significa que não se pode ter um se dentro de uma expressão, tem que haver um operador condicional separado '?'.
Em Pony control flow afirmações como esta são todas as expressões que devolvem um valor. Você devolve um valor a uma declaração. Você para loop (que chegaremos um pouco mais tarde) lhe devolve um valor.
Isto significa que você pode usar se diretamente em um cálculo:
x = 1 + if lots then 100 else 2 end
Isto dará x um valor de 3 ou 101, dependendo dos lotes variáveis.
Se o então e outros ramos de um se produzem diferentes tipos, então o se produz uma união dos dois.
var x: (String | Bool) =
if friendly then
"Hello"
else
false
end
Mas e se o meu se não tiver um outro? Qualquer outro ramo que não exista dá um Nenhum implícito.
var x: (String | None) =
if friendly then
"Olá"
End
As mesmas regras que se aplicam ao valor de um se a expressão se aplica também aos loops. Vamos dar uma olhada no que seria um valor de loop:
actor Main
new create(env: Env) =>
var x: (String | None) =
for name in ["Bob"; "Fred"; "Sarah"].values() do
name
end
match x
| let s: String => env.out.print("x é " + s)
| None => env.out.print("x é vazio")
End
Isto dará x o valor "Sarah", pois é o sobrenome em nossa lista. Se nosso loop tiver 0 iterações, então o valor de seu outro bloco será o valor de x. Ou se não houver mais nenhum bloco, o valor será Nenhum.
actor Main
new create(env: Env) =>
var x: (String | None) =
for name in Array[String].values() do
name
else
"sem nomes!"
end
match x
| let s: String => env.out.print("x é " + s)
| None => env.out.print("x é vazio")
End
Aqui o valor de x é "sem nomes"!
actor Main
new create(env: Env) =>
var x: (String | None) =
for name in Array[String].values() do
name
end
match x
| let s: String => env.out.print("x é " + s)
| None => env.out.print("x é vazio")
End
E por último, aqui x seria Nenhum.
Laços
Se lhe permite escolher o que fazer, mas para fazer algo mais de uma vez, você quer um loop.
Enquanto
Pony enquanto loops são muito parecidos com aqueles em outros idiomas. Uma expressão de condição é avaliada e, se for verdade, executamos o código dentro do loop. Quando terminamos, avaliamos a condição novamente e continuamos até que ela seja falsa.
Aqui está um exemplo que imprime os números de 1 a 10:
var count: U32 = 1
while count <= 10 do
env.out.print(count.string())
count = count + 1
end
Assim como se as expressões, embora seja também uma expressão. O valor retornado é apenas o valor da expressão dentro do laço, na última vez em que o contornamos. Para este exemplo que será o valor dado por contagem = contagem + 1 quando a contagem for incrementada para 11. Como as atribuições do Pony devolvem o antigo valor de nosso loop while retornará 10.
Mas e se a condição avaliarmos como falsa na primeira vez que tentarmos, então não daremos a volta ao laço de forma alguma? No Pony, enquanto as expressões também podem ter um outro bloco. Em geral, os outros blocos do Pony fornecem um valor quando a expressão a que eles estão ligados não o faz. Um tempo não tem um valor a dar se a condição avalia para falso na primeira vez, então o outro o fornece em seu lugar.
Então, isto é como um outro bloco em um loop de um tempo em Python? Não, isto é muito diferente. Em Python, o outro é executado quando o tempo se completa. Em Pony, o outro só é executado quando a expressão no tempo não é.
Break
Às vezes, você quer parar parcialmente um loop e desistir completamente. Pony tem a palavra-chave break para isto e é muito semelhante à sua contraparte em línguas como C++, C# e Python.
O break sai imediatamente do loop mais interno em que está. Como o loop tem que retornar uma quebra de valor pode ter uma expressão. Isto é opcional, e se for deixado de fora, o valor do outro bloco é devolvido.
Vamos a um exemplo. Suponhamos que você queira ver uma lista de nomes, procurando por "Jack" ou "Jill". Se nenhum dos dois aparecer, você apenas pegará o sobrenome que lhe foi dado, e se não lhe for dado nenhum nome, você usará "Herbert".
var name =
while moreNames() do
var name' = getName()
if name' == "Jack" or name' == "Jill" then
break name'
end
name'
else
"Herbert"
End
Portanto, primeiro perguntamos se há mais nomes a serem obtidos. Se houver, então, obtemos um nome e vemos se é "Jack" ou "Jill". Se for o fim e sairmos do ciclo, devolvemos o nome que encontramos. Caso contrário, tentamos novamente.
O nome da linha' aparece no final do loop, de modo que será nosso valor retornado da última iteração se não for encontrado "Jack" ou "Jill".
O outro bloco fornece nosso valor de "Herbert", se não houver nenhum nome disponível.
Posso sair de múltiplos loops aninhados, como a quebra rotulada Java? Não, o Pony não suporta isso. Se você precisar romper vários loops, você provavelmente deve refatorar seu código ou usar uma função de trabalhador.
Continue
Às vezes, você quer parar parcialmente a iteração de um loop e passar para o próximo. Como em outros idiomas, Pony usa a palavra-chave continue para isso.
Continua a executar a iteração atual do loop mais interno em que está e avalia a condição pronta para a próxima iteração.
Se continuar é executado durante a última iteração do loop, então não temos valor a retornar do loop. Neste caso, usamos a outra expressão do laço para obter um valor. Como com a expressão se, se nenhuma outra expressão for fornecida, nenhuma é retornada.
Posso continuar um laço externo, aninhado como o Java etiquetado continue? Não, o Pony não suporta isso. Se você precisar continuar um laço externo, você provavelmente deve refatorar seu código.
For
Para iterar sobre uma coleção de itens, Pony usa a palavra-chave. Isto é muito semelhante a foreach em C#, para...em Python e para em Java quando usado com uma coleção. É muito diferente de para em C e C++.
O Pony para iteração de loop sobre uma coleção de itens usando um iterador. Em cada iteração, ao redor do loop, perguntamos ao iterador se há mais elementos para processar, e se há, perguntamos pelo próximo.
Por exemplo, para imprimir todas as cordas de uma matriz:
for name in ["Bob"; "Fred"; "Sarah"].values() do
env.out.print(name)
end
Observe a chamada aos valores() na matriz - isto é porque o laço precisa de um iterador, não de uma matriz.
O iterador não precisa ser de nenhum tipo em particular, mas precisa fornecer os seguintes métodos:
fun has_next(): Bool
fun next(): T?
Onde T é o tipo dos objetos da coleção. Você não precisa se preocupar com isso a menos que esteja escrevendo seus próprios iteradores. Para usar as coleções existentes, como as fornecidas na biblioteca padrão, você pode simplesmente usar para e tudo funcionará. Se você escrever seus próprios iteradores, observe que usamos a digitação estrutural, de modo que seu iterador não precisa declarar que ele fornece qualquer tipo em particular.
Você pode pensar no exemplo acima como sendo equivalente a:
let iterator = ["Bob"; "Fred"; "Sarah"].values()
while iterator.has_next() do
let name = iterator.next()?
env.out.print(name)
end
Note que o nome da variável é declarado let, portanto não é possível atribuir à variável de controle dentro do laço.
Posso usar o break e continuar com para loops? Sim, para loops pode ter outras expressões anexadas e pode usar break e continuar da mesma forma que enquanto.
Repita
A construção final do loop que o Pony fornece é repetida até. Aqui avaliamos a expressão no laço e depois avaliamos uma expressão de condição para ver se terminamos ou devemos dar a volta novamente.
Isto é semelhante a fazer em C++, C# e Java, exceto que a condição de terminação é invertida; isto é, aquelas linguagens terminam o loop quando a expressão da condição é falsa, mas Pony termina o loop quando a expressão da condição é verdadeira.
As diferenças entre while e repeat no Pony são:
- Sempre damos a volta ao laço pelo menos uma vez com repetição, enquanto que com enquanto podemos não dar a volta de forma alguma.
- A condição de terminação é invertida.
Suponha que estamos tentando criar algo e queremos continuar tentando até que seja bom o suficiente:
actor Main
new create(env: Env) =>
var counter = U64(1)
repeat
env.out.print("hello!")
counter = counter + 1
until counter > 7 end
Assim como nos loops, o valor dado por um loop de repetição é o valor da expressão dentro do loop na última iteração, e pode ser usado para quebrar e continuar.
Uma vez que você sempre dá a volta a um loop repetido pelo menos uma vez, você precisa dar uma outra expressão a ele? Sim, talvez você precise. Uma continuação na última iteração de um laço repetido precisa obter um valor de algum lugar, e uma outra expressão é usada para isso.