Tu peux définir tes propres fonctions personnalisées en jq pour encapsuler la logique dont tu as besoin.
Les fonctions se comportent exactement comme les fonctions natives : elles prennent une entrée et émettent zéro, une ou plusieurs sorties.
Tu peux définir une fonction jq avec la syntaxe suivante :
# no arguments
def funcname: expression;
# or with arguments
def funcname(args): expression;
def,jq, tu peux utiliser des espaces arbitraires pour la lisibilité.Les fonctions doivent être définies avant d'être utilisées : ceci provoque une erreur :
def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined
Cela implique que tu dois placer les fonctions en haut de ton code jq, avant l'expression « principale ».
Les fonctions peuvent être imbriquées :
def A:
def B(n): n + 1;
B(10)
;
A
# => 11
Ici, la fonction B n'est visible que dans le corps de A.
Une fonction introduit une nouvelle portée pour les variables et les fonctions imbriquées.
Les arguments d'une fonction sont séparés par des points-virgules, et non par des virgules. Par exemple, une fonction qui prend un nombre, puis ajoute un nombre et multiplie par un nombre :
def add_mul(adder; multiplier): (. + adder) * multiplier;
10 | add_mul(5; 4) # => 60
Les points-virgules sont nécessaires parce que la virgule a déjà un rôle en jq : un opérateur qui joint des flux.
Utiliser une virgule au lieu d'un point-virgule tentera de faire deux appels à une fonction add_mul à un argument, qui n'existe pas et échouera donc dès le premier appel tenté :
10 | add_mul(5, 4)
# error: add_mul/1 is not defined
Les arguments d'une fonction sont des filtres, pas des valeurs. En ce sens, ils se comportent comme ce que d'autres langages appellent des callbacks :
Prenons la fonction add_mul comme exemple :
10 | add_mul(. + 5; . - 2) # => 200
Que se passe-t-il ici ?
adder reçoit l'expression . + 5
. + adder, cela devient . + . + 5
. == 10
multiplier est l'expression . - 2
25 * 8 == 200
Parfois, tu voudras « matérialiser » un argument dans une variable :
def my_func(arg):
arg as $arg
| other stuff ...
;
Il existe une notation abrégée pour cela :
def my_func($arg):
other stuff ...
;
Note bien qu'il s'agit simplement de « sucre syntaxique » : le nom arg sans $ reste dans la portée de la fonction.
Les fonctions ont une arité, c'est-à-dire le nombre d'arguments qu'elles prennent.
Des fonctions peuvent porter le même nom avec des arités différentes.
La fonction native range le montre : range/1, range/2 et range/3 coexistent toutes les trois.
Cela peut être utile pour définir des fonctions récursives qui transportent un état via leurs arguments.
Par exemple, map pourrait être implémentée comme ceci :
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 effectue une optimisation des appels terminaux, mais uniquement pour les fonctions d'arité 0.
Un module jq est un fichier qui ne contient que des fonctions.
Les modules sont inclus dans un programme jq avec les commandes include ou import.
Dans cet exercice, on va jouer avec une voiture télécommandée, que tu as enfin réussi à t'offrir après avoir économisé assez d'argent.
Les voitures démarrent avec une batterie pleine (100 %). Chaque fois que tu conduis la voiture avec la télécommande, elle parcourt 20 mètres et consomme un pour cent de la batterie. Le surnom de la voiture n'est connu qu'au moment où elle est créée.
La voiture télécommandée possède un bel affichage LED qui montre deux informations :
"<METERS> meters"."Battery at <PERCENTAGE>%".Si la batterie est à 0 %, tu ne peux plus conduire la voiture et l'affichage de la batterie indiquera "Battery empty".
Implémente la fonction new_remote_control_car/0 pour renvoyer un objet voiture télécommandée toute neuve :
new_remote_control_car
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": null
# }
Implémente la fonction new_remote_control_car/1 pour renvoyer un objet voiture télécommandée toute neuve avec un surnom fourni :
new_remote_control_car("Blue")
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": "Blue"
# }
Implémente la fonction display_distance/0 qui prend un objet voiture en entrée et renvoie la string de distance telle qu'affichée sur l'écran LED :
new_remote_control_car | display_distance
# => "0 meters"
Implémente la fonction display_battery/0 qui prend un objet voiture en entrée et renvoie la string du pourcentage de batterie telle qu'affichée sur l'écran LED :
new_remote_control_car | display_battery
# => "Battery at 100%"
Si la batterie est à 0 %, l'affichage de la batterie indique « Battery empty ».
Implémente la fonction drive/0 qui :
new_remote_control_car("Red") | drive
# => {
# "battery_percentage": 99,
# "distance_driven_in_meters": 20,
# "nickname": "Red"
# }
Modifie la fonction drive/0 pour qu'elle n'augmente pas la distance parcourue et ne diminue pas le pourcentage de batterie lorsque la batterie est à plat (à 0 %) :
{
battery_percentage: 0,
distance_driven_in_meters: 2000,
nickname: "Red"
} | drive
# => {
# "battery_percentage": 0,
# "distance_driven_in_meters": 2000,
# "nickname": "Red"
# }
Inscris-toi sur Exercism pour apprendre et maîtriser jq avec 12 concepts75 exercices, et un vrai mentorat humain, le tout gratuitement.