Puedes definir tus propias funciones personalizadas en jq para encapsular la lógica que necesites.
Las funciones se comportan igual que las integradas: reciben una entrada y emiten cero, una o más salidas.
Puedes definir una función de jq con la siguiente sintaxis:
# no arguments
def funcname: expression;
# or with arguments
def funcname(args): expression;
def,jq, puedes usar espacios en blanco arbitrarios para que se lea mejor.Las funciones deben definirse antes de usarse: esto es un error:
def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined
Esto implica que debes colocar las funciones al inicio de tu código de jq, antes de la expresión «principal».
Las funciones se pueden anidar:
def A:
def B(n): n + 1;
B(10)
;
A
# => 11
Aquí, la función B solo es visible en el cuerpo de A.
Una función introduce un nuevo ámbito para las variables y las funciones anidadas.
Los argumentos de una función se separan con punto y coma, no con comas. Por ejemplo, una función que recibe un número y luego suma un número y multiplica por un número:
def add_mul(adder; multiplier): (. + adder) * multiplier;
10 | add_mul(5; 4) # => 60
Se necesitan los puntos y coma porque la coma ya tiene un propósito en jq: es un operador que une flujos.
Usar una coma en lugar de un punto y coma intentará hacer dos llamadas a una función add_mul de 1 argumento, que no existe y por lo tanto fallará en la primera llamada:
10 | add_mul(5, 4)
# error: add_mul/1 is not defined
Los argumentos de una función son filtros, no valores. En este sentido, actúan como lo que otros lenguajes describen como callbacks:
Usando la función add_mul como ejemplo:
10 | add_mul(. + 5; . - 2) # => 200
¿Qué está pasando aquí?
adder recibe la expresión . + 5
. + adder, eso se convierte en . + . + 5
. == 10
multiplier es la expresión . - 2
25 * 8 == 200
A veces vas a querer «materializar» un argumento en una variable:
def my_func(arg):
arg as $arg
| other stuff ...
;
Hay una forma abreviada de hacer esto:
def my_func($arg):
other stuff ...
;
Ten en cuenta que esto es solo «azúcar sintáctico»: el nombre arg sin $ sigue estando en el ámbito de la función.
Las funciones tienen una aridad: la cantidad de argumentos que reciben.
Las funciones pueden usar el mismo nombre con distintas aridades.
La función integrada range lo demuestra: range/1, range/2 y range/3 coexisten.
Esto puede ser útil para definir funciones recursivas que llevan estado a través de los argumentos.
Por ejemplo, map podría implementarse así:
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]
jq realiza optimización de llamadas de cola, pero solo para funciones de aridad 0.
Un módulo de jq es un archivo que contiene solo funciones.
Los módulos se incluyen en un programa de jq con los comandos include o import.
En este ejercicio vas a jugar con un auto a control remoto, para el que por fin ahorraste suficiente dinero para comprarlo.
Los autos empiezan con la batería llena (100%). Cada vez que conduces el auto con el control remoto, recorre 20 metros y consume uno por ciento de la batería. El apodo del auto no se conoce hasta que se crea.
El auto a control remoto tiene una pantalla LED muy elegante que muestra dos datos:
"<METERS> meters"."Battery at <PERCENTAGE>%".Si la batería está al 0%, ya no puedes conducir el auto y la pantalla de la batería mostrará "Battery empty".
Implementa la función new_remote_control_car/0 para que devuelva un objeto de auto a control remoto completamente nuevo:
new_remote_control_car
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": null
# }
Implementa la función new_remote_control_car/1 para que devuelva un objeto de auto a control remoto completamente nuevo con un apodo proporcionado:
new_remote_control_car("Blue")
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": "Blue"
# }
Implementa la función display_distance/0, que recibe como argumento un objeto de auto y devuelve el string de la distancia tal como se muestra en la pantalla LED:
new_remote_control_car | display_distance
# => "0 meters"
Implementa la función display_battery/0, que recibe como argumento un objeto de auto y devuelve el string del porcentaje de batería tal como se muestra en la pantalla LED:
new_remote_control_car | display_battery
# => "Battery at 100%"
Si la batería está al 0%, la pantalla de la batería mostrará «Battery empty».
Implementa la función drive/0, que:
new_remote_control_car("Red") | drive
# => {
# "battery_percentage": 99,
# "distance_driven_in_meters": 20,
# "nickname": "Red"
# }
Modifica la función drive/0 para que no aumente la distancia recorrida ni disminuya el porcentaje de batería cuando la batería está agotada (al 0%):
{
battery_percentage: 0,
distance_driven_in_meters: 2000,
nickname: "Red"
} | drive
# => {
# "battery_percentage": 0,
# "distance_driven_in_meters": 2000,
# "nickname": "Red"
# }
Regístrate en Exercism para aprender y dominar jq con 12 conceptos75 ejercicios y mentoría humana real, todo gratis.