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 sea más legible.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 tienes que colocar las funciones al principio de tu código 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 scope para las variables y las funciones anidadas.
Los argumentos de las funciones se separan con puntos y comas, no con comas. Por ejemplo, una función que recibe un número y, a continuación, le suma un número y multiplica por otro número:
def add_mul(adder; multiplier): (. + adder) * multiplier;
10 | add_mul(5; 4) # => 60
Los puntos y comas son necesarios porque la coma ya tiene un propósito en jq: es un operador que une flujos.
Si usas una coma en lugar de un punto y coma, se 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 las funciones son filtros, no valores. En este sentido, se comportan como lo que otros lenguajes describen como callbacks:
Tomando como ejemplo la función add_mul:
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 querrás «materializar» un argumento en una variable:
def my_func(arg):
arg as $arg
| other stuff ...
;
Existe una forma abreviada de hacer esto:
def my_func($arg):
other stuff ...
;
Ten en cuenta que esto no es más que «azúcar sintáctico»: el nombre arg sin $ sigue estando en el scope de la función.
Las funciones tienen una aridad: el número de argumentos que reciben.
Las funciones pueden usar el mismo nombre con aridades distintas.
La función integrada range lo demuestra: range/1, range/2 y range/3 coexisten.
Esto puede ser útil para definir funciones recursivas que mantienen su 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 aplica la optimización de llamadas de cola, pero solo en funciones de aridad 0.
Un módulo de jq es un archivo que solo contiene 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 coche teledirigido, para el que por fin has ahorrado suficiente dinero para comprarlo.
Los coches empiezan con la batería llena (100%). Cada vez que conduces el coche con el mando a distancia, recorre 20 metros y consume un uno por ciento de la batería. El apodo del coche no se conoce hasta que se crea.
El coche teledirigido tiene una pantalla LED llamativa que muestra dos datos:
"<METERS> meters"."Battery at <PERCENTAGE>%".Si la batería está al 0%, ya no puedes conducir el coche 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 coche teledirigido 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 coche teledirigido 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 toma un objeto de coche como entrada y devuelve el string de la distancia tal y como se muestra en la pantalla LED:
new_remote_control_car | display_distance
# => "0 meters"
Implementa la función display_battery/0, que toma un objeto de coche como entrada y devuelve el string del porcentaje de batería tal y 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"
# }
Actualiza 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.