আপনার প্রয়োজনমতো লজিক গুছিয়ে রাখতে আপনি 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 মডিউল হলো এমন একটি ফাইল, যাতে শুধু ফাংশন থাকে।
মডিউলগুলো include বা import কমান্ডের মাধ্যমে একটি jq প্রোগ্রামে যুক্ত করা হয়।
এই অনুশীলনীতে আপনি একটি রিমোট কন্ট্রোলড কার নিয়ে খেলবেন, যেটি কেনার জন্য আপনি অবশেষে যথেষ্ট টাকা জমাতে পেরেছেন।
কারগুলো সম্পূর্ণ (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"
# }
ব্যাটারি শেষ হয়ে গেলে (0%-এ থাকলে) অতিক্রান্ত দূরত্ব যাতে না বাড়ে এবং ব্যাটারির শতাংশ যাতে না কমে, সেভাবে drive/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টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।