পাঠ ০৯ · ৫৮-এর মধ্যে · মডিউল ২
Home / Courses / Concepts of Programming Languages & Compiler Design / লজিক প্যারাডাইম

লজিক ও ডিক্লারেটিভ প্রোগ্রামিং প্যারাডাইম

Logic & declarative programming paradigm
৮ মিনিট পড়া শুরু · Beginner Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ডিক্লারেটিভ প্যারাডাইমের সংজ্ঞা এবং L06-এর ইম্পারেটিভ "কীভাবে"-এর সাথে সরাসরি বৈসাদৃশ্য
  • লজিক প্রোগ্রামিং-এর মূল উপাদান — ফ্যাক্ট, রুল ও কোয়েরি
  • কীভাবে একটি ইনফারেন্স ইঞ্জিন বিদ্যমান ফ্যাক্ট থেকে নতুন ফ্যাক্ট স্বয়ংক্রিয়ভাবে বের করে
  • Python-এ একটি ছোট ফ্যামিলি-ট্রি উদাহরণে infer_new_facts ফাংশন বাস্তবায়ন ও যাচাই

১ · ডিক্লারেটিভ প্যারাডাইম — "কী", "কীভাবে" নয়

ডিক্লারেটিভ প্রোগ্রামিংDeclarative Programmingপ্রোগ্রাম বর্ণনা করে কাঙ্ক্ষিত ফলাফল কী, ধাপে-ধাপে কীভাবে তা অর্জন করতে হবে তা নয় — আন্ডারলাইং সিস্টেম নিজেই এক্সিকিউশন-কৌশল ঠিক করে। প্রোগ্রামে বর্ণনা করে কী ফলাফল দরকার, কীভাবে ধাপে ধাপে সেটি অর্জন করতে হবে তা বলে দেয় না — আন্ডারলাইং সিস্টেম/ইঞ্জিন নিজেই এক্সিকিউশন-কৌশল ঠিক করে। এটি L06-এর ইম্পারেটিভ প্যারাডাইমের "কীভাবে"-কেন্দ্রিক দর্শনের ঠিক বিপরীত। SQL একটি পরিচিত, প্রায় সব প্রোগ্রামারই কোনো-না-কোনো সময় ব্যবহার করা ডিক্লারেটিভ ভাষার উদাহরণ — একটি SELECT কোয়েরি কাঙ্ক্ষিত রেজাল্ট সেট বর্ণনা করে, কোনো ধাপে-ধাপে রিট্রিভাল অ্যালগরিদম লিখে দেয় না; ডেটাবেস ইঞ্জিন নিজেই ঠিক করে কীভাবে সেই ফলাফল বের করবে।

২ · লজিক প্রোগ্রামিং — ফ্যাক্ট ও রুল

লজিক প্রোগ্রামিংLogic Programmingএকটি ডিক্লারেটিভ প্যারাডাইম যেখানে প্রোগ্রাম ফ্যাক্ট ও রুল দিয়ে গঠিত, এবং একটি কোয়েরি সেই ফ্যাক্ট/রুল সন্তুষ্ট করা মান স্বয়ংক্রিয় ইনফারেন্সের মাধ্যমে খুঁজে বের করে। (যেমন Prolog) একটি নির্দিষ্ট ডিক্লারেটিভ প্যারাডাইম — প্রোগ্রাম গঠিত হয় ফ্যাক্ট (সত্য বলে ঘোষিত বক্তব্য, যেমন ("parent", "alice", "bob")) ও রুল (বিদ্যমান ফ্যাক্ট থেকে কীভাবে নতুন ফ্যাক্ট অনুমান করা যায় তার বর্ণনা) দিয়ে। একটি কোয়েরি তখন সিস্টেমকে জিজ্ঞাসা করে সেই ফ্যাক্ট ও রুল সন্তুষ্ট করে এমন মান খুঁজে বের করতে — একটি স্বয়ংক্রিয় সার্চ/ইনফারেন্স প্রক্রিয়ার (যেমন ব্যাকওয়ার্ড-চেইনিং/ রেজোলিউশন, শুধু নাম উল্লেখ করা হলো, বাস্তবায়ন এই পাঠের আওতার বাইরে) মাধ্যমে — প্রোগ্রামারের নিজের হাতে-লেখা কোনো নির্দিষ্ট অ্যালগরিদম দিয়ে নয়।

Fact ১: parent(alice, bob) Fact ২: parent(bob, carol) Rule: parent(X,Y) ও parent(Y,Z) সত্য হলে ⇒ grandparent(X,Z) ইনফার করো নতুন Fact: grandparent(alice, carol)
প্রোগ্রামার কোথাও "কীভাবে খুঁজবে" লেখেননি — শুধু একটি রুল বর্ণনা করেছেন, ইঞ্জিন নিজেই সব ফ্যাক্ট-জোড়ায় রুলটি প্রয়োগ করে নতুন ফ্যাক্ট বের করেছে।

৩ · কোড: একটি মিনি ইনফারেন্স ইঞ্জিন

নিচের কোডে একটি ছোট ফ্যামিলি-ট্রি ফ্যাক্ট-সেট এবং একটি "grandparent" রুল দেওয়া আছে — infer_new_facts(facts, rule_fn) ফাংশনটি রুলটি সব সম্ভাব্য ফ্যাক্ট-জোড়ার ওপর প্রয়োগ করে এবং নতুন কোনো ফ্যাক্ট বের করা যায় কি না তা রিটার্ন করে। লক্ষ্য করুন প্রোগ্রামার কোথাও লেখেননি "প্রথমে alice-এর সন্তান খোঁজো, তারপর সেই সন্তানের সন্তান খোঁজো" — শুধু সম্পর্কটি কী তা রুল আকারে বর্ণনা করা হয়েছে।

Python
# একটি ছোট ফ্যামিলি-ট্রি ফ্যাক্ট-সেট
facts = [
    ("parent", "alice", "bob"),
    ("parent", "bob", "carol"),
    ("parent", "carol", "dave"),
]

def grandparent_rule(facts):
    """রুল: parent(X,Y) ও parent(Y,Z) দুটোই সত্য হলে grandparent(X,Z) ইনফার করা যায়"""
    new_facts = set()
    parent_facts = [f for f in facts if f[0] == "parent"]
    for (_, x, y1) in parent_facts:
        for (_, y2, z) in parent_facts:
            if y1 == y2:                       # একই মধ্যবর্তী ব্যক্তি (Y) মিলে গেলে
                new_facts.add(("grandparent", x, z))
    return new_facts

def infer_new_facts(facts, rule_fn):
    """রুলটি প্রয়োগ করে, বিদ্যমান ফ্যাক্ট বাদ দিয়ে শুধু সত্যিকারের নতুন ফ্যাক্টগুলো রিটার্ন করে"""
    inferred = rule_fn(facts)
    existing = set(facts)
    return inferred - existing

inferred = infer_new_facts(facts, grandparent_rule)

print("প্রদত্ত facts:")
for f in facts:
    print(" ", f)

print("\nনতুনভাবে ইনফার করা facts:")
for f in sorted(inferred):
    print(" ", f)

    
হাতে-যাচাই: parent(alice, bob) ও parent(bob, carol) — এই দুটোতে মধ্যবর্তী ব্যক্তি "bob" মিলে যায়, তাই grandparent(alice, carol) ইনফার হয়। একইভাবে parent(bob, carol) ও parent(carol, dave)-এ মধ্যবর্তী ব্যক্তি "carol" মিলে যায়, তাই grandparent(bob, dave) ইনফার হয়। মোট দুটো নতুন ফ্যাক্ট — উভয়ই ইনপুট ফ্যাক্ট-সেটের সাথে হাতে মিলিয়ে সঠিক প্রমাণিত।
discrete-math কোর্সের সাথে সম্পর্ক

একটি রুল থেকে নতুন ফ্যাক্ট ইনফার করার প্রক্রিয়া মূলত প্রেডিকেট লজিকের মডাস পোনেন্স-এর মতো একটি ইনফারেন্স নিয়মের প্রয়োগ — Discrete Mathematics কোর্সে লজিক ও প্রুফ টেকনিকের গভীর তাত্ত্বিক ভিত্তি কভার করা হয়েছে; এখানে আমরা সেই তত্ত্বের একটি সরাসরি প্রোগ্রামিং-ল্যাঙ্গুয়েজ প্রয়োগ দেখলাম।

মূল কথা · Key takeaway

লজিক প্রোগ্রামিং-এ প্রোগ্রামার সম্পর্ক ও নিয়ম বর্ণনা করেন, অনুসন্ধান-অ্যালগরিদম লেখেন না। উপরের কোডে infer_new_facts নিজেই "সার্চ ইঞ্জিন"-এর ভূমিকা পালন করেছে — সব সম্ভাব্য জোড়া পরীক্ষা করে দেখেছে রুলটি কোথায় প্রযোজ্য। বাস্তব Prolog ইঞ্জিনগুলো এই ধারণাকেই অনেক বেশি সাধারণীকৃত ও অপ্টিমাইজড রূপে বাস্তবায়ন করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ SQL-এর SELECT কোয়েরিকে কেন ডিক্লারেটিভ বলা হয়, ইম্পারেটিভ নয়?

একটি SELECT কোয়েরি বর্ণনা করে কোন ফলাফল চাই ("এই শর্ত মেলে এমন সব সারি") — কিন্তু কোথাও বলে না ডেটাবেস কীভাবে ডিস্ক থেকে পড়বে, কোন ইনডেক্স ব্যবহার করবে, বা কোন ক্রমে টেবিল স্ক্যান করবে। সেই এক্সিকিউশন-কৌশল সম্পূর্ণভাবে ডেটাবেস ইঞ্জিনের (তার কোয়েরি অপ্টিমাইজারের) সিদ্ধান্ত — একই কোয়েরি বিভিন্ন সময় ভিন্ন এক্সিকিউশন-প্ল্যানেও চলতে পারে, ফলাফল একই থাকা সত্ত্বেও। এই "কী চাই বলো, কীভাবে তা ইঞ্জিনের সিদ্ধান্ত" বৈশিষ্ট্যই একে ডিক্লারেটিভ করে তোলে।

প্র ০২ উপরের কোডে যদি facts-এ আরেকটি এন্ট্রি ("parent", "dave", "eve") যোগ করা হয়, নতুন কোন grandparent ফ্যাক্ট ইনফার হবে?

grandparent(carol, eve) — কারণ এখন parent(carol, dave) ও parent(dave, eve) দুটোই ফ্যাক্ট-সেটে আছে, এবং মধ্যবর্তী ব্যক্তি "dave" দুটোতেই মিলে যায়। বিদ্যমান দুটো ইনফার করা ফ্যাক্ট (grandparent(alice, carol), grandparent(bob, dave)) অপরিবর্তিত থাকবে — মোট তিনটি grandparent ফ্যাক্ট ইনফার হবে। কোডের কোনো লজিক বদলাতে হয়নি, শুধু ইনপুট ফ্যাক্ট-সেট বড় হয়েছে।

প্র ০৩ infer_new_facts ফাংশনে শেষে inferred - existing (সেট সাবট্রাকশন) কেন করা হয়েছে — সরাসরি rule_fn(facts)-এর ফলাফলই কেন রিটার্ন করা হয়নি?

grandparent_rule সব সম্ভাব্য জোড়া পরীক্ষা করে যা ইনফার করা যায় তার সম্পূর্ণ সেট রিটার্ন করে — এর মধ্যে এমন ফ্যাক্টও থাকতে পারে যা ইতিমধ্যে মূল facts তালিকায় সরাসরি দেওয়া ছিল (এই নির্দিষ্ট উদাহরণে ঘটেনি, কিন্তু সাধারণভাবে ঘটতে পারে)। inferred - existing নিশ্চিত করে ফাংশনটি শুধু সত্যিকারের নতুন (আগে থেকে জানা ছিল না এমন) ফ্যাক্ট রিটার্ন করে — একটি বাস্তব ইনফারেন্স ইঞ্জিনের গুরুত্বপূর্ণ বৈশিষ্ট্য, নয়তো পুরোনো ও নতুন জ্ঞানের মধ্যে পার্থক্য বোঝা যেত না।

অনুশীলন

  1. পরীক্ষা করুন: উপরের কোডে facts-এ ("parent", "carol", "dave")-এর একটি ডুপ্লিকেট এন্ট্রি যোগ করে Run চাপুন — ইনফার করা ফলাফলে কোনো পরিবর্তন হয় কি?

    না, কোনো পরিবর্তন হবে না — ইনফার করা ফলাফল একই দুটি ফ্যাক্ট থাকবে (grandparent(alice, carol), grandparent(bob, dave)), কারণ new_facts একটি Python set, এবং সেট স্বভাবতই ডুপ্লিকেট বাদ দেয়। এটি ডেমোনস্ট্রেট করে ইনফারেন্স ইঞ্জিনটি একই ফ্যাক্ট একাধিকবার প্রসেস হলেও নির্ভুল (ইডেমপোটেন্ট) থাকে।

  2. চিন্তা করুন: যদি একটি "great-grandparent" সম্পর্কও ইনফার করতে চান (grandparent + parent), তাহলে grandparent_rule-এর মতো নতুন কী রুল লিখতে হবে (কোড পরিবর্তন করবেন না, শুধু চিন্তা করুন)?

    একটি নতুন রুল ফাংশন দরকার হবে যা ইতিমধ্যে-ইনফার-করা grandparent ফ্যাক্ট এবং মূল parent ফ্যাক্ট — দুটো ভিন্ন ধরনের ফ্যাক্ট — একসাথে দেখে: যদি grandparent(X, Y) এবং parent(Y, Z) দুটোই সত্য হয়, তাহলে great_grandparent(X, Z) ইনফার করা যায়। এটি দেখায় কীভাবে লজিক প্রোগ্রামিং-এ রুলগুলো একে অপরের ইনফার করা ফলাফলের ওপর ভিত্তি করে ধাপে ধাপে জটিলতর সম্পর্ক তৈরি করতে পারে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ M2 প্রোগ্রামিং প্যারাডাইম মডিউলের শেষ পাঠে যান — মাল্টি-প্যারাডাইম ভাষা ও প্যারাডাইম বেছে নেওয়া।
  • আগের পাঠ L08 ফাংশনাল প্রোগ্রামিং প্যারাডাইম — আরেকটি ডিক্লারেটিভ-ঘেঁষা শৈলী, যেখানে "কী গণনা করতে হবে" এক্সপ্রেশন দিয়ে প্রকাশ করা হয়।
  • পরের পাঠ L10 মাল্টি-প্যারাডাইম ল্যাঙ্গুয়েজ ও প্যারাডাইম বেছে নেওয়া — M2-এর চারটি প্যারাডাইম একসাথে দেখে কোন সমস্যায় কোনটি বেছে নেবেন তার সিদ্ধান্ত-কাঠামো।
আগের পাঠ
ফাংশনাল প্রোগ্রামিং প্যারাডাইম