Percursos
/
jq
jq
/
Exercícios
/
Carro telecomandado
Carro telecomandado

Carro telecomandado

Exercício de aprendizagem

Introdução

Funções

Podes definir as tuas próprias funções personalizadas em jq para encapsular a lógica de que precisares. As funções funcionam exatamente como as funções nativas: recebem um valor de entrada e emitem zero, uma ou mais saídas.

Definir uma função

Podes definir uma função de jq com a seguinte sintaxe:

# no arguments
def funcname: expression;

# or with arguments
def funcname(args): expression;
  • começa com a palavra-chave def,
  • dois pontos antes do corpo da função,
  • o corpo é constituído por uma única expressão,
  • termina com um ponto e vírgula,
  • tal como no resto da sintaxe do jq, podes usar espaços em branco à vontade para facilitar a leitura.

Onde colocar as funções

As funções têm de ser definidas antes de serem usadas: isto dá erro:

def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined

Isto implica que tens de colocar as funções no início do teu código jq, antes da expressão «principal».

Funções aninhadas

As funções podem ser aninhadas:

def A:
    def B(n): n + 1;
    B(10)
    ;
A
# => 11

Aqui, a função B só é visível no corpo de A.

Âmbito

Uma função introduz um novo âmbito para variáveis e funções aninhadas.

Argumentos

Os argumentos de uma função são separados por pontos e vírgulas, não por vírgulas. Por exemplo, uma função que recebe um número e depois soma um número e multiplica por um número:

def add_mul(adder; multiplier): (. + adder) * multiplier;

10 | add_mul(5; 4)    # => 60
Note

Os pontos e vírgulas são necessários porque a vírgula já tem uma função no jq: é um operador que junta fluxos.

Usar uma vírgula em vez de um ponto e vírgula tenta fazer duas chamadas a uma função add_mul de 1 argumento, que não existe e, por isso, falha logo na primeira tentativa de chamada:

10 | add_mul(5, 4)
# error: add_mul/1 is not defined
Os argumentos são expressões

Os argumentos de uma função são filtros, não valores. Neste sentido, comportam-se como aquilo a que outras linguagens chamam callbacks:

Usando a função add_mul como exemplo:

10 | add_mul(. + 5; . - 2)    # => 200

O que está aqui a acontecer?

  • o argumento adder recebe a expressão . + 5
    • quando a função faz . + adder, isso passa a ser . + . + 5
    • isso dá 25, uma vez que . == 10
  • de forma semelhante, o argumento multiplier é a expressão . - 2
    • isso dá 8
    • e o resultado é 25 * 8 == 200
Argumentos como valores

Às vezes vais querer «materializar» um argumento numa variável:

def my_func(arg):
    arg as $arg
    | other stuff ...
;

Há uma forma abreviada de fazer isto:

def my_func($arg):
    other stuff ...
;

Repara que isto é apenas «açúcar sintático»: o nome arg sem $ continua dentro do âmbito da função.

Aridade

As funções têm uma aridade: o número de argumentos que recebem.

As funções podem usar o mesmo nome com aridades diferentes. A função nativa range demonstra isto: range/1, range/2 e range/3 coexistem.

Isto pode ser útil para definir funções recursivas que transportam estado através dos argumentos. Por exemplo, map poderia ser implementado assim:

def my_map($accumulator; func):
    if length == 0
        then $accumulator
        else first as $elem | .[1:] | my_map($accumulator + [$elem | func]; func)
    end
    ;

def my_map(func):
    my_map([]; func)
    ;

[1, 2, 3, 4] | my_map(. * 10)   # => [10, 20, 30, 40]

Recursão

O jq faz otimização de chamadas em cauda, mas apenas para funções de aridade 0.

Módulos

Um módulo de jq é um ficheiro que contém apenas funções. Os módulos são incluídos num programa jq com os comandos include ou import.

Instruções

Neste exercício vais brincar com um carro telecomandado, que finalmente conseguiste comprar depois de poupares dinheiro suficiente.

Os carros começam com a bateria cheia (100%). Cada vez que conduzes o carro com o telecomando, este percorre 20 metros e gasta um por cento da bateria. A alcunha do carro só é conhecida quando este é criado.

O carro telecomandado tem um ecrã de LED sofisticado que mostra duas informações:

  • A distância total percorrida, apresentada como: "<METERS> meters".
  • A carga restante da bateria, apresentada como: "Battery at <PERCENTAGE>%".

Se a bateria estiver a 0%, já não consegues conduzir o carro e o ecrã da bateria mostra "Battery empty".

1. Cria um carro telecomandado novo

Implementa a função new_remote_control_car/0 para devolver um objeto de carro telecomandado novo:

new_remote_control_car
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": null
#    }

2. Cria um carro telecomandado novo com uma alcunha

Implementa a função new_remote_control_car/1 para devolver um objeto de carro telecomandado novo com uma alcunha fornecida:

new_remote_control_car("Blue")
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": "Blue"
#    }

3. Mostra a distância

Implementa a função display_distance/0, que recebe um objeto de carro como parâmetro de entrada e apresenta a string da distância tal como aparece no ecrã de LED:

new_remote_control_car | display_distance
# => "0 meters"

4. Mostra a percentagem da bateria

Implementa a função display_battery/0, que recebe um objeto de carro como parâmetro de entrada e apresenta a string da percentagem da bateria tal como aparece no ecrã de LED:

new_remote_control_car | display_battery
# => "Battery at 100%"

Se a bateria estiver a 0%, o ecrã da bateria mostra "Battery empty".

5. Conduzir altera a bateria e a distância percorrida

Implementa a função drive/0 que:

  • recebe um objeto de carro como parâmetro de entrada
  • atualiza o número de metros percorridos em 20
  • gasta 1% da bateria
  • devolve o objeto de carro modificado
new_remote_control_car("Red") | drive
# => {
#      "battery_percentage": 99,
#      "distance_driven_in_meters": 20,
#      "nickname": "Red"
#    }

6. Tem em conta conduzir com a bateria descarregada

Atualiza a função drive/0 para não aumentar a distância percorrida nem diminuir a percentagem da bateria quando a bateria está descarregada (a 0%):

{
  battery_percentage: 0,
  distance_driven_in_meters: 2000,
  nickname: "Red"
} | drive
# => {
#      "battery_percentage": 0,
#      "distance_driven_in_meters": 2000,
#      "nickname": "Red"
#    }
Editar via GitHub A ligação abre numa nova janela ou separador
jq Exercism

Estás pronto para começar Carro telecomandado?

Inscreve-te no Exercism para aprenderes e dominares jq com 12 conceitos75 exercícios, e mentoria humana real, tudo grátis.