Tracks
/
jq
jq
/
Übungen
/
Ferngesteuertes Auto
Ferngesteuertes Auto

Ferngesteuertes Auto

Lernübung

Einführung

Funktionen

Du kannst in jq deine eigenen benutzerdefinierten Funktionen definieren, um die Logik zu kapseln, die du brauchst. Funktionen verhalten sich wie eingebaute Funktionen: Sie nehmen einen Eingabewert und geben null, eine oder mehrere Ausgaben aus.

Eine Funktion definieren

Du kannst eine jq-Funktion mit der folgenden Syntax definieren:

# no arguments
def funcname: expression;

# or with arguments
def funcname(args): expression;
  • beginnt mit dem Schlüsselwort def,
  • ein Doppelpunkt vor dem Funktionskörper,
  • der Körper besteht aus einem einzigen Ausdruck,
  • endet mit einem Semikolon,
  • wie im restlichen jq-Syntax kannst du beliebige Leerzeichen zur besseren Lesbarkeit verwenden.

Wohin Funktionen gehören

Funktionen müssen definiert werden, bevor sie verwendet werden. Das hier ist ein Fehler:

def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined

Das bedeutet, du musst Funktionen oben in deinem jq-Code platzieren, vor dem „Haupt“-Ausdruck.

Verschachtelte Funktionen

Funktionen können verschachtelt werden:

def A:
    def B(n): n + 1;
    B(10)
    ;
A
# => 11

Hier ist die B-Funktion nur im Körper von A sichtbar.

Gültigkeitsbereich

Eine Funktion führt einen neuen Gültigkeitsbereich für Variablen und verschachtelte Funktionen ein.

Argumente

Funktionsargumente werden durch Semikolons getrennt, nicht durch Kommas. Zum Beispiel eine Funktion, die eine Zahl entgegennimmt und dann eine Zahl addiert und mit einer Zahl multipliziert:

def add_mul(adder; multiplier): (. + adder) * multiplier;

10 | add_mul(5; 4)    # => 60
Note

Semikolons sind nötig, weil das Komma in jq bereits einen Zweck hat: Es ist ein Operator, der Streams verbindet.

Wenn du ein Komma statt eines Semikolons verwendest, wird versucht, zwei Aufrufe der add_mul-Funktion mit einem Argument zu machen. Diese Funktion existiert nicht und schlägt daher beim ersten versuchten Aufruf fehl:

10 | add_mul(5, 4)
# error: add_mul/1 is not defined
Argumente sind Ausdrücke

Funktionsargumente sind Filter, keine Werte. In diesem Sinn verhalten sie sich wie das, was andere Sprachen als Callbacks bezeichnen:

Am Beispiel der add_mul-Funktion:

10 | add_mul(. + 5; . - 2)    # => 200

Was passiert hier?

  • das adder-Argument erhält den Ausdruck . + 5
    • wenn die Funktion . + adder ausführt, wird daraus . + . + 5
    • das ergibt 25, da . == 10
  • ähnlich ist das multiplier-Argument der Ausdruck . - 2
    • das ergibt 8
    • dann ist das Ergebnis 25 * 8 == 200
Argumente als Werte

Manchmal möchtest du ein Argument in eine Variable „materialisieren“:

def my_func(arg):
    arg as $arg
    | other stuff ...
;

Dafür gibt es eine Kurzschreibweise:

def my_func($arg):
    other stuff ...
;

Beachte, dass das nur „syntaktischer Zucker“ ist: Der Name arg ohne $ ist weiterhin im Gültigkeitsbereich der Funktion.

Stelligkeit

Funktionen haben eine Stelligkeit: die Anzahl der Argumente, die sie entgegennehmen.

Funktionen können denselben Namen mit unterschiedlichen Stelligkeiten verwenden. Die eingebaute Funktion range zeigt das: range/1, range/2 und range/3 existieren alle nebeneinander.

Das kann nützlich sein, um rekursive Funktionen zu definieren, die ihren Zustand über Argumente tragen. Zum Beispiel könnte map so implementiert werden:

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]

Rekursion

jq führt Tail-Call-Optimierung durch, aber nur für Funktionen mit Stelligkeit 0.

Module

Ein jq-Modul ist eine Datei, die nur Funktionen enthält. Module werden mit den Befehlen include oder import in ein jq-Programm eingebunden.

Anleitung

In dieser Übung beschäftigst du dich mit einem ferngesteuerten Auto, für das du endlich genug Geld gespart hast.

Die Autos starten mit vollen (100 %) Akkus. Jedes Mal, wenn du das Auto mit der Fernbedienung fährst, legt es 20 Meter zurück und verbraucht ein Prozent des Akkus. Der Spitzname des Autos ist erst bekannt, wenn es erstellt wurde.

Das ferngesteuerte Auto hat eine schicke LED-Anzeige, die zwei Informationen anzeigt:

  • Die insgesamt gefahrene Strecke, angezeigt als: "<METERS> meters".
  • Die verbleibende Akkuladung, angezeigt als: "Battery at <PERCENTAGE>%".

Wenn der Akku bei 0 % ist, kannst du das Auto nicht mehr fahren und die Akkuanzeige zeigt "Battery empty".

1. Ein brandneues ferngesteuertes Auto erstellen

Implementiere die Funktion new_remote_control_car/0, die ein brandneues ferngesteuertes Auto-Objekt zurückgibt:

new_remote_control_car
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": null
#    }

2. Ein brandneues ferngesteuertes Auto mit Spitznamen erstellen

Implementiere die Funktion new_remote_control_car/1, die ein brandneues ferngesteuertes Auto-Objekt mit einem angegebenen Spitznamen zurückgibt:

new_remote_control_car("Blue")
# => {
#      "battery_percentage": 100,
#      "distance_driven_in_meters": 0,
#      "nickname": "Blue"
#    }

3. Die Entfernung anzeigen

Implementiere die Funktion display_distance/0, die ein Auto-Objekt als Eingabe nimmt und den String für die Entfernung ausgibt, wie er auf der LED-Anzeige dargestellt wird:

new_remote_control_car | display_distance
# => "0 meters"

4. Den Akkustand anzeigen

Implementiere die Funktion display_battery/0, die ein Auto-Objekt als Eingabe nimmt und den String für den Akkustand ausgibt, wie er auf der LED-Anzeige dargestellt wird:

new_remote_control_car | display_battery
# => "Battery at 100%"

Wenn der Akku bei 0 % ist, zeigt die Akkuanzeige „Battery empty".

5. Das Fahren verändert den Akkustand und die gefahrene Strecke

Implementiere die Funktion drive/0, die:

  • ein Auto-Objekt als Eingabe nimmt
  • die Anzahl der gefahrenen Meter um 20 erhöht
  • 1 % des Akkus verbraucht
  • das veränderte Auto-Objekt ausgibt
new_remote_control_car("Red") | drive
# => {
#      "battery_percentage": 99,
#      "distance_driven_in_meters": 20,
#      "nickname": "Red"
#    }

6. Fahren mit leerem Akku berücksichtigen

Aktualisiere die Funktion drive/0, sodass sie die gefahrene Strecke nicht erhöht und den Akkustand nicht verringert, wenn der Akku leer ist (bei 0 %):

{
  battery_percentage: 0,
  distance_driven_in_meters: 2000,
  nickname: "Red"
} | drive
# => {
#      "battery_percentage": 0,
#      "distance_driven_in_meters": 2000,
#      "nickname": "Red"
#    }
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
jq Exercism

Bereit, mit Ferngesteuertes Auto zu starten?

Melde dich bei Exercism an, um jq mit 12 Konzepte75 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.