Funções
Funções
Funções são como "máquinas de venda automática" no dia a dia — você insere parâmetros, elas processam de acordo com regras predefinidas e retornam resultados. Em Go, funções são cidadãos de primeira classe: podem ser atribuídas a variáveis, passadas como argumentos, retornadas como valores, e podem até ser sem nome. Dominar funções é um passo-chave para escrever código Go elegante.
1. Conceitos Fundamentais
| Conceito | Descrição |
|---|---|
Definição func |
Use a palavra-chave func para declarar funções, suportando parâmetros e tipos de valor de retorno |
| Múltiplos valores de retorno | Funções Go podem retornar múltiplos valores, comumente usado para resultado + erro |
| Valores de retorno nomeados | Valores de retorno podem ser nomeados, usados diretamente no corpo da função, e retornados automaticamente com return |
| Parâmetros variádicos | Use ...Type para aceitar qualquer número de argumentos |
| Funções anônimas | Funções sem nomes, frequentemente usadas para invocação imediata ou atribuição a variáveis |
| Closures | Funções anônimas que capturam variáveis externas, formando closures |
Função init |
Cada pacote pode ter múltiplas funções init que executam automaticamente na inicialização do programa |
defer |
Execução adiada, executa em ordem de pilha antes da função retornar |
2. Sintaxe/Uso Básico
Definição de Função
// Definição básica de função
func nomeFuncao(listaParametros) tipoRetorno {
// Corpo da função
return valor
}
// Sem parâmetros, sem valor de retorno
func dizerOla() {
fmt.Println("Olá!")
}
// Com parâmetros e valor de retorno
func somar(a int, b int) int {
return a + b
}
// Abreviação quando os tipos dos parâmetros são iguais
func somar(a, b int) int {
return a + b
}
Múltiplos Valores de Retorno
// Retornar dois valores: resultado e erro
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisor não pode ser zero")
}
return a / b, nil
}
// Receber ambos os valores de retorno ao chamar
resultado, err := dividir(10, 3)
Valores de Retorno Nomeados
// Valores de retorno nomeados: use nomes de variáveis diretamente no corpo da função
func infoRetangulo(comprimento, largura float64) (area, perimetro float64) {
area = comprimento * largura
perimetro = 2 * (comprimento + largura)
return // Retorna automaticamente os valores de retorno nomeados
}
Parâmetros Variádicos
// Parâmetros variádicos: use ...Type para aceitar qualquer número de argumentos
func soma(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// Chamando
soma(1, 2, 3) // 6
soma(1, 2, 3, 4, 5) // 15
Funções Anônimas e Closures
// Atribuir função anônima a variável
saudacao := func(nome string) string {
return "Olá, " + nome
}
// Função invocada imediatamente
func() {
fmt.Println("Executado imediatamente")
}()
// Closure: captura variável externa
func contador() func() int {
contagem := 0
return func() int {
contagem++ // Captura a variável externa contagem
return contagem
}
}
Função init
// Função init: executa automaticamente na inicialização do programa, sem necessidade de chamada manual
// Cada pacote pode ter múltiplas funções init
func init() {
fmt.Println("função init executada")
}
defer
// defer: execução adiada, executa em ordem LIFO (último a entrar, primeiro a sair) antes da função retornar
func lerArquivo(nomeArquivo string) {
f, _ := os.Open(nomeArquivo)
defer f.Close() // Fechar arquivo antes da função terminar
// ... ler arquivo
}
defer são avaliados quando declarados, não quando executados. Esta é uma armadilha comum.
init não podem ser chamadas por outras funções; executam automaticamente antes de main. Se houver múltiplas funções init, executam em ordem alfabética do arquivo fonte.
3. Sintaxe/Uso Básico (Exemplos)
Exemplo 1: Uso Básico (Dificuldade ⭐)
Objetivo: Aprender definição básica de função, chamada e múltiplos valores de retorno.
package main
import (
"errors"
"fmt"
)
// saudacao aceita um nome e retorna uma saudação
func saudacao(nome string) string {
return "Olá, " + nome + "!"
}
// somar retorna a soma de dois inteiros
func somar(a, b int) int {
return a + b
}
// trocar troca duas strings (demonstra múltiplos valores de retorno)
func trocar(a, b string) (string, string) {
return b, a
}
// dividir demonstra tratamento de erros
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("divisor não pode ser zero")
}
return a / b, nil
}
func main() {
// Chamar funções de valor de retorno único
fmt.Println(saudacao("Alice")) // Saída: Olá, Alice!
fmt.Println(somar(3, 5)) // Saída: 8
// Chamar função de múltiplos valores de retorno
x, y := trocar("olá", "mundo")
fmt.Println(x, y) // Saída: mundo olá
// Chamar função com tratamento de erros
resultado, err := dividir(10, 3)
if err != nil {
fmt.Println("Erro:", err)
} else {
fmt.Printf("10 / 3 = %.2f\n", resultado) // Saída: 10 / 3 = 3.33
}
// Testar erro de divisão por zero
_, err = dividir(10, 0)
if err != nil {
fmt.Println("Erro:", err) // Saída: Erro: divisor não pode ser zero
}
}
Saída:
Olá, Alice!
8
mundo olá
10 / 3 = 3.33
Erro: divisor não pode ser zero
Pontos-Chave:
- Use a palavra-chave
funcpara definir funções; tipos de parâmetros vêm após os nomes dos parâmetros - Múltiplos valores de retorno são separados por vírgulas; use múltiplas variáveis para recebê-los ao chamar
- Tratamento de erros é um padrão central do Go: retorne
(resultado, erro)
Exemplo 2: Uso Intermediário (Dificuldade ⭐⭐)
Objetivo: Aprender valores de retorno nomeados, parâmetros variádicos, funções anônimas e closures.
package main
import "fmt"
// ============ Valores de Retorno Nomeados ============
// retangulo calcula área e perímetro de um retângulo (usando valores de retorno nomeados)
func retangulo(comprimento, largura float64) (area, perimetro float64) {
area = comprimento * largura // Use variáveis nomeadas diretamente
perimetro = 2 * (comprimento + largura)
return // Retorna automaticamente valores de retorno nomeados, sem precisar escrever return area, perimetro
}
// ============ Parâmetros Variádicos ============
// soma calcula a soma de qualquer número de inteiros
func soma(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
// media calcula a média, demonstrando mistura de variádicos com outros parâmetros
func media(primeiro float64, resto ...float64) float64 {
total := primeiro
contagem := 1.0
for _, v := range resto {
total += v
contagem++
}
return total / contagem
}
// ============ Funções Anônimas ============
// aplicar aplica uma função a um inteiro e retorna o resultado
func aplicar(n int, f func(int) int) int {
return f(n)
}
func main() {
// Valores de retorno nomeados
area, perimetro := retangulo(5, 3)
fmt.Printf("Área: %.1f, Perímetro: %.1f\n", area, perimetro)
// Saída: Área: 15.0, Perímetro: 16.0
// Parâmetros variádicos
fmt.Println("soma(1,2,3):", soma(1, 2, 3)) // 6
fmt.Println("soma(1,2,3,4,5):", soma(1, 2, 3, 4, 5)) // 15
// Parâmetros variádicos: passando um slice
nums := []int{10, 20, 30}
fmt.Println("soma(nums...):", soma(nums...)) // 60 (use ... para expandir o slice)
// Misturando variádicos com outros parâmetros
fmt.Printf("média: %.2f\n", media(10, 20, 30)) // 20.00
// Funções anônimas como argumentos
dobrar := func(n int) int { return n * 2 }
quadrado := func(n int) int { return n * n }
fmt.Println("aplicar(5, dobrar):", aplicar(5, dobrar)) // 10
fmt.Println("aplicar(5, quadrado):", aplicar(5, quadrado)) // 25
// Invocar função anônima imediatamente
msg := "Olá"
func(m string) {
fmt.Println(m)
}(msg)
// ============ Closures ============
// contador retorna um closure contador
contador := func() func() int {
contagem := 0
return func() int {
contagem++ // Captura variável externa contagem
return contagem
}
}()
fmt.Println("contador:", contador()) // 1
fmt.Println("contador:", contador()) // 2
fmt.Println("contador:", contador()) // 3
// Closure implementando um acumulador
acumulador := func() func(int) int {
soma := 0
return func(n int) int {
soma += n
return soma
}
}()
fmt.Println("acumulador(10):", acumulador(10)) // 10
fmt.Println("acumulador(20):", acumulador(20)) // 30
fmt.Println("acumulador(30):", acumulador(30)) // 60
}
Saída:
Área: 15.0, Perímetro: 16.0
soma(1,2,3): 6
soma(1,2,3,4,5): 15
soma(nums...): 60
média: 20.00
aplicar(5, dobrar): 10
aplicar(5, quadrado): 25
Olá
contador: 1
contador: 2
contador: 3
acumulador(10): 10
acumulador(20): 30
acumulador(30): 60
Pontos-Chave:
- Valores de retorno nomeados tornam o código mais claro, mas não os use em excesso pois pode reduzir a legibilidade
- Parâmetros variádicos
...Typedevem ser o último parâmetro da função - Ao passar um slice para parâmetros variádicos, use
slice...para expandi-lo - Closures "lembram" o estado das variáveis externas; cada chamada compartilha a mesma variável
Exemplo 3: Aplicação Abrangente (Dificuldade ⭐⭐⭐)
Objetivo: Padrões de design de funções do mundo real: padrão de opções funcionais, cadeia de middleware, defer com tratamento de erros.
package main
import (
"fmt"
"strings"
"time"
)
// ============ Padrão de Opções Funcionais ============
// Configuração do servidor
type Server struct {
host string
port int
timeout time.Duration
maxConn int
}
// Option é um tipo de função para opções de configuração
type Option func(*Server)
// WithHost define o endereço do host
func WithHost(host string) Option {
return func(s *Server) {
s.host = host
}
}
// WithPort define a porta
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
// WithTimeout define a duração do timeout
func WithTimeout(timeout time.Duration) Option {
return func(s *Server) {
s.timeout = timeout
}
}
// WithMaxConn define o máximo de conexões
func WithMaxConn(max int) Option {
return func(s *Server) {
s.maxConn = max
}
}
// NewServer cria um servidor usando o padrão de opções
func NewServer(opts ...Option) *Server {
// Configuração padrão
s := &Server{
host: "localhost",
port: 8080,
timeout: 30 * time.Second,
maxConn: 100,
}
// Aplicar todas as opções
for _, opt := range opts {
opt(s)
}
return s
}
// String implementa a interface Stringer
func (s *Server) String() string {
return fmt.Sprintf("Server{host:%s, port:%d, timeout:%v, maxConn:%d}",
s.host, s.port, s.timeout, s.maxConn)
}
// ============ Padrão de Cadeia de Middleware ============
// Handler é um tipo de função de processamento
type Handler func(string) string
// Middleware é um tipo de middleware
type Middleware func(Handler) Handler
// loggingMiddleware registra entrada e saída
func loggingMiddleware(next Handler) Handler {
return func(input string) string {
fmt.Printf("[Log] Entrada: %s\n", input)
result := next(input)
fmt.Printf("[Log] Saída: %s\n", result)
return result
}
}
// upperMiddleware converte entrada para maiúsculas
func upperMiddleware(next Handler) Handler {
return func(input string) string {
return next(strings.ToUpper(input))
}
}
// wrapMiddleware envolve saída com colchetes
func wrapMiddleware(next Handler) Handler {
return func(input string) string {
return "【" + next(input) + "】"
}
}
// chain combina múltiplos middlewares em uma cadeia
func chain(handler Handler, middlewares ...Middleware) Handler {
// Envolver de trás para frente, garantindo que o primeiro middleware execute primeiro
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
// ============ defer na Prática: Limpeza de Recursos e Recuperação de Erros ============
// simulateDB simula operações de banco de dados
func simulateDB() (err error) {
fmt.Println("1. Adquirindo conexão com banco de dados")
// defer executa em ordem LIFO
defer fmt.Println("5. Liberando conexão com banco de dados (executa por último)")
defer fmt.Println("4. Escrevendo log de operação")
defer fmt.Println("3. Fechando cursor")
fmt.Println("2. Executando consulta")
// Simular recuperação de panic
defer func() {
if r := recover(); r != nil {
fmt.Printf(" [Recuperação] Panic capturado: %v\n", r)
err = fmt.Errorf("operação falhou: %v", r)
}
}()
// Simular um panic
panic("timeout de conexão")
// Código abaixo não será executado
// fmt.Println("Consulta completa")
// return nil
}
func main() {
// ===== Padrão de Opções Funcionais =====
fmt.Println("=== Padrão de Opções Funcionais ===")
// Usando configuração padrão
s1 := NewServer()
fmt.Println(s1)
// Configuração personalizada
s2 := NewServer(
WithHost("0.0.0.0"),
WithPort(9090),
WithTimeout(60*time.Second),
WithMaxConn(500),
)
fmt.Println(s2)
// ===== Cadeia de Middleware =====
fmt.Println("\n=== Cadeia de Middleware ===")
// Handler base
handler := func(s string) string {
return "Processado: " + s
}
// Combinar middleware
final := chain(handler, loggingMiddleware, upperMiddleware, wrapMiddleware)
result := final("olá mundo")
fmt.Println("Resultado final:", result)
// ===== defer e Recuperação de Erros =====
fmt.Println("\n=== Ordem de Execução do defer ===")
err := simulateDB()
if err != nil {
fmt.Println("Função principal capturou erro:", err)
}
}
Saída:
=== Padrão de Opções Funcionais ===
Server{host:localhost, port:8080, timeout:30s, maxConn:100}
Server{host:0.0.0.0, port:9090, timeout:1m0s, maxConn:500}
=== Cadeia de Middleware ===
[Log] Entrada: OLÁ MUNDO
[Log] Saída: 【Processado: OLÁ MUNDO】
Resultado final: 【Processado: OLÁ MUNDO】
=== Ordem de Execução do defer ===
1. Adquirindo conexão com banco de dados
2. Executando consulta
[Recuperação] Panic capturado: timeout de conexão
3. Fechando cursor
4. Escrevendo log de operação
5. Liberando conexão com banco de dados (executa por último)
Função principal capturou erro: operação falhou: timeout de conexão
Pontos-Chave:
- Padrão de opções funcionais: Alcança configuração flexível através de closures retornados; padrão de design amplamente utilizado na comunidade Go
- Cadeia de middleware: Funções como cidadãs de primeira classe podem ser combinadas e passadas para formar pipelines de processamento
- Ordem de execução do defer: LIFO (último a entrar, primeiro a sair); o primeiro defer declarado executa por último
- panic/recover:
defer+recoverpode capturarpanice prevenir travamentos do programa
3. Casos de Uso Comuns
Caso 1: Encapsulamento de Tratamento de Erros
Em projetos reais, você frequentemente precisa encapsular lógica unificada de tratamento de erros:
package main
import (
"fmt"
"strconv"
)
// Result é um wrapper de resultado unificado
type Result struct {
Data interface{}
Err error
}
// parseJSON simula análise de JSON, retorna resultado e erro
func parseJSON(jsonStr string) (map[string]interface{}, error) {
// Demo simplificado: use encoding/json em projetos reais
if jsonStr == "" {
return nil, fmt.Errorf("string JSON não pode ser vazia")
}
return map[string]interface{}{"key": "value"}, nil
}
// parseInt converte com segurança uma string para inteiro
func parseInt(s string) (int, error) {
n, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("falha ao analisar inteiro '%s': %w", s, err)
}
return n, nil
}
// withRetry tenta novamente uma função
func withRetry(tentativas int, f func() error) error {
var err error
for i := 0; i < tentativas; i++ {
err = f()
if err == nil {
return nil
}
fmt.Printf(" Tentativa %d falhou: %v\n", i+1, err)
}
return fmt.Errorf("falhou após %d tentativas: %w", tentativas, err)
}
func main() {
// Tratamento de erros
resultado, err := parseJSON(`{"nome": "teste"}`)
if err != nil {
fmt.Println("Erro de análise:", err)
return
}
fmt.Println("Resultado da análise:", resultado)
// Conversão de tipo segura
num, err := parseInt("123")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Resultado da conversão:", num)
// Mecanismo de tentativa
tentativa := 0
err = withRetry(3, func() error {
tentativa++
if tentativa < 3 {
return fmt.Errorf("falha simulada")
}
return nil
})
if err != nil {
fmt.Println("Falha final:", err)
} else {
fmt.Println("Sucesso final!")
}
}
Caso 2: Funções como Pipelines de Processamento de Dados
package main
import (
"fmt"
"strings"
)
// Transform é um tipo de função de transformação de dados
type Transform func(string) string
// Pipeline é um pipeline de processamento de dados
type Pipeline struct {
etapas []Transform
}
// NewPipeline cria um novo pipeline
func NewPipeline() *Pipeline {
return &Pipeline{}
}
// Add adiciona etapas de processamento
func (p *Pipeline) Add(etapas ...Transform) *Pipeline {
p.etapas = append(p.etapas, etapas...)
return p
}
// Execute roda o pipeline
func (p *Pipeline) Execute(entrada string) string {
resultado := entrada
for _, etapa := range p.etapas {
resultado = etapa(resultado)
}
return resultado
}
func main() {
// Definir funções de transformação
trim := func(s string) string { return strings.TrimSpace(s) }
lower := func(s string) string { return strings.ToLower(s) }
replace := func(s string) string { return strings.ReplaceAll(s, " ", "-") }
prefix := func(s string) string { return "processado-" + s }
// Construir pipeline de processamento
pipeline := NewPipeline().
Add(trim, lower, replace, prefix)
// Executar pipeline
resultado := pipeline.Execute(" Olá Mundo Go ")
fmt.Println(resultado) // Saída: processado-olá-mundo-go
// Reutilizar pipeline para múltiplas entradas
entradas := []string{" Foo Bar ", " OLÁ ", " Go Lang "}
for _, entrada := range entradas {
fmt.Printf("%-20s => %s\n", entrada, pipeline.Execute(entrada))
}
}
❓ Perguntas Frequentes
P1: Os parâmetros de função são passados por valor ou por referência?
R: Em Go, todos os parâmetros de função são passados por valor. No entanto, slices, maps, channels, ponteiros e tipos semelhantes passam uma "cópia da referência" — modificar os dados para os quais apontam afeta os dados originais, mas reatribuí-los não afeta a variável original.
package main
import "fmt"
// modifySlice modifica o conteúdo do slice (afeta o slice original)
func modifySlice(s []int) {
s[0] = 999 // Modificar elemento afeta o slice original
fmt.Println("Dentro da função:", s)
}
// reassignSlice reatribui o slice (não afeta o slice original)
func reassignSlice(s []int) {
s = append(s, 4, 5, 6) // append pode criar um novo array subjacente
fmt.Println("Dentro da função:", s)
}
func main() {
nums := []int{1, 2, 3}
modifySlice(nums)
fmt.Println("Fora da função:", nums) // [999 2 3], slice original modificado
reassignSlice(nums)
fmt.Println("Fora da função:", nums) // [999 2 3], slice original inalterado
}
P2: Quando os argumentos do defer são avaliados?
R: Os argumentos das instruções defer são avaliados quando declarados, não quando executados.
package main
import "fmt"
func main() {
x := 10
// Argumentos do defer avaliados no momento da declaração, x = 10
defer fmt.Println("x no defer =", x)
x = 20
fmt.Println("x após modificação =", x)
// Se precisar de avaliação no momento de execução do defer, use um closure
defer func() {
fmt.Println("x no closure defer =", x) // x = 20
}()
x = 30
}
Saída:
x após modificação = 20
x no closure defer = 30
x no defer = 10
P3: Um pacote pode ter múltiplas funções init? Qual a ordem de execução?
R: Sim. Um pacote (ou até um único arquivo) pode ter múltiplas funções init. Ordem de execução:
- Primeiro, execute as funções
initdos pacotes importados - Dentro do mesmo pacote, execute em ordem alfabética do arquivo fonte
- Dentro do mesmo arquivo, execute na ordem em que as funções
initaparecem
package main
import "fmt"
func init() {
fmt.Println("Primeiro init")
}
func init() {
fmt.Println("Segundo init")
}
func main() {
fmt.Println("função main")
}
Saída:
Primeiro init
Segundo init
função main
P4: Como implementar parâmetros opcionais?
R: Go não suporta diretamente parâmetros opcionais. Os três métodos seguintes são comumente usados:
package main
import "fmt"
// Método 1: Usando struct
type Config struct {
Host string
Port int
Debug bool
}
func connect(cfg Config) {
fmt.Printf("Conectando a %s:%d (debug=%v)\n", cfg.Host, cfg.Port, cfg.Debug)
}
// Método 2: Usando padrão de opções funcionais (recomendado)
type Option func(*Config)
func WithHost(host string) Option {
return func(c *Config) { c.Host = host }
}
func WithPort(port int) Option {
return func(c *Config) { c.Port = port }
}
func WithDebug(debug bool) Option {
return func(c *Config) { c.Debug = debug }
}
func newConnect(opts ...Option) {
cfg := &Config{Host: "localhost", Port: 8080, Debug: false}
for _, opt := range opts {
opt(cfg)
}
fmt.Printf("Conectando a %s:%d (debug=%v)\n", cfg.Host, cfg.Port, cfg.Debug)
}
func main() {
// Método struct
connect(Config{Host: "127.0.0.1", Port: 3306})
// Padrão de opções
newConnect()
newConnect(WithHost("127.0.0.1"), WithPort(3306), WithDebug(true))
}
📖 Resumo
- Definição de função: Use a palavra-chave
func; tipos de parâmetros vêm após os nomes dos parâmetros; suporta múltiplos valores de retorno - Valores de retorno nomeados: Valores de retorno podem ser nomeados, usados diretamente no corpo da função, e retornados automaticamente com
return - Parâmetros variádicos:
...Typeaceita qualquer número de argumentos; deve ser o último parâmetro - Funções anônimas: Funções sem nomes; podem ser atribuídas a variáveis ou invocadas imediatamente
- Closures: Funções anônimas que capturam variáveis externas; o tempo de vida das variáveis é estendido
- Função init: Executa automaticamente na inicialização do programa; não pode ser chamada manualmente; adequada para trabalho de inicialização
- defer: Execução adiada em ordem LIFO; argumentos são avaliados no momento da declaração
- Funções são cidadãs de primeira classe: Podem ser atribuídas, passadas e retornadas, permitindo padrões de design flexíveis
📝 Exercícios
Exercício 1 (⭐): Prática Básica de Funções
Requisitos: Escreva e teste as seguintes funções:
max(a, b int) int— Retorna o maior de dois inteirosehPar(n int) bool— Verifica se um inteiro é partrocar(a, b *int)— Troca dois inteiros usando ponteiros
// Estrutura de referência
package main
import "fmt"
func max(a, b int) int {
// Implemente aqui
return 0
}
func ehPar(n int) bool {
// Implemente aqui
return false
}
func trocar(a, b *int) {
// Implemente aqui (dica: use ponteiros)
}
func main() {
fmt.Println(max(3, 5)) // Deve imprimir: 5
fmt.Println(ehPar(4)) // Deve imprimir: true
fmt.Println(ehPar(7)) // Deve imprimir: false
x, y := 10, 20
trocar(&x, &y)
fmt.Println(x, y) // Deve imprimir: 20 10
}
Exercício 2 (⭐⭐): Parâmetros Variádicos e Closures
Requisitos:
- Escreva
filtrar(nums []int, predicado func(int) bool) []int— Filtra elementos em um slice que satisfazem uma condição - Escreva
criarMultiplicador(fator int) func(int) int— Retorna um closure de multiplicação - Use
filtrare um closure para selecionar todos os números pares de um slice
// Estrutura de referência
package main
import "fmt"
func filtrar(nums []int, predicado func(int) bool) []int {
// Implemente: itere nums, adicione elementos que satisfazem o predicado ao slice resultado
return nil
}
func criarMultiplicador(fator int) func(int) int {
// Implemente: retorne um closure que multiplica a entrada pelo fator
return nil
}
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// Filtrar números pares
pares := filtrar(nums, func(n int) bool {
return n%2 == 0
})
fmt.Println("Pares:", pares) // [2 4 6 8 10]
// Filtrar números maiores que 5
grandes := filtrar(nums, func(n int) bool {
return n > 5
})
fmt.Println("Maiores que 5:", grandes) // [6 7 8 9 10]
// Usar closures para criar multiplicadores
dobrar := criarMultiplicador(2)
triplicar := criarMultiplicador(3)
fmt.Println("dobrar(5):", dobrar(5)) // 10
fmt.Println("triplicar(5):", triplicar(5)) // 15
}
Exercício 3 (⭐⭐⭐): Projeto Prático — Calculadora com Padrão de Comando
Requisitos: Implemente um sistema de calculadora que suporte registro de comandos:
- Defina uma struct
Calculatorcom um mapa de comandosmap[string]func([]int) int - Implemente
Register(nome string, op func([]int) int)para registrar comandos - Implemente
Execute(nome string, args []int) (int, error)para executar comandos - Registre os seguintes comandos:
add(soma),mul(produto),max(máximo) - Use a função
initpara registrar automaticamente todos os comandos
// Estrutura de referência
package main
import (
"errors"
"fmt"
)
type Calculator struct {
// Defina campos aqui
}
func NewCalculator() *Calculator {
// Implemente aqui
return nil
}
func (c *Calculator) Register(nome string, op func([]int) int) {
// Implemente aqui
}
func (c *Calculator) Execute(nome string, args []int) (int, error) {
// Implemente: encontre comando e execute; retorne erro se não encontrado
return 0, nil
}
func (c *Calculator) ListCommands() []string {
// Implemente: retorne todos os nomes de comandos registrados
return nil
}
func main() {
calc := NewCalculator()
// Registrar comandos
calc.Register("add", func(args []int) int {
soma := 0
for _, v := range args {
soma += v
}
return soma
})
calc.Register("mul", func(args []int) int {
produto := 1
for _, v := range args {
produto *= v
}
return produto
})
calc.Register("max", func(args []int) int {
if len(args) == 0 {
return 0
}
m := args[0]
for _, v := range args[1:] {
if v > m {
m = v
}
}
return m
})
// Testar
testes := []struct {
cmd string
args []int
}{
{"add", []int{1, 2, 3, 4, 5}},
{"mul", []int{2, 3, 4}},
{"max", []int{10, 3, 7, 9, 1}},
}
for _, t := range testes {
resultado, err := calc.Execute(t.cmd, t.args)
if err != nil {
fmt.Printf("Erro: %v\n", err)
} else {
fmt.Printf("%s(%v) = %d\n", t.cmd, t.args, resultado)
}
}
// Listar todos os comandos
fmt.Println("Comandos disponíveis:", calc.ListCommands())
// Testar comando inexistente
_, err := calc.Execute("sqrt", []int{16})
if err != nil {
fmt.Println("Erro:", err)
}
}
Saída Esperada:
add([1 2 3 4 5]) = 15
mul([2 3 4]) = 24
max([10 3 7 9 1]) = 10
Comandos disponíveis: [add mul max]
Erro: comando desconhecido: sqrt



