একটি অবজেক্টের জন্য মিউটেবল (mutable) হওয়ার মানে কী?
ফ্যান্সি শব্দগুলো আপনাকে বিভ্রান্ত করুক না, “মিউটাবিলিটি” শুধু মানে একটি অবজেক্টের ইন্টারনাল স্টেট পরিবর্তন করা যায়। এটি সকল অবজেক্টের ডিফল্ট, বাদ দিয়ে যেগুলো ফ্রোজেন (frozen), বা যেগুলো স্পেশাল অবজেক্টের একটি লিস্টের অংশ।
অন্য কথায়, রুবিতে সকল অবজেক্ট মিউটেবল নয়!
উদাহরণস্বরূপ:
নাম্বার বা সিম্বল, বা এমনকি true বা false (যেগুলোও অবজেক্ট) এর জন্য পরিবর্তন হয় না এমন কোনো মানে নেই।
নাম্বার ১ সবসময় ১ই থাকবে।
কিন্তু অন্যান্য অবজেক্ট, বিশেষ করে যেগুলো ডেটা সংরক্ষণের জন্য তৈরি, যেমন Array বা Hash অবজেক্ট, পারফরম্যান্সের কারণেই পরিবর্তন করার ক্ষমতা রাখতে হবে।
বিকল্প কী?
আপনি পরিবর্তনসহ একটি অবজেক্টের নতুন কপি তৈরি করতে পারেন এবং সেই নতুন অবজেক্টটি রিটার্ন করতে পারেন, মূল অবজেক্টটিকে অক্ষত রেখে।
যদি অ্যারে ইমিউটেবল হতো এবং আপনি একটি অ্যারের শুধু একটি এলিমেন্ট পরিবর্তন করতে চাইলে, আপনাকে সকল ডেটা কপি করতে হতো, যার মধ্যে সেই এলিমেন্টগুলিও থাকত যেগুলো পরিবর্তন হয়নি।
কল্পনা করুন এক মিলিয়ন (বা তার বেশি) এলিমেন্ট বিশিষ্ট একটি অ্যারে প্রতিবার কপি করতে হলে যেকোনো ছোট পরিবর্তনের জন্য! এটা আচ্ছা ইফিশিয়েন্ট নয়…
যাই হোক।
চলুন রুবিতে মিউটাবিলিটি কীভাবে কাজ করে সেটা একটু গভীরে দেখি।
মিউটাবিলিটি এবং ভেরিয়েবলס পয়েন্টার হিসেবে
প্রোগ্রামিং এররোর একটি ক্যাটাগরি রয়েছে যা দুটি জিনিসের কম্বিনেশন থেকে হয়:
- মিউটেবল অবজেক্ট।
- ভেরিয়েবলগুলো সরাসরি ডেটা ধারণ করে না, বরং সেটি কোথায় সংরক্ষিত সেটার একটি রেফারেন্স ধারণ করে।
এই এররগুলো যে একটি উপায়ে দেখায় সেটি হলো যখন আপনি একটি ভেরিয়েবলকে ‘অ্যালিয়াস’ (alias) করার চেষ্টা করেন।
এখানে একটি উদাহরণ:
name = "Peter" other_name = name puts other_name # "Peter"
এই উদাহরণে, name এবং other_name দুটোই একই স্ট্রিং অবজেক্টের রেফারেন্স ধারণ করে। আপনি যেকোনো একটি ব্যবহার করে এই স্ট্রিংয়ের কন্টেন্ট ডিসপ্লে বা মডিফাই করতে পারেন।

সমস্যা দেখায় যদি আমরা other_name-কে স্ট্রিংয়ের একটি কপি মনে করে ব্যবহার করি।
other_name[0] = 'T' name # "Teter" other_name # "Teter"
যেহেতু দুটি ভেরিয়েবল একই স্ট্রিংয়ের দিকে পয়েন্ট করে, আমরা নিশ্চয়ই “Peter”-কে “Teter”-এ পরিবর্তন করেছি। এটা একটি সমস্যা কারণ আমরা সম্ভবত “Peter”-কে রাখতে চেয়েছিলাম।
অবজেক্ট ক্লোনিং
এই সমস্যা থেকে নিচ্ছুর হওয়ার একটি উপায় হলো dup মেথড ব্যবহার করা।
এটি রুবিকে বলে আপনাকে অবজেক্টের একটি কপি দিতে। clone মেথডও রয়েছে, যা কপি দাওয়ার পাশাপাশি ফ্রোজেন স্ট্যাটাস এবং অবজেক্টে ডিফাইন করা সিংগলটন মেথডগুলোকেও কপি করে।
একটি উদাহরণ দেখি:
numbers = [1, 2, 3] more_numbers = numbers.dup more_numbers << 4 numbers # [1, 2, 3] more_numbers # [1, 2, 3, 4]
এই উদাহরণে আপনি দেখতে পাচ্ছেন কীভাবে মূল numbers অ্যারে অপরিবর্তিত রয়েছে। থার্ড লাইন থেকে সেই dup কলটি সরিয়ে ফেলুন এবং দেখুন কী হয় 🙂
রুবি Freeze মেথড
একটি অবজেক্টকে অয়াচ্ছিত পরিবর্তন থেকে রক্ষা করার আরেকটি উপায় হলো এটি ‘ফ্রিজ’ করা। যেকোনো রুবি অবজেক্ট freeze মেথড ব্যবহার করে ফ্রিজ করা যায়।
যখন একটি অবজেক্ট ফ্রিজ করা হয়, এই অবজেক্টকে পরিবর্তন করার যেকোনো চেষ্টা একটি RuntimeError এক্সেপশন ফেলবে।
নোট: আপনি
frozen?মেথড ব্যবহার করে চেক করতে পারেন একটি অবজেক্ট ফ্রিজ করা হয়েছে কিনা।
উদাহরণ:
animals = %w( cat dog tiger ) animals.freeze animals << 'monkey' # RuntimeError: can't modify frozen Array
একটি জিনিস মনে রাখতে হবে যে এটি শুধু একটি অবজেক্টকে ফ্রিজ করবে, এই উদাহরণে অ্যারেটি নিজে, যা আমাদের আইটেম যোগ বা বাদ দেওয়া থেকে রোধ করে। কিন্তু অ্যারের ভেতরের স্ট্রিংগুলো ফ্রিজ করা নয়, তাই সেগুলো এখনও পরিবর্তন করা যায়!
animals[1][0] = 't' # => ["cat", "tog", "tiger"]
আপনি যদি স্ট্রিংগুলো ফ্রিজ করতে চান তবে সেগুলোতে freeze কল করতে হবে। এমন: animals.each(&:freeze)।
ফ্রোজেন স্ট্রিংস
মিউটেবল অবজেক্টের পারফরম্যান্সে প্রভাব পড়ে, বিশেষ করে স্ট্রিংস। কারণ হলো একটি বড় প্রোগ্রামে একটিই স্ট্রিং একাধিকবার ব্যবহৃত হওয়ার ভালো চান্স রয়েছে।
রুবি প্রতিটি স্ট্রিংয়ের জন্য একটি নতুন অবজেক্ট তৈরি করবে, এমনকি দুটি স্ট্রিং এক जैसा দেখালেও, বা অন্য কথায় তাদের একই ‘কন্টেন্ট’ থাকলেও। আপনি সহজে irb-এ object_id মেথড ব্যবহার করে এটি দেখতে পারেন।
এখানে একটি উদাহরণ:
a = 'test' b = 'test' a.object_id # 76325640 b.object_id # 76317550
এটি একটি সমস্যা কারণ এই অবজেক্টগুলো এক্সট্রা মেমরি এবং এক্সট্রা CPU সাইকেল কনসাম করে।
রুবি ২.১ থেকে, আপনি যখন ফ্রোজেন স্ট্রিংস ব্যবহার করেন, রুবি একই স্ট্রিং অবজেক্টটি ব্যবহার করবে। এটি একই স্ট্রিংয়ের নতুন কপি তৈরি করার প্রয়োজনীয়তা দূর করে। যার ফলশ্রুতিরূপে কিছু মেমরি বাঁচায় এবং একটি ছোট পারফরম্যান্স বুস্ট পাওয়া যায়।
রেইলস व्यাপকভাবে ফ্রোজেন স্ট্রিংস ব্যবহার করে এই কারণেই। উদাহরণস্বরূপ, এই PR-এ এক নজর দিন।
এটি রুবি ডেভেলপমেন্ট টিমকে স্ট্রিংসকে ডিফল্টভাবে ইমিউটেবল অবজেক্ট হিসেবে বিবেচনা করার কথা চিন্তা করতে বাধ্য করল। সত্যি, রুবি ২.৩, যা কয়েক দিন আগে রিলিজ হয়েছে, আপনার প্রজেক্টের জন্য এটি সক্ষম করার দুটি পদ্ধতি দিয়েছে।
একটি হলো প্রতিটি ফাইলের শীর্ষে # frozen_string_literal: true ইনক্লুড করা যেখানে আপনি স্ট্রিংসকে ইমিউটেবল চান। আরেকটি হলো কমান্ড-লাইন আর্গুমেন্ট --enable=frozen-string-literal ব্যবহার করা।
ডিফল্টভাবে ইমিউটেবল স্ট্রিংস সম্ভবত রুবি ৩.০-এ আসবে।
এখন পাগলার মত আপনার অ্যাপের সকল স্ট্রিং ফ্রিজ করা শুরু করবেন না। আপনি শুধু সেই স্ট্রিংস ফ্রিজ করবেন যেগুলো সैंकड़ोंবার ব্যবহার হয় যাতে কিছু লাভ দেখা যায়।そう言うと, এখানে একটি টুল আছে যা আপনি পটেনশিয়াল স্ট্রিং খুঁজে বের করতে ব্যবহার করতে পারেন।
আপনার মেথডগুলো জানুন
এক মিউটেবল অবজেক্টের সকল মেথড আসলে অবজেক্টটি পরিবর্তন করে না, উদাহরণস্বরূপ, gsub মেথড একটি নতুন স্ট্রিং রিটার্ন করবে, মূলটিকে অক্ষত রেখে।
এই মেথডগুলোর মধ্যে কিছুর একটি বিকল্প ভার্শন রয়েছে যা অবজেক্টটিকে ইন-প্লেস পরিবর্তন করে, যা প্রায়শই আরও ইফিশিয়েন্ট। এই মেথডগুলো প্রায়শই এক্সক্লামেশন সিম্বল ! দিয়ে শেষ হয় তাদের ইফেক্ট বোঝাতে।
এই ‘ব্যাং’ (bang) মেথডের দুটি উদাহরণ হলো gsub! এবং map!।
এটি নোট করুন:
! দিয়ে শেষ হয় এমন একটি মেথড সবসময় মানে ‘অবজেক্ট পরিবর্তন করার মেথড’ নয়।
বroad términosে, ! সিম্বলটি ‘ডেঞ্জার’ (danger) বোঝাতে ব্যবহৃত হয়। এর একটি উদাহরণ হলো exit! মেথড, যা প্রোগ্রামটি তত্ক্ষণাৎ বন্ধ করে দেবে, যেকোনো এক্সিট হ্যান্ডলার অগ্রাহ্য করে।
একteres মেথডও রয়েছে jotka অবজেক্ট পরিবর্তন করে কিন্তু ! দিয়ে শেষ হয় না। উদাহরণ: delete, clear, push, concat, এবং অ protok।
সমাপ্তি
মিউটাবিলিটি একটি ট্রিকি বিষয় হতে পারে, কিন্তু যেহেতু আপনি এই পোস্টটি পড়েছেন আপনি এখন এটিকে হ্যান্ডল করার জন্য অনেক ভালো প্রস্তুত। রুবি ডকুমেন্টেশন চেক করুন যদি আপনি নিশ্চিত ন हों একটি মেথড কী করছে, এটি আপনাকে সমস্যা থেকে বাঁচাবে।
আশা করি আর্টিকেলটি ইনফরমেটিভ লাগলো, দয়া করে আপনার বন্ধুদের সাথে শেয়ার করুন যাতে তারা ও এনজয় করতে পারে। আপনি আমার নিউজলেটারও জয়েন করুন নিচে যাতে এই টাইপের আরও কন্টেন্ট আসার সময় আপনি মিস না করেন!