Parcours
/
jq
jq
/
Exercices
/
Voiture télécommandée
Voiture télécommandée

Voiture télécommandée

Exercice d'apprentissage

Introduction

Les fonctions

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.

Définis une fonction

Tu peux définir une fonction jq avec la syntaxe suivante :

# no arguments
def funcname: expression;

# or with arguments
def funcname(args): expression;
  • commence par le mot-clé def,
  • un deux-points avant le corps de la fonction,
  • le corps est constitué d'une seule expression,
  • se termine par un point-virgule,
  • comme dans le reste de la syntaxe jq, tu peux utiliser des espaces arbitraires pour la lisibilité.

Où placer les fonctions

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 ».

Fonctions imbriquées

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.

Portée

Une fonction introduit une nouvelle portée pour les variables et les fonctions imbriquées.

Arguments

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
Note

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 sont des expressions

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 ?

  • l'argument adder reçoit l'expression . + 5
    • quand la fonction fait . + adder, cela devient . + . + 5
    • ce qui vaut 25 puisque . == 10
  • de même, l'argument multiplier est l'expression . - 2
    • ce qui vaut 8
    • le résultat est alors 25 * 8 == 200
Les arguments en tant que valeurs

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.

Arité

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]

Récursivité

jq effectue une optimisation des appels terminaux, mais uniquement pour les fonctions d'arité 0.

Modules

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.

Instructions

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 :

  • La distance totale qu'elle a parcourue, affichée sous la forme : "<METERS> meters".
  • La charge restante de la batterie, affichée sous la forme : "Battery at <PERCENTAGE>%".

Si la batterie est à 0 %, tu ne peux plus conduire la voiture et l'affichage de la batterie indiquera "Battery empty".

1. Crée une voiture télécommandée toute neuve

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
#    }

2. Crée une voiture télécommandée toute neuve avec un surnom

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"
#    }

3. Affiche la distance

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"

4. Affiche le pourcentage de batterie

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 ».

5. La conduite modifie la batterie et la distance parcourue

Implémente la fonction drive/0 qui :

  • prend un objet voiture en entrée
  • augmente de 20 le nombre de mètres parcourus
  • consomme 1 % de la batterie
  • renvoie l'objet voiture modifié
new_remote_control_car("Red") | drive
# => {
#      "battery_percentage": 99,
#      "distance_driven_in_meters": 20,
#      "nickname": "Red"
#    }

6. Prends en compte la conduite avec une batterie à plat

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"
#    }
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
jq Exercism

Prêt à commencer Voiture télécommandée ?

Inscris-toi sur Exercism pour apprendre et maîtriser jq avec 12 concepts75 exercices, et un vrai mentorat humain, le tout gratuitement.