ডিকম্পোজিশন বলতে কোনো কালেকশনের এলিমেন্টগুলো আলাদা করে বের করার কাজকে বোঝায়, যেমন একটি Array বা Hash।
এরপর ডিকম্পোজ করা মানগুলো একই স্টেটমেন্টেই ভ্যারিয়েবলে অ্যাসাইন করা যায়।
মাল্টিপল অ্যাসাইনমেন্ট হলো একই স্টেটমেন্টে একাধিক ভ্যারিয়েবলে ডিকম্পোজ করা মান অ্যাসাইন করার ক্ষমতা।
এতে কোড আরও সংক্ষিপ্ত ও পাঠযোগ্য হয়, আর এটি করতে অ্যাসাইন করার ভ্যারিয়েবলগুলো কমা দিয়ে আলাদা করা হয়, যেমন first, second, third = [1, 2, 3]।
স্প্ল্যাট অপারেটর (*) আর ডাবল স্প্ল্যাট অপারেটর (**) প্রায়ই ডিকম্পোজিশনের ক্ষেত্রে ব্যবহৃত হয়।
*<variable_name> আর **<variable_name> কে * আর ** এর সঙ্গে গুলিয়ে ফেলা উচিত নয়।
* আর ** যথাক্রমে গুণ ও সূচকের জন্য ব্যবহৃত হয়, অন্যদিকে *<variable_name> আর **<variable_name> কম্পোজিশন ও ডিকম্পোজিশন অপারেটর হিসেবে ব্যবহৃত হয়।
মাল্টিপল অ্যাসাইনমেন্ট দিয়ে আপনি এক লাইনেই একাধিক ভ্যারিয়েবলে অ্যাসাইন করতে পারেন।
মানগুলো আলাদা করতে কমা , ব্যবহার করুন:
>> a, b = 1, 2
=> [1, 2]
>> a
=> 1
মাল্টিপল অ্যাসাইনমেন্ট একটি ডেটা টাইপেই সীমাবদ্ধ নয়:
>> x, y, z = 1, "Hello", true
=> [1, "Hello", true]
>> x
=> 1
>> y
=> 'Hello'
>> z
=> true
মাল্টিপল অ্যাসাইনমেন্ট দিয়ে অ্যারের এলিমেন্টগুলো অদলবদল করা যায়। সর্টিং অ্যালগরিদম রচনার সময় এই কৌশলটি বেশ কমন। যেমন:
>> numbers = [1, 2]
=> [1, 2]
>> numbers[0], numbers[1] = numbers[1], numbers[0]
=> [2, 1]
>> numbers
=> [2, 1]
এটিকে "প্যারালাল অ্যাসাইনমেন্ট" নামেও ডাকা হয়, আর এটি একটি টেম্পোরারি ভ্যারিয়েবল এড়াতে ব্যবহার করা যায়।
মানের চেয়ে বেশি ভ্যারিয়েবল থাকলে বাড়তি ভ্যারিয়েবলগুলোতে nil অ্যাসাইন হবে:
>> a, b, c = 1, 2
=> [1, 2]
>> b
=> 2
>> c
=> nil
Ruby-তে অ্যারে/হ্যাশের এলিমেন্টগুলোকে আলাদা আলাদা ভ্যারিয়েবলে ডিকম্পোজ করা সম্ভব। অ্যারেতে মানগুলো যেহেতু ইন্ডেক্স ক্রমে থাকে, তাই সেগুলো একই ক্রমে ভ্যারিয়েবলগুলোতে আনপ্যাক হয়:
>> fruits = ["apple", "banana", "cherry"]
>> x, y, z = fruits
>> x
=> "apple"
কোনো মানের প্রয়োজন না থাকলে _ দিয়ে বোঝাতে পারেন যে সেটি "নেওয়া হয়েছে কিন্তু ব্যবহার করা হয়নি":
>> fruits = ["apple", "banana", "cherry"]
>> _, _, z = fruits
>> z
=> "cherry"
একটি অ্যারের ভেতরের অ্যারে (যাকে নেস্টেড অ্যারে-ও বলা হয়) থেকে মান ডিকম্পোজ ও অ্যাসাইন করা ঠিক শ্যালো ডিকম্পোজিশনের মতোই কাজ করে, তবে মানের প্রসঙ্গ বা অবস্থান স্পষ্ট করতে ডিলিমিটেড ডিকম্পোজিশন এক্সপ্রেশন (()) দরকার হয়:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> (a, b), (c, d) = fruits_vegetables
>> a
=> "apple"
>> d
=> "potato"
আপনি একটি নেস্টেড অ্যারের শুধু একটি অংশও গভীরভাবে আনপ্যাক করতে পারেন:
>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>> a, (c, d) = fruits_vegetables
>> a
=> ["apple", "banana"]
>> c
=> "carrot"
ডিকম্পোজিশনে ভ্যারিয়েবলের অবস্থান ভুল হলে এবং/অথবা মানের সংখ্যা ভুল হলে আপনি একটি সিনট্যাক্স এরর পাবেন:
fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
(a, b), (d) = fruits_vegetables
# syntax error, unexpected '=', expecting '.' or &. or :: or '['
((a, b), (d)) = fruits_vegetables
# syntax error, unexpected ')', expecting '.' or &. or :: or '['
এখানে পরীক্ষা করে দেখুন, তাহলে বুঝবেন যে ডান দিকের উপলব্ধ মান নয়, বরং প্রথম প্যাটার্নই নির্ধারণ করে। সিনট্যাক্স এররটি ডেটা স্ট্রাকচারের সাথে সম্পর্কিত নয়।
*) দিয়ে একটি অ্যারে ডিকম্পোজ করাএকটি অ্যারে ডিকম্পোজ করার সময় স্প্ল্যাট অপারেটর (*) দিয়ে "বাকি পড়ে থাকা" মানগুলো ধরা যায়।
এটি অ্যারেটি স্লাইস করার চেয়ে স্পষ্ট (যা কিছু ক্ষেত্রে কম পাঠযোগ্য)।
যেমন, আমরা প্রথম এলিমেন্টটি বের করে বাকি মানগুলো প্রথম এলিমেন্ট ছাড়া একটি নতুন অ্যারেতে অ্যাসাইন করতে পারি:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *last = fruits
>> x
=> "apple"
>> last
=> ["banana", "cherry", "orange", "kiwi", "melon", "mango"]
আমরা অ্যারের শুরু ও শেষের মানগুলোও বের করতে পারি, আর মাঝের সব মান একসাথে গ্রুপ করতে পারি:
>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>> x, *middle, y, z = fruits
>> y
=> "melon"
>> middle
=> ["banana", "cherry", "orange", "kiwi"]
ডিপ ডিকম্পোজিশনেও আমরা * ব্যবহার করতে পারি:
>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>> (a, *rest), b = fruits_vegetables
>> a
=> "apple"
>> rest
=> ["banana", "melon"]
Hash ডিকম্পোজ করাএকটি হ্যাশ ডিকম্পোজ করা অ্যারে ডিকম্পোজ করার চেয়ে একটু আলাদা। একটি হ্যাশ আনপ্যাক করতে হলে প্রথমে সেটিকে একটি অ্যারেতে রূপান্তর করতে হবে। নাহলে কোনো ডিকম্পোজিশনই হবে না:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory
>> x
=> {:apple=>6, :banana=>2, :cherry=>3}
>> y
=> nil
একটি Hash-কে অ্যারেতে রূপান্তর করতে আপনি to_a মেথড ব্যবহার করতে পারেন:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> fruits_inventory.to_a
=> [[:apple, 6], [:banana, 2], [:cherry, 3]]
>> x, y, z = fruits_inventory.to_a
>> x
=> [:apple, 6]
কী (key)গুলো আনপ্যাক করতে চাইলে আপনি keys মেথড ব্যবহার করতে পারেন:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.keys
>> x
=> :apple
মানগুলো আনপ্যাক করতে চাইলে আপনি values মেথড ব্যবহার করতে পারেন:
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> x, y, z = fruits_inventory.values
>> x
=> 6
কম্পোজ করা হলো একাধিক মানকে একটি অ্যারেতে গ্রুপ করে একটি ভ্যারিয়েবলে অ্যাসাইন করার ক্ষমতা। আপনি যখন মানগুলো ডিকম্পোজ করতে, পরিবর্তন করতে, তারপর ফলাফলগুলো আবার একটি ভ্যারিয়েবলে কম্পোজ করতে চান তখন এটি কাজে লাগে। এছাড়া এটি ২ বা তার বেশি অ্যারে/হ্যাশ মার্জ করাও সম্ভব করে।
*) দিয়ে একটি অ্যারে কম্পোজ করাস্প্ল্যাট অপারেটর (*) দিয়ে একটি অ্যারে কম্পোজ করা যায়।
এটি সব মান একটি অ্যারেতে প্যাক করে।
>> fruits = ["apple", "banana", "cherry"]
>> more_fruits = ["orange", "kiwi", "melon", "mango"]
# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>> combined_fruits = *fruits, *more_fruits
>> combined_fruits
=> ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
**) দিয়ে একটি হ্যাশ কম্পোজ করাডাবল স্প্ল্যাট অপারেটর (**) দিয়ে একটি হ্যাশ কম্পোজ করা হয়।
এটি একটি হ্যাশের সব কী (key)/মান জোড়া আরেকটি হ্যাশে প্যাক করে, বা দুটি হ্যাশ একসাথে জোড়া লাগায়।
>> fruits_inventory = {apple: 6, banana: 2, cherry: 3}
>> more_fruits_inventory = {orange: 4, kiwi: 1, melon: 2, mango: 3}
# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
# then the pairs are packed into combined_fruits_inventory
>> combined_fruits_inventory
=> {:apple=>6, :banana=>2, :cherry=>3, :orange=>4, :kiwi=>1, :melon=>2, :mango=>3}
*) ও ডাবল স্প্ল্যাট অপারেটর (**) এর ব্যবহারআপনি যখন এমন একটি মেথড তৈরি করেন যা যেকোনো সংখ্যক আর্গুমেন্ট গ্রহণ করে, তখন মেথড ডেফিনিশনে *arguments বা **keyword_arguments ব্যবহার করতে পারেন।
*arguments যেকোনো সংখ্যক পজিশনাল (নন-কিওয়ার্ডেড) আর্গুমেন্ট প্যাক করতে ব্যবহৃত হয় আর
**keyword_arguments যেকোনো সংখ্যক কিওয়ার্ড আর্গুমেন্ট প্যাক করতে ব্যবহৃত হয়।
*arguments এর ব্যবহার:
# This method is defined to take any number of positional arguments
# (Using the single line form of the definition of a method.)
>> def my_method(*arguments)= arguments
# Arguments given to the method are packed into an array
>> my_method(1, 2, 3)
=> [1, 2, 3]
>> my_method("Hello")
=> ["Hello"]
>> my_method(1, 2, 3, "Hello", "Mars")
=> [1, 2, 3, "Hello", "Mars"]
**keyword_arguments এর ব্যবহার:
# This method is defined to take any number of keyword arguments
>> def my_method(**keyword_arguments)= keyword_arguments
# Arguments given to the method are packed into a dictionary
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
ডিফাইন করা মেথডে কিওয়ার্ড আর্গুমেন্টের জন্য (**keyword_arguments বা <key_word>: <value>) কোনো প্যারামিটার ডিফাইন করা না থাকলে, কিওয়ার্ড আর্গুমেন্টগুলো একটি হ্যাশে প্যাক হয়ে শেষ প্যারামিটারে অ্যাসাইন হবে।
>> def my_method(a)= a
>> my_method(a: 1, b: 2, c: 3)
=> {:a => 1, :b => 2, :c => 3}
*arguments আর **keyword_arguments একসাথেও ব্যবহার করা যায়:
def my_method(*arguments, **keyword_arguments)
p arguments.sum
for (key, value) in keyword_arguments.to_a
p key.to_s + " = " + value.to_s
end
end
my_method(1, 2, 3, a: 1, b: 2, c: 3)
6
"a = 1"
"b = 2"
"c = 3"
নির্দিষ্ট পজিশনাল আর্গুমেন্টের সুবিধার জন্য আপনি *arguments এর আগে ও পরে আর্গুমেন্ট লিখতেও পারেন।
এটি অ্যারে ডিকম্পোজ করার মতোই কাজ করে।
আর্গুমেন্টগুলো একটি নির্দিষ্ট ক্রমে সাজাতে হবে:
def my_method(<positional_arguments>, *arguments, <positional_arguments>, <keyword_arguments>, **keyword_arguments)
এই ক্রম না মানলে আপনি একটি এরর পাবেন।
def my_method(a, b, *arguments)
p a
p b
p arguments
end
my_method(1, 2, 3, 4, 5)
1
2
[3, 4, 5]
আপনি *arguments এর আগে ও পরে পজিশনাল আর্গুমেন্ট লিখতে পারেন:
>> def my_method(a, *middle, b)= middle
>> my_method(1, 2, 3, 4, 5)
=> [2, 3, 4]
আপনি পজিশনাল আর্গুমেন্ট, *arguments, কিওয়ার্ড আর্গুমেন্ট আর **keyword_arguments-ও একসাথে মেলাতে পারেন:
>> def my_method(first, *many, last, a:, **keyword_arguments)
p first
p many
p last
p a
p keyword_arguments
end
>> my_method(1, 2, 3, 4, 5, a: 6, b: 7, c: 8)
1
[2, 3, 4]
5
6
{:b => 7, :c => 8}
ভুল ক্রমে আর্গুমেন্ট লিখলে একটি এরর হবে:
def my_method(a:, **keyword_arguments, first, *arguments, last)
arguments
end
my_method(1, 2, 3, 4, a: 5)
syntax error, unexpected local variable or method, expecting & or '&'
... my_method(a:, **keyword_arguments, first, *arguments, last)
স্প্ল্যাট অপারেটর (*) দিয়ে আর্গুমেন্টের একটি অ্যারে মেথড কলে আনপ্যাক করতে পারেন:
def my_method(a, b, c)
p c
p b
p a
end
numbers = [1, 2, 3]
my_method(*numbers)
3
2
1
ডাবল স্প্ল্যাট অপারেটর (**) দিয়ে আর্গুমেন্টের একটি হ্যাশও মেথড কলে আনপ্যাক করতে পারেন:
def my_method(a:, b:, c:)
p c
p b
p a
end
numbers = {a: 1, b: 2, c: 3}
my_method(**numbers)
3
2
1
আপনার বন্ধু Linus একজন লোকোমোটিভ ইঞ্জিনিয়ার, যিনি শহরগুলোর মধ্যে মালবাহী ট্রেন চালান। ট্রেন সামলানোয় তিনি দুর্দান্ত হলেও, লজিস্টিকস বা কম্পিউটার সামলানোয় ঠিক তেমন দুর্দান্ত নন। ট্রেনের তথ্য গুছিয়ে রাখতে এবং রুট ডেটার ভুল সংশোধন করতে তিনি আপনার প্রোগ্রামিং সহায়তা নিতে চান।
এই অনুশীলনীটি স্লাইসিং, ইনডেক্সিং আর বিভিন্ন hash মেথড ব্যবহার করেও সহজে সমাধান করা যেত।
তবে নিচের প্রতিটি কাজ সমাধান করার সময় আমরা চাই আপনি প্যাকিং, আনপ্যাকিং আর মাল্টিপল অ্যাসাইনমেন্ট অনুশীলন করুন।
আপনার বন্ধু প্রতিটি ওয়াগনের আইডেন্টিফায়ার (ID) হিসাব রাখছেন, কিন্তু সিস্টেমকে যে কোনো সময়ে কতগুলো ওয়াগন প্রসেস করতে হবে তা তিনি কখনোই নিশ্চিত নন। এই ডেটাকে একটি একীভূত অ্যারেতে প্যাক করে রাখলে বাকি লজিস্টিকস প্রোগ্রামের জন্য কাজ অনেক সহজ হতো।
এমন একটি মেথড generate_list_of_wagons() বাস্তবায়ন করুন, যেটি ইচ্ছেমতো সংখ্যক ওয়াগন ID গ্রহণ করে।
প্রতিটি ID একটি ধনাত্মক ইন্টিজার হবে।
এরপর মেথডটি প্রদত্ত IDগুলোকে একটি একক অ্যারে হিসেবে return করবে।
LocomotiveEngineer.generate_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
# => [1, 7, 12, 3, 14, 8, 5]
এই পর্যায়ে আপনি ডেটা এবং লজিস্টিকস প্রোগ্রামে এটি কীভাবে ব্যবহৃত হয় সে সম্পর্কে একটা ধারণা পেতে শুরু করেছেন। ID সিস্টেম সবসময় লোকোমোটিভকে 1 আইডি দেয়, আর ট্রেনের বাকি ওয়াগনগুলোকে 1-এর চেয়ে বড় এলোমেলোভাবে বেছে নেওয়া একটি ID দেয়।
আপনার বন্ধুকে ট্রেনে দুটি নতুন ওয়াগন জুড়তে হয়েছিল, কিন্তু সিস্টেম আপডেট করতে ভুলে গেছেন! এখন ট্রেনের অ্যারেতে থাকা প্রথম দুটি ওয়াগনকে শেষে সরাতে হবে, নইলে সবকিছু এলোমেলো হয়ে যাবে।
ব্যাপারটা আরও জটিল করতে, আপনার বন্ধু সম্প্রতি একটি দ্বিতীয় অ্যারে খুঁজে পেয়েছেন, যাতে হারিয়ে যাওয়া ওয়াগন ID আছে বলে মনে হচ্ছে। তিনি শুধু এটুকু মনে করতে পারছেন যে নতুন ওয়াগনগুলো সরানোর পর, এই দ্বিতীয় অ্যারের IDগুলোকে নির্ধারিত লোকোমোটিভের ঠিক পরেই বসাতে হবে।
তাঁর ভুলগুলো সংশোধন করে এবং ডেটা একত্রিত করে দিলে Linus আপনার কাছে সত্যিই কৃতজ্ঞ থাকবেন।
এমন একটি মেথড fix_list_of_wagons() বাস্তবায়ন করুন, যেটি ওয়াগন ID ধারণ করা দুটি অ্যারে নেয়।
এটি প্রথম অ্যারের প্রথম দুটি আইটেমকে শেষে সরিয়ে নেবে, এবং দ্বিতীয় অ্যারের মানগুলো লোকোমোটিভ ID (1)-এর পিছনে (অর্থাৎ ডান দিকে) বসাবে।
এরপর মেথডটি পরিবর্তনগুলোসহ একটি অ্যারে return করবে।
LocomotiveEngineer.fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
# => [1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]
এখন সব ওয়াগন ডেটা সঠিক, Linus চান আপনি সিস্টেমের রুটিং তথ্য আপডেট করুন। একটি পরিবহন রুটে ট্রেন হয়তো মাল তুলতে বা নামাতে কয়েকটি আলাদা স্টেশনে থামতে পারে। প্রতিটি যাত্রায় এই ধরনের মধ্যবর্তী ডেলিভারি পয়েন্টের সংখ্যা আলাদা হতে পারে। আপনার বন্ধু চান আপনি সিস্টেমের রুটিং হ্যাশ আপডেট করুন, যাতে অনুপস্থিত বা অতিরিক্ত ডেলিভারি তথ্য যুক্ত হয়।
এমন একটি মেথড add_missing_stops() বাস্তবায়ন করুন, যেটি একটি রুটিং হ্যাশ এবং তারপর পরিবর্তনশীল সংখ্যক কিওয়ার্ড আর্গুমেন্ট গ্রহণ করে।
এই আর্গুমেন্টগুলো এক বা একাধিক স্টপ ধারণ করা একটি হ্যাশ আকারে হতে পারে, অথবা যেকোনো সংখ্যক stop_<number>: "city" কিওয়ার্ড জোড়া আকারে হতে পারে।
এরপর আপনার মেথডটি রুটিং হ্যাশ ফেরত দেবে, যাতে যোগ করা সব স্টপ ক্রমানুসারে ধারণ করা একটি বাড়তি কী (key) যুক্ত থাকবে।
LocomotiveEngineer.add_missing_stops({from: "New York", to: "Miami"},
stop_1: "Washington, DC", stop_2: "Charlotte", stop_3: "Atlanta",
stop_4: "Jacksonville", stop_5: "Orlando")
# => {from: "New York", to: "Miami", stops: ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}
স্টপগুলো সবসময় সঠিক ক্রমেই পাঠানো হবে, তাই সেগুলো সাজিয়ে ফেলার চিন্তা আপনার করতে হবে না।
Linus রুটিং প্রোগ্রাম নিয়ে কাজ করছেন এবং লক্ষ্য করেছেন যে কিছু রুটে গুরুত্বপূর্ণ কিছু বিবরণ অনুপস্থিত। প্রাথমিক রুট তথ্য একটি হ্যাশ হিসেবে তৈরি করা হয়েছে, আর আপনার বন্ধু চান আপনি যা যা অনুপস্থিত থাকতে পারে তা দিয়ে ওই হ্যাশ আপডেট করুন। সিস্টেমের প্রতিটি রুটে কিছুটা আলাদা বিবরণ দরকার হয়, তাই Linus সত্যিই একটি জেনেরিক সমাধান চান।
extend_route_information() নামের একটি মেথড বাস্তবায়ন করুন, যেটি দুটি হ্যাশ গ্রহণ করে।
প্রথম হ্যাশ-এ ট্রেন রুটটির উৎস ও গন্তব্য শহর থাকে।
দ্বিতীয় হ্যাশ-এ ট্রেনের গতি, দৈর্ঘ্য বা তাপমাত্রার মতো অন্যান্য রুটিং বিবরণ থাকে। মেথডটি সব রুটিং তথ্যসহ একটি সমন্বিত হ্যাশ ফেরত দেবে।
দ্বিতীয় হ্যাশ-এ উদাহরণে দেখানো বৈশিষ্ট্যগুলোর চেয়ে ভিন্ন বা বেশি বৈশিষ্ট্য থাকতে পারে।
LocomotiveEngineer.extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"})
# => {"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"}
Exercism-এ সাইন আপ করুন, Ruby ট্র্যাকের 21টি কনসেপ্ট121টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।