المسارات
/
jq
jq
/
التمارين
/
سيارة التحكم عن بُعد
سيارة التحكم عن بُعد

سيارة التحكم عن بُعد

تمرين تعلّمي

مقدمة

الدوال

يمكنك تعريف دوال مخصّصة بنفسك في 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
Note

الفواصل المنقوطة ضرورية لأن الفاصلة لها وظيفة أخرى في 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
    • ويُقيَّم ذلك إلى 25 بما أن . == 10
  • وبالمثل، الوسيط multiplier هو التعبير . - 2
    • ويُقيَّم ذلك إلى 8
    • ثم تكون النتيجة 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".

1. أنشئ سيارة تحكم عن بُعد جديدة تمامًا

نفّذ الدالة new_remote_control_car/0 لتُرجع كائن سيارة تحكم عن بُعد جديدة تمامًا:

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

2. أنشئ سيارة تحكم عن بُعد جديدة تمامًا مع لقب

نفّذ الدالة new_remote_control_car/1 لتُرجع كائن سيارة تحكم عن بُعد جديدة تمامًا مع لقب مُعطى:

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

3. اعرض المسافة

نفّذ الدالة display_distance/0 التي تأخذ كائن سيارة كمدخل وتُخرج سلسلة نصية للمسافة كما تظهر على شاشة LED:

new_remote_control_car | display_distance
# => "0 meters"

4. اعرض نسبة شحن البطارية

نفّذ الدالة display_battery/0 التي تأخذ كائن سيارة كمدخل وتُخرج سلسلة نصية لنسبة شحن البطارية كما تظهر على شاشة LED:

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

إذا وصلت البطارية إلى 0%، فستعرض شاشة البطارية "Battery empty".

5. القيادة تغيّر البطارية والمسافة المقطوعة

نفّذ الدالة drive/0 التي:

  • تأخذ كائن سيارة كمدخل
  • تزيد عدد الأمتار المقطوعة بمقدار 20
  • تستهلك 1% من البطارية
  • تُخرج كائن السيارة بعد التعديل
new_remote_control_car("Red") | drive
# => {
#      "battery_percentage": 99,
#      "distance_driven_in_meters": 20,
#      "nickname": "Red"
#    }

6. تعامل مع القيادة ببطارية فارغة

حدّث الدالة drive/0 بحيث لا تزيد المسافة المقطوعة ولا تنقص نسبة شحن البطارية عندما تكون البطارية فارغة (عند 0%):

{
  battery_percentage: 0,
  distance_driven_in_meters: 2000,
  nickname: "Red"
} | drive
# => {
#      "battery_percentage": 0,
#      "distance_driven_in_meters": 2000,
#      "nickname": "Red"
#    }
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
jq Exercism

مستعد لبدء سيارة التحكم عن بُعد؟

سجّل في Exercism لتتعلّم وتتقن jq عبر 12 مفهومًا75 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.