Em programação de computadores, um loop é uma estrutura de código que faz um loop para executar repetidamente uma parte de um código, frequentemente até que alguma condição seja alcançada. O uso de loops em programação de computadores permite que você automatize e repita tarefas semelhantes várias vezes. Vamos supor que você tivesse uma lista de arquivos que precisasse processar, ou se quisesse contar o número de linhas em um artigo. Você poderia usar um loop em seu código para resolver esses tipos de problemas.
Na linguagem Go, um loop for
implementa a execução repetida de um código baseado em um contador ou uma variável de loop. Ao contrário do que ocorre com outras linguagens de programação que têm vários constructos de looping como o while
, do
etc., o go tem somente o loop for
. Isso serve para tornar seu código mais claro e mais legível, considerando que você não precisa se preocupar com várias estratégias para chegar no mesmo constructo de looping. Essa legibilidade aprimorada e a redução da carga cognitiva durante o desenvolvimento também tornarão o seu código menos propenso a erros do que com outras linguagens.
Neste tutorial, você aprenderá como o loop for
do Go funciona, incluindo as três grandes variações de sua utilização. Vamos começar mostrando como criar diferentes tipos de loops for
, seguido de como fazer o loop através de tipos de dados sequenciais em Go. Vamos terminar explicando como usar os loops aninhados
Para atender a uma variedade de casos de uso, existem três maneiras diferentes de criar loops for
em Go, cada qual com seus próprios recursos. Essas maneiras são para criar um loop for
com uma Condition, uma ForClause, ou uma RangeClause. Nesta seção, explicaremos como declarar e usar as variantes ForClause e Condição.
Vamos ver como podemos usar um loop for
com o ForClause primeiro.
Um ForClause loop é definido como tendo uma instrução inicial, seguida de uma condição e, depois, uma instrução de post. Eles são organizados com a seguinte sintaxe:
for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
[Action]
}
Para explicar o que os componentes anteriores fazem, vejamos um loop for
que incrementa por meio de uma gama especificada de valores usando a sintaxe do ForClause:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Vamos desmembrar esse loop e identificar cada parte.
A primeira parte do loop é i := 0
. Esta é a instrução inicial:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Essa instrução afirma que estamos declarando uma variável chamada i
e definindo o valor inicial para 0
.
Em seguida, temos a condição:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Nessa condição, afirmamos que enquanto o i
for menor do que o valor 5
, o loop deve continuar em looping.
Por fim, temos a instrução post:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Na instrução post, incrementamos a variável do loop i
em um toda vez que uma iteração ocorrer, usando o operador de incremento i++
.
Quando executamos esse programa, o resultado fica parecido com este:
Output0
1
2
3
4
O loop executou 5 vezes. Inicialmente, ele definiu i
para 0
e, depois, verificou se o i
era menor do que 5
. Como o valor de i
era menor que 5
, o loop executou e a ação de fmt.Println(i)
foi executada. Após o loop terminar, o instrução post i++
foi chamada e o valor i
foi incrementado em 1.
Nota: tenha em mente que, em programação, tendemos a começar no índice 0, o que explica por que, a despeito da impressão de 5 números, eles variam de 0 a 4.
Não estamos limitados a iniciar no 0 ou concluir em um valor especificado. Podemos atribuir qualquer valor para nossa instrução inicial e parar em qualquer valor em nossa instrução post. Isso nos permite criar qualquer intervalo desejado para fazer o loop:
for i := 20; i < 25; i++ {
fmt.Println(i)
}
Aqui, a iteração vai de 20 (inclusive) até 25 (exclusive), de modo que o resultado fica parecido com este:
Output20
21
22
23
24
Também podemos usar nossa instrução post para incrementar em diferentes valores. Esse procedimento é parecido com o step
de outras linguagens:
Primeiro, vamos usar uma instrução post com um valor positivo:
for i := 0; i < 15; i += 3 {
fmt.Println(i)
}
Neste caso, o loop for
foi configurado para que os números de 0 a 15 sejam impressos, mas a incrementos de 3, de modo a imprimir apenas um número a cada três, desta forma:
Output0
3
6
9
12
Também podemos usar um valor negativo para que o nosso argumento de instrução post faça a iteração retroativamente. Teremos, porém, que ajustar devidamente nossa instrução inicial e os argumentos de condição:
for i := 100; i > 0; i -= 10 {
fmt.Println(i)
}
Aqui, definimos i
para um valor inicial de 100
, usamos a condição i < 0
para parar em 0
e a instrução post para diminuir o valor em 10 com o operador -=
. O loop começa em 100
e termina em 0
, diminuindo o valor em 10 a cada iteração. Podemos ver isso ocorrer no resultado:
Output100
90
80
70
60
50
40
30
20
10
Também é possível excluir a instrução inicial e a instrução post da sintaxe for
e usar apenas a condição. Trata-se de um loop de Condição:
i := 0
for i < 5 {
fmt.Println(i)
i++
}
Desta vez, declaramos a variável i
separadamente do loop for
na linha anterior do código. O loop tem apenas uma cláusula de condição que verifica se o i
é menor do que 5
. Desde que a condição avalie como true
, o loop continuará a iterar.
Às vezes, você pode não saber o número de iterações que serão necessárias para concluir uma determinada tarefa. Neste caso, é possível omitir todas as instruções e usar a palavra-chave break
para sair da execução:
for {
if someCondition {
break
}
// do action here
}
Como exemplo disso, vamos supor que estivéssemos lendo uma estrutura de tamanho indeterminado como a de um buffer e não soubéssemos quando terminaríamos a leitura:
package main
import (
"bytes"
"fmt"
"io"
)
func main() {
buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n")
for {
line, err := buf.ReadString('\n')
if err != nil {
if err == io.EOF {
fmt.Print(line)
break
}
fmt.Println(err)
break
}
fmt.Print(line)
}
}
No código anterior, o buf :=bytes.NewBufferString("one\ntwo\nthree\nfour\n")
declara um buffer com alguns dados. Como não sabemos quando o buffer irá terminar a leitura, criamos um loop for
sem cláusula. Dentro do loop for
, usamos line, err := buf.ReadString('\n')
para ler uma linha do buffer e verificamos se existe um erro ao ler do buffer. Se houver, lidamos com o erro e usamos a palavra-chave break
para sair do loop for. Com esses pontos de break
, não é necessário incluir uma condição para interromper o loop.
Nesta seção, aprendemos como declarar um loop do ForClause e a usá-lo para iterar por meio de uma gama conhecida de valores. Também aprendemos a usar um loop de condição para iterar até que uma condição específica seja cumprida. Em seguida, vamos aprender como o RangeClause é usado para iterar através de tipos de dados sequenciais.
Na linguagem Go, é comum o uso de loops for
para iterar sobre os elementos de tipos de dados sequenciais ou dados de coleta sequencial ou tipos de dados de coleta, como fatias, matrizes e strings. Para facilitar esse processo, podemos utilizar um loop for
com a sintaxe do RangeClause. Embora você possa fazer loops em tipos de dados sequenciais usando a sintaxe do ForClause, a RangeClause é mais clara e fácil de ler.
Antes de examinarmos o uso da RangeClause, vejamos como podemos iterar por meio de uma fatia, usando a sintaxe do ForClause:
package main
import "fmt"
func main() {
sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
for i := 0; i < len(sharks); i++ {
fmt.Println(sharks[i])
}
}
Executar isso dará o seguinte resultado, imprimindo cada um dos elementos da fatia:
Outputhammerhead
great white
dogfish
frilled
bullhead
requiem
Agora, vamos usar a RangeClause para executar o mesmo conjunto de ações:
package main
import "fmt"
func main() {
sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
for i, shark := range sharks {
fmt.Println(i, shark)
}
}
Neste caso, estamos imprimindo cada item da lista. Embora tenhamos usado as variáveis i
e shark
, poderíamos ter chamado as variáveis por qualquer outro nome de variável válido e, ainda assim, obteríamos o mesmo resultado:
Output0 hammerhead
1 great white
2 dogfish
3 frilled
4 bullhead
5 requiem
Ao usar range
em uma fatia, ele irá sempre retornar dois valores. O primeiro valor será o índice em que a iteração atual do loop está e o segundo será o valor naquele índice. Neste caso, para a primeira iteração, o índice era 0
, e o valor era hammerhead
.
Às vezes, queremos apenas o valor dentro dos elementos da fatia, não do índice. Se alterarmos o código anterior para imprimir apenas o valor, no entanto, vamos receber um erro de tempo de compilação:
package main
import "fmt"
func main() {
sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
for i, shark := range sharks {
fmt.Println(shark)
}
}
Outputsrc/range-error.go:8:6: i declared and not used
Como o i
foi declarado no loop for
, mas nunca foi usado, o compilador responderá com o erro i declared and not used
. Este é o mesmo erro que você receberá no Go sempre que for declarar uma variável e não a utilizar.
Por isso, o Go tem o identificador em branco, que é um sublinhado (_
). Em um loop for
, é possível utilizar o identificador em branco para ignorar qualquer valor retornado da palavra-chave range
. Neste caso, queremos ignorar o índice, que é o primeiro argumento retornado.
package main
import "fmt"
func main() {
sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
for _, shark := range sharks {
fmt.Println(shark)
}
}
Outputhammerhead
great white
dogfish
frilled
bullhead
requiem
Esse resultado mostra que o loop for
iterou por toda a fatia de strings e imprimiu cada item da fatia sem o índice.
Também é possível usar o range
para adicionar itens a uma lista:
package main
import "fmt"
func main() {
sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
for range sharks {
sharks = append(sharks, "shark")
}
fmt.Printf("%q\n", sharks)
}
Output['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']
Aqui, adicionamos uma string com espaço reservado de "shark"
para cada item do comprimento da fatia sharks
.
Note que não precisamos usar o identificador em branco _
para ignorar nenhum dos valores retornados do operador range
. O Go nos permite omitir toda a parte da instrução range
se não precisarmos usar qualquer um do valores retornados.
Também podemos usar o operador range
para preencher valores de uma fatia:
package main
import "fmt"
func main() {
integers := make([]int, 10)
fmt.Println(integers)
for i := range integers {
integers[i] = i
}
fmt.Println(integers)
}
Neste exemplo, a fatia integers
é inicializada com dez valores em branco, mas o loop for
define todos os valores desta forma:
Output[0 0 0 0 0 0 0 0 0 0]
[0 1 2 3 4 5 6 7 8 9]
A primeira vez que imprimimos o valor da fatia integers
, vemos todos os zeros. Então, iteramos pro meio de cada índice e definimos o valor para o índice atual. Em seguida, imprimimos o valor de integers
uma segunda vez, mostrando que todos eles agora têm um valor de 0
a 9
.
Também podemos usar o operador range
para iterar por meio de cada caractere em uma string:
package main
import "fmt"
func main() {
sammy := "Sammy"
for _, letter := range sammy {
fmt.Printf("%c\n", letter)
}
}
OutputS
a
m
m
y
Ao iterar por um mapa, o range
retornará a key (chave) e o** value** (valor):
package main
import "fmt"
func main() {
sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
for key, value := range sammyShark {
fmt.Println(key + ": " + value)
}
}
Outputcolor: blue
location: ocean
name: Sammy
animal: shark
Nota: é importante notar que a ordem na qual um mapa retorna é aleatória. Cada vez que você executa esse programa, você pode obter um resultado diferente.
Agora que aprendemos como iterar em dados sequenciais com loops for
do tipo range
, vamos ver como usar loops dentro de loops.
Assim como acontece em outras linguagens de programação, os loops também podem ser aninhados em Go. Nesting (aninhamento) é quando temos um constructo dentro de outro. Neste caso, um loop aninhado é um loop que ocorre dentro de outro loop. Eles podem ser úteis quando o que se quer é uma ação em loop sendo realizada em cada elemento de um conjunto de dados.
Os loops aninhados são estruturalmente semelhantes às instruções aninhadas if
. Eles são construídos desta forma:
for {
[Action]
for {
[Action]
}
}
Primeiro, o programa encontra o loop externo, executando sua primeira iteração. Essa primeira iteração aciona o loop interno, o loop aninhado, o qual é executado até sua finalização. Em seguida, o programa retorna para o topo do loop externo, finalizando a segunda iteração e acionando novamente o loop aninhado. Novamente, o loop aninhado executa até sua finalização e o programa retorna para o topo do loop externo até a sequência estar completa ou que uma instrução break ou outra interrompa o processo.
Vamos implementar um loop for
aninhado para analisarmos melhor. Neste exemplo, o loop externo irá iterar por uma fatia de inteiros chamada de numList
e o loop interno irá iterar por uma fatia de strings chamada alphaList
.
package main
import "fmt"
func main() {
numList := []int{1, 2, 3}
alphaList := []string{"a", "b", "c"}
for _, i := range numList {
fmt.Println(i)
for _, letter := range alphaList {
fmt.Println(letter)
}
}
}
Ao executarmos esse programa, vamos receber o seguinte resultado:
Output1
a
b
c
2
a
b
c
3
a
b
c
O resultado ilustra que o programa termina a primeira iteração do loop externo imprimindo 1
, que, por sua vez, aciona a conclusão do loop interno, imprimindo a
, b
e c
, consecutivamente. Assim que o loop interno for finalizado, o programa retorna para o topo do loop externo, imprime o número 2
, e imprime novamente o loop interno em sua totalidade (a
, b
, c
) etc.
Os loops for
aninhados podem ser úteis para iterar através de itens dentro de fatias compostas por fatias. Em uma fatia composta por fatias, se usarmos apenas um loop for
, o programa dará como resultado cada lista interna como um item:
package main
import "fmt"
func main() {
ints := [][]int{
[]int{0, 1, 2},
[]int{-1, -2, -3},
[]int{9, 8, 7},
}
for _, i := range ints {
fmt.Println(i)
}
}
Output[0 1 2]
[-1 -2 -3]
[9 8 7]
Para acessar cada item individual das fatias internas, implementaremos um loop for
aninhado:
package main
import "fmt"
func main() {
ints := [][]int{
[]int{0, 1, 2},
[]int{-1, -2, -3},
[]int{9, 8, 7},
}
for _, i := range ints {
for _, j := range i {
fmt.Println(j)
}
}
}
Output0
1
2
-1
-2
-3
9
8
7
Ao usarmos um loop for
aninhado aqui, podemos iterar com os itens individuais contidos nas fatias.
Neste tutorial, aprendemos a declarar e usar loops for
para resolver tarefas repetitivas no Go. Também aprendemos as três diferentes variações de um loop for
e quando usá-las. Para aprender mais sobre os loops for
e como controlar o fluxo deles, leia o artigo Usando as instruções break e continue ao trabalhar com loops em Go.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!