يمكنك تعريف دوال مخصّصة بنفسك في jq لتغلّف بها أي منطق تحتاجه.
تعمل الدوال تمامًا كما تعمل الدوال المدمجة: فهي تأخذ مدخلًا وتُنتج صفرًا أو مخرجًا واحدًا أو أكثر.
يمكنك تعريف دالة في jq باستخدام الصيغة التالية:
# no arguments
def funcname: expression;
# or with arguments
def funcname(args): expression;
def المفتاحية،jq، يمكنك استخدام أي مسافات بيضاء لتحسين القراءة.يجب تعريف الدوال قبل استخدامها: فما يلي خطأ:
def A: B(10);
def B(n): n + 1;
A
# => error: B/1 is not defined
وهذا يعني أنه يجب عليك وضع الدوال في أعلى كود jq، قبل التعبير «الرئيسي».
يمكن أن تكون الدوال متداخلة:
def A:
def B(n): n + 1;
B(10)
;
A
# => 11
هنا لا تظهر دالة B إلا داخل جسم A.
تُنشئ الدالة نطاقًا جديدًا للمتغيرات والدوال المتداخلة.
وسائط الدالة تُفصل بفواصل منقوطة لا بفواصل. على سبيل المثال، دالة تأخذ عددًا، ثم تضيف إليه عددًا وتضرب الناتج في عدد:
def add_mul(adder; multiplier): (. + adder) * multiplier;
10 | add_mul(5; 4) # => 60
الفواصل المنقوطة ضرورية لأن الفاصلة لها وظيفة أخرى في jq: فهي عامل يدمج التدفقات.
استخدام فاصلة بدل الفاصلة المنقوطة سيحاول إجراء استدعاءين لدالة add_mul ذات وسيط واحد، وهي دالة غير موجودة، لذا سيفشل الاستدعاء الأول:
10 | add_mul(5, 4)
# error: add_mul/1 is not defined
وسائط الدالة هي مُرشِّحات، لا قيم. وبهذا المعنى، فهي تتصرف مثل ما تُسمّى في لغات أخرى دوال الاستدعاء الراجعة:
باستخدام دالة add_mul كمثال:
10 | add_mul(. + 5; . - 2) # => 200
ما الذي يحدث هنا؟
adder على التعبير . + 5
. + adder، يصبح ذلك . + . + 5
. == 10
multiplier هو التعبير . - 2
25 * 8 == 200
أحيانًا قد ترغب في «تجسيد» وسيط في متغير:
def my_func(arg):
arg as $arg
| other stuff ...
;
وهناك صيغة مختصرة لذلك:
def my_func($arg):
other stuff ...
;
لاحظ أن هذا مجرّد «سكر نحوي»: فالاسم arg بدون $ لا يزال ضمن النطاق في الدالة.
للدوال عدد وسائط، أي عدد الوسائط التي تأخذها.
ويمكن للدوال أن تستخدم الاسم نفسه بعدد مختلف من الوسائط.
وتوضّح دالة range المدمجة ذلك: فالدوال range/1 وrange/2 وrange/3 تتعايش جميعًا.
قد يكون هذا مفيدًا لتعريف دوال عودية تحمل حالتها عبر الوسائط.
على سبيل المثال، يمكن تنفيذ map هكذا:
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 تحسين الاستدعاء الذيلّي، لكن للدوال ذات عدد الوسائط صفر فقط.
وحدة jq هي ملف لا يحتوي إلا على دوال.
وتُضمّ الوحدات إلى برنامج jq بأمرَي include أو import.
في هذا التمرين ستلهو بسيارة تحكم عن بُعد، وقد ادّخرت أخيرًا ما يكفي من المال لشرائها.
تبدأ السيارات ببطارية ممتلئة (100%). في كل مرة تقود فيها السيارة باستخدام وحدة التحكم، تقطع 20 مترًا وتستهلك واحدًا بالمئة من البطارية. ولا يُعرف لقب السيارة إلا عند إنشائها.
تحتوي سيارة التحكم عن بُعد على شاشة LED أنيقة تعرض معلومتين:
"<METERS> meters"."Battery at <PERCENTAGE>%".إذا وصلت البطارية إلى 0%، فلن تعود قادرًا على قيادة السيارة، وستعرض شاشة البطارية "Battery empty".
نفّذ الدالة new_remote_control_car/0 لتُرجع كائن سيارة تحكم عن بُعد جديدة تمامًا:
new_remote_control_car
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": null
# }
نفّذ الدالة new_remote_control_car/1 لتُرجع كائن سيارة تحكم عن بُعد جديدة تمامًا مع لقب مُعطى:
new_remote_control_car("Blue")
# => {
# "battery_percentage": 100,
# "distance_driven_in_meters": 0,
# "nickname": "Blue"
# }
نفّذ الدالة display_distance/0 التي تأخذ كائن سيارة كمدخل وتُخرج سلسلة نصية للمسافة كما تظهر على شاشة LED:
new_remote_control_car | display_distance
# => "0 meters"
نفّذ الدالة display_battery/0 التي تأخذ كائن سيارة كمدخل وتُخرج سلسلة نصية لنسبة شحن البطارية كما تظهر على شاشة LED:
new_remote_control_car | display_battery
# => "Battery at 100%"
إذا وصلت البطارية إلى 0%، فستعرض شاشة البطارية "Battery empty".
نفّذ الدالة drive/0 التي:
new_remote_control_car("Red") | drive
# => {
# "battery_percentage": 99,
# "distance_driven_in_meters": 20,
# "nickname": "Red"
# }
حدّث الدالة drive/0 بحيث لا تزيد المسافة المقطوعة ولا تنقص نسبة شحن البطارية عندما تكون البطارية فارغة (عند 0%):
{
battery_percentage: 0,
distance_driven_in_meters: 2000,
nickname: "Red"
} | drive
# => {
# "battery_percentage": 0,
# "distance_driven_in_meters": 2000,
# "nickname": "Red"
# }
سجّل في Exercism لتتعلّم وتتقن jq عبر 12 مفهومًا75 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.