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.
Du kannst eine jq-Funktion mit der folgenden Syntax definieren:
# no arguments
def funcname: expression;
# or with arguments
def funcname(args): expression;
def,jq-Syntax kannst du beliebige Leerzeichen zur besseren Lesbarkeit verwenden.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.
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.
Eine Funktion führt einen neuen Gültigkeitsbereich für Variablen und verschachtelte Funktionen ein.
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
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
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?
adder-Argument erhält den Ausdruck . + 5
. + adder ausführt, wird daraus . + . + 5
. == 10
multiplier-Argument der Ausdruck . - 2
25 * 8 == 200
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.
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]
jq führt Tail-Call-Optimierung durch, aber nur für Funktionen mit Stelligkeit 0.
Ein jq-Modul ist eine Datei, die nur Funktionen enthält.
Module werden mit den Befehlen include oder import in ein jq-Programm eingebunden.
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:
"<METERS> meters"."Battery at <PERCENTAGE>%".Wenn der Akku bei 0 % ist, kannst du das Auto nicht mehr fahren und die Akkuanzeige zeigt "Battery empty".
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
# }
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"
# }
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"
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".
Implementiere die Funktion drive/0, die:
new_remote_control_car("Red") | drive
# => {
# "battery_percentage": 99,
# "distance_driven_in_meters": 20,
# "nickname": "Red"
# }
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"
# }
Melde dich bei Exercism an, um jq mit 12 Konzepte75 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.