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.
Podes definir uma função de jq com a seguinte sintaxe:
# no arguments
def funcname: expression;
# or with arguments
def funcname(args): expression;
def,jq, podes usar espaços em branco à vontade para facilitar a leitura.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».
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.
Uma função introduz um novo âmbito para variáveis e funções aninhadas.
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
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 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?
adder recebe a expressão . + 5
. + adder, isso passa a ser . + . + 5
. == 10
multiplier é a expressão . - 2
25 * 8 == 200
À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.
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]
O jq faz otimização de chamadas em cauda, mas apenas para funções de aridade 0.
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.
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:
"<METERS> meters"."Battery at <PERCENTAGE>%".Se a bateria estiver a 0%, já não consegues conduzir o carro e o ecrã da bateria mostra "Battery empty".
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
# }
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"
# }
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"
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".
Implementa a função drive/0 que:
new_remote_control_car("Red") | drive
# => {
# "battery_percentage": 99,
# "distance_driven_in_meters": 20,
# "nickname": "Red"
# }
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"
# }
Inscreve-te no Exercism para aprenderes e dominares jq com 12 conceitos75 exercícios, e mentoria humana real, tudo grátis.