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

GO
// 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

GO
// 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

GO
// 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

GO
// 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

GO
// 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

GO
// 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

GO
// 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
}
💡 Dica: Em Go, todos os argumentos de função são passados por valor. Slices, maps, channels, ponteiros, etc. passam uma cópia da referência, mas os dados para os quais apontam são compartilhados.

💡 Dica: Os argumentos das instruções defer são avaliados quando declarados, não quando executados. Esta é uma armadilha comum.

💡 Dica: Funções 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.

GO
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
    }
}
▶ Experimente

Saída:

TEXT
Olá, Alice!
8
mundo olá
10 / 3 = 3.33
Erro: divisor não pode ser zero

Pontos-Chave:


Exemplo 2: Uso Intermediário (Dificuldade ⭐⭐)

Objetivo: Aprender valores de retorno nomeados, parâmetros variádicos, funções anônimas e closures.

GO
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
}
▶ Experimente

Saída:

TEXT
Á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:


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.

GO
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)
    }
}
▶ Experimente

Saída:

TEXT
=== 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:


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:

GO
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

GO
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.

GO
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.

GO
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:

TEXT
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:

  1. Primeiro, execute as funções init dos pacotes importados
  2. Dentro do mesmo pacote, execute em ordem alfabética do arquivo fonte
  3. Dentro do mesmo arquivo, execute na ordem em que as funções init aparecem
GO
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:

TEXT
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:

GO
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


📝 Exercícios

Exercício 1 (⭐): Prática Básica de Funções

Requisitos: Escreva e teste as seguintes funções:

  1. max(a, b int) int — Retorna o maior de dois inteiros
  2. ehPar(n int) bool — Verifica se um inteiro é par
  3. trocar(a, b *int) — Troca dois inteiros usando ponteiros
GO
// 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:

  1. Escreva filtrar(nums []int, predicado func(int) bool) []int — Filtra elementos em um slice que satisfazem uma condição
  2. Escreva criarMultiplicador(fator int) func(int) int — Retorna um closure de multiplicação
  3. Use filtrar e um closure para selecionar todos os números pares de um slice
GO
// 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:

  1. Defina uma struct Calculator com um mapa de comandos map[string]func([]int) int
  2. Implemente Register(nome string, op func([]int) int) para registrar comandos
  3. Implemente Execute(nome string, args []int) (int, error) para executar comandos
  4. Registre os seguintes comandos: add (soma), mul (produto), max (máximo)
  5. Use a função init para registrar automaticamente todos os comandos
GO
// 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:

TEXT
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

Próxima Lição

Próxima Lição: Arrays e Slices →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%