নেম, ভ্যারিয়েবল ও বাইন্ডিং
এই পাঠে যা শিখবেন
- বাইন্ডিং-এর general সংজ্ঞা এবং কেন এটি শুধু ভ্যারিয়েবলের চেয়ে বেশি কিছু কভার করে
- চারটি বাইন্ডিং টাইম চেনা এবং বাস্তব উদাহরণ দিয়ে আলাদা করা
- স্ট্যাটিক বনাম ডায়নামিক বাইন্ডিং-এর সম্পর্ক টাইপিং ও স্কোপিং-এর সাথে
- Python দিয়ে বাইন্ডিং টাইম অনুসারে sort করা একটি ছোট প্রোগ্রাম
১ · বাইন্ডিং কী
বাইন্ডিংBindingএকটি নেম-এর সাথে কোনো অ্যাট্রিবিউট — টাইপ, ভ্যালু, মেমরি লোকেশন বা অপারেশন — এর association। প্রোগ্রামিং ল্যাঙ্গুয়েজ থিওরিতে একটি deliberately general concept। হলো একটি নেমএকটি প্রোগ্রামে ব্যবহৃত পরিচয়সূচক টেক্সট — ভ্যারিয়েবল, ফাংশন, কীওয়ার্ড, অপারেটর — সবকিছুরই একটি নেম আছে।-এর সাথে কোনো একটি অ্যাট্রিবিউট-এর association। বেশিরভাগ প্রোগ্রামার যখন "বাইন্ডিং" শোনেন, তখন প্রথমেই মনে করেন "ভ্যারিয়েবল = ভ্যালু" — কিন্তু এটি একটি অনেক বেশি সাধারণ (general) ধারণা। M6-এর সিম্বল টেবিল (L28-L29) একটি কম্পাইলার কীভাবে এই বাইন্ডিংগুলো ভিতরে ভিতরে ট্র্যাক করে তা দেখিয়েছিল — এই পাঠ থেকে আমরা প্রশ্নটাকে উল্টে দিচ্ছি: একজন ভাষা-ডিজাইনার নেম-বাইন্ডিং নিয়ে ঠিক কী কী সিদ্ধান্ত নিতে পারেন, এবং সেই সিদ্ধান্তগুলোর পরিণতি কী।
কয়েকটি উদাহরণ দেখলে "বাইন্ডিং" ধারণাটির ব্যাপ্তি স্পষ্ট হয় —
- ভ্যারিয়েবল বাইন্ডিং: নেম
xএকটি নির্দিষ্ট মেমরি লোকেশনে বাইন্ড হয়, এবং সেই লোকেশন একটি ভ্যালুতে বাইন্ড হয়। - অপারেটর বাইন্ডিং: নেম
+"addition" অপারেশনে বাইন্ড হয় — এটি ভাষা ডিজাইন করার সময়ই ঠিক হয়ে যায়। - কীওয়ার্ড বাইন্ডিং: নেম
ifএকটি নির্দিষ্ট সিলেকশন-কন্ট্রোল সিমান্টিক্সে বাইন্ড হয়। - টাইপ বাইন্ডিং: নেম
xএকটি নির্দিষ্ট টাইপে (যেমনint) বাইন্ড হতে পারে।
২ · বাইন্ডিং টাইম
বাইন্ডিং টাইমBinding Timeযে মুহূর্তে একটি নেম-অ্যাট্রিবিউট association নির্ধারিত হয় — যত আগে বাইন্ডিং হয়, তত কম নমনীয়তা কিন্তু তত কম রানটাইম খরচ। হলো ঠিক কোন মুহূর্তে একটি বাইন্ডিং নির্ধারিত হয়। প্রথম থেকে শেষ পর্যন্ত চারটি স্তর —
সবচেয়ে আগে বাইন্ড হয় — যেমন
+ নেমটি addition অপারেশনে বাইন্ড হয় যখন ভাষাটি নিজেই ডিজাইন করা হয়, কোনো নির্দিষ্ট প্রোগ্রাম লেখার আগেই।প্রোগ্রাম কম্পাইল হওয়ার সময় বাইন্ড হয় — স্ট্যাটিকালি-টাইপড ভাষায় (M7/L32) একটি ভ্যারিয়েবলের টাইপ সাধারণত এখানেই ফিক্সড হয়ে যায়।
প্রোগ্রাম মেমরিতে লোড হওয়ার সময় বাইন্ড হয় — কিছু সিস্টেমে একটি গ্লোবাল ভ্যারিয়েবলের মেমরি অ্যাড্রেস এখানে ঠিক হয়।
সবচেয়ে দেরিতে বাইন্ড হয় — একটি ভ্যারিয়েবলের আসল ভ্যালু, এবং (L39-এ দেখব) স্ট্যাক-অ্যালোকেটেড লোকাল ভ্যারিয়েবলের মেমরি অ্যাড্রেস প্রতিটি ফাংশন কলে নতুন করে বাইন্ড হয়।
৩ · স্ট্যাটিক বনাম ডায়নামিক বাইন্ডিং
একটি স্ট্যাটিক বাইন্ডিং রান টাইম শুরু হওয়ার আগেই ফিক্সড হয়ে যায় এবং এক্সিকিউশনের
সময় আর বদলায় না। একটি ডায়নামিক বাইন্ডিং প্রোগ্রাম চলাকালীন বদলাতে পারে। লক্ষ্য করুন —
বাইন্ডিং টাইমের চারটি স্তরের মধ্যে "রান টাইম"-এর আগের তিনটি (ডিজাইন/কম্পাইল/লোড টাইম) সবসময় স্ট্যাটিক,
কিন্তু রান-টাইম বাইন্ডিং স্ট্যাটিক অথবা ডায়নামিক দুটোই হতে পারে — যেমন একটি ভ্যারিয়েবলের ভ্যালু বাইন্ডিং
সাধারণত ডায়নামিক (assignment দিয়ে বদলানো যায়), কিন্তু একটি const ভ্যারিয়েবলের ভ্যালু রান
টাইমে একবার বাইন্ড হয়ে আর বদলায় না।
স্ট্যাটিক/ডায়নামিক বাইন্ডিং একটি সাধারণ axis — এবং M7/L32-এর স্ট্যাটিক-বনাম-ডায়নামিক টাইপিং এবং M8/L38-এর স্ট্যাটিক-বনাম-ডায়নামিক স্কোপিং আসলে এই একই axis-এর দুটো নির্দিষ্ট প্রয়োগ (instance) — শুধু ভিন্ন অ্যাট্রিবিউটের উপর প্রয়োগ করা। টাইপিং প্রশ্ন করে "নেম-এর টাইপ অ্যাট্রিবিউট কখন বাইন্ড হয়?" — স্কোপিং প্রশ্ন করে "একটি নেম রেফারেন্স কোন ডিক্লারেশন অ্যাট্রিবিউটে বাইন্ড হয়?" পরের পাঠে (L38) আমরা ঠিক এই দ্বিতীয় প্রশ্নটি গভীরভাবে দেখব।
৪ · কোড: বাইন্ডিং টাইম অনুসারে sort করা
নিচের কোড সেলে আমরা একটি ছোট্ট Binding রেকর্ড টাইপ বানাব (নেম, অ্যাট্রিবিউট, বাইন্ডিং টাইম),
এরপর চারটি বাইন্ডিং টাইমের একটি করে উদাহরণ যোগ করে সময়ানুসারে (আগে থেকে পরে) sort করব — যাতে পুরো
স্পেকট্রামটি একসাথে চোখে পড়ে।
from collections import namedtuple
Binding = namedtuple("Binding", ["name", "attribute", "binding_time"])
# বাইন্ডিং টাইমের ক্রম -- সংখ্যা যত ছোট, তত আগে ফিক্সড হয়
BINDING_TIME_ORDER = {
"language_design_time": 0,
"compile_time": 1,
"load_time": 2,
"run_time": 3,
}
bindings = [
Binding("+", "addition_operation", "language_design_time"),
Binding("if", "selection_keyword", "language_design_time"),
Binding("x", "int", "compile_time"),
Binding("global_counter", "0x4000", "load_time"),
Binding("x", 42, "run_time"),
]
sorted_bindings = sorted(bindings, key=lambda b: BINDING_TIME_ORDER[b.binding_time])
print("বাইন্ডিং টাইম অনুসারে (আগে -> পরে):\n")
for b in sorted_bindings:
print(f" [{b.binding_time:<21}] name={b.name!r:<8} -> attribute={b.attribute!r}")
x নেমটি দুইবার আলাদা এন্ট্রি হিসেবে আছে: একবার তার টাইপ অ্যাট্রিবিউট
(compile_time-এ বাইন্ড) নিয়ে, আরেকবার তার ভ্যালু অ্যাট্রিবিউট (run_time-এ বাইন্ড) নিয়ে। এটিই মূল
কথা — একই নেমের একাধিক ভিন্ন অ্যাট্রিবিউট থাকতে পারে, এবং প্রতিটি অ্যাট্রিবিউট তার নিজস্ব বাইন্ডিং টাইমে
বাইন্ড হতে পারে, স্বাধীনভাবে।
বাইন্ডিং একটি নেম-কে একটি অ্যাট্রিবিউটের সাথে যুক্ত করে, এবং এই association ল্যাঙ্গুয়েজ ডিজাইন টাইম থেকে রান টাইম পর্যন্ত যেকোনো মুহূর্তে ঘটতে পারে। স্ট্যাটিক/ডায়নামিক বাইন্ডিং-এর এই সাধারণ axis-ই M7-এর টাইপিং ও M8-এর বাকি পাঠগুলোর (স্কোপ, লাইফটাইম, ক্লোজার) তাত্ত্বিক ভিত্তি — পরের পাঠে আমরা সরাসরি স্কোপ রেজোলিউশনে যাচ্ছি।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
অপারেটর +-কেও কেন একটি "বাইন্ডিং" বলা যায়, যেখানে এটি কোনো ভ্যারিয়েবল নয়?
কারণ বাইন্ডিং-এর সংজ্ঞা "ভ্যারিয়েবল = ভ্যালু"-এর চেয়ে অনেক বেশি সাধারণ — এটি যেকোনো নেম ও অ্যাট্রিবিউটের
মধ্যে association। + নেমটি "addition operation" নামক অ্যাট্রিবিউটে বাইন্ড হয়েছে — এই
বাইন্ডিং শুধু ভাষা ডিজাইন করার সময় একবারই ঘটে (language design time) এবং প্রতিটি প্রোগ্রামে চিরকাল একই
থাকে, তবু এটি একটি সম্পূর্ণ বৈধ বাইন্ডিং।
প্র ০২ একটি ভ্যারিয়েবলের টাইপ কম্পাইল টাইমে বাইন্ড হওয়া, আর তার ভ্যালু রান টাইমে বাইন্ড হওয়া — এই পার্থক্য বাস্তবে কী প্রভাব ফেলে?
টাইপ কম্পাইল টাইমে বাইন্ড হওয়ার মানে হলো — একটি টাইপ-মিসম্যাচ এরর প্রোগ্রাম রান করার আগেই ধরা পড়ে যেতে পারে (M7/L32-এর স্ট্যাটিক টাইপিং)। কিন্তু ভ্যালু বাইন্ডিং রান টাইম পর্যন্ত অপেক্ষা করতে হয়, কারণ ভ্যালুটি সাধারণত ইনপুট, গণনা, বা প্রোগ্রামের প্রবাহের উপর নির্ভর করে — যা কম্পাইল টাইমে জানা সম্ভব নয়। এই কারণেই টাইপ-চেকিং একটি স্ট্যাটিক অ্যানালাইসিস হতে পারে, কিন্তু ভ্যালু-নির্ধারণ কখনোই নয়।
প্র ০৩ স্ট্যাটিক/ডায়নামিক বাইন্ডিং-এর সাধারণ axis-টি কীভাবে টাইপিং (M7) ও স্কোপিং (M8/L38)-এর সাথে সম্পর্কিত?
টাইপিং ও স্কোপিং দুটোই আসলে একই প্রশ্নের ভিন্ন প্রয়োগ: "একটি নির্দিষ্ট অ্যাট্রিবিউট কখন এবং কতটা স্থায়ীভাবে একটি নেমের সাথে বাইন্ড হয়?" স্ট্যাটিক টাইপিং মানে "টাইপ" অ্যাট্রিবিউট কম্পাইল টাইমে ফিক্সড হয়ে যায়; স্ট্যাটিক স্কোপিং মানে "কোন ডিক্লারেশন" অ্যাট্রিবিউট কম্পাইল টাইমে (আসলে, সোর্স টেক্সটের গঠন দেখেই) ফিক্সড হয়ে যায়। দুটোই একই "static = আগে ফিক্সড, dynamic = রানটাইমে বদলাতে পারে" নীতির উদাহরণ, শুধু ভিন্ন অ্যাট্রিবিউটের উপর প্রয়োগ করা।
অনুশীলন
-
চিন্তা করুন: Python-এ একটি ভ্যারিয়েবলের টাইপ কোন বাইন্ডিং টাইমে বাইন্ড হয় (ডিজাইন, কম্পাইল, লোড, নাকি রান টাইম)? Python ডায়নামিকালি-টাইপড হওয়ায় উত্তরটি C-এর মতো স্ট্যাটিকালি-টাইপড ভাষা থেকে কীভাবে আলাদা?
Python-এ একটি ভ্যারিয়েবলের টাইপ রান টাইমে বাইন্ড হয় — একই নেম
xপ্রোগ্রাম চলাকালীন প্রথমে একটি int-এ, পরে একটি string-এ পুনরায় বাইন্ড হতে পারে (M7/L32-এর ডায়নামিক টাইপিং)। C-তে এর বিপরীত — একটি ভ্যারিয়েবলের টাইপ কম্পাইল টাইমে একবার ফিক্সড হয়ে যায় এবং পুরো প্রোগ্রামে অপরিবর্তনীয় থাকে (স্ট্যাটিক টাইপ বাইন্ডিং)। -
পরীক্ষা করুন: উপরের কোড সেলে
bindingsতালিকায় নতুন একটি এন্ট্রি যোগ করুন —Binding("y", "float", "compile_time")— এবং Run চেপে দেখুন sort করা আউটপুটে এটি ঠিক কোথায় বসে।নতুন এন্ট্রি
compile_timeগ্রুপে বসবে — অর্থাৎx -> int (compile_time)এন্ট্রির ঠিক পাশে (sort স্থিতিশীল/stable হওয়ায় একই বাইন্ডিং-টাইম গ্রুপের ভেতরে মূল তালিকার ক্রম বজায় থাকে), এবংlanguage_design_timeএন্ট্রিগুলোর পরে কিন্তুload_timeওrun_timeএন্ট্রিগুলোর আগে প্রদর্শিত হবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ পরের পাঠ — স্ট্যাটিক বনাম ডায়নামিক স্কোপিং — এই বাইন্ডিং-ধারণাকে সরাসরি "কোন ডিক্লারেশন" প্রশ্নে প্রয়োগ করে।
- ক্লোজার ও লেক্সিক্যাল এনভায়রনমেন্ট পাঠ ৪০ এই মডিউলেরই পরের ধাপ — বাইন্ডিং কীভাবে একটি ভ্যারিয়েবলের লাইফটাইমকে তার মূল স্কোপের বাইরেও টিকিয়ে রাখতে পারে।
- Data Structures & Algorithms কোর্স সঙ্গী কোর্স সিম্বল টেবিল বাস্তবায়নে (M6/L28) hash table সরাসরি ব্যবহৃত হয় — এই কোর্সে সেই ভিত্তি শেখানো হয়েছে।