ট্র্যাক
/
C++
C++
/
অনুশীলনী
/
ডক্টর ডেটা
ডক্টর ডেটা

ডক্টর ডেটা

লার্নিং অনুশীলনী

ভূমিকা

C++-এ ডিক্লারেশন প্রায়ই ডেফিনিশন থেকে আলাদা থাকে। ডিক্লারেশনগুলো তথাকথিত হেডার ফাইলে দলবদ্ধ করা হয়, আর সংশ্লিষ্ট ইমপ্লিমেন্টেশনগুলো সোর্স ফাইলে রাখা হয়। হেডার ফাইলগুলোকে আপনি একটি API হিসেবে ভাবতে পারেন। হেডার ফাইলটি একটি কোডবেসে কী আছে তা বলে দেবে, কিন্তু কীভাবে তার বিস্তারিত না জানিয়েই।

হেডার আর সোর্স

হেডার ফাইলের সবচেয়ে কমন ফাইল এক্সটেনশন হলো .h। কিছু প্রজেক্ট .hpp ব্যবহার করে, বা এক্সটেনশন একেবারেই বাদ দেয়।

ডেফিনিশনগুলো আলাদা একটি .cpp ফাইলে থাকে। অংশগুলো আবার একত্র করতে, সোর্স ফাইলটি শুরু হয় সংশ্লিষ্ট হেডার ফাইলটি ইনক্লুড করে।

আপনি যদি "quick_math" নামের একটি লাইব্রেরি লিখতে চান, যেটি "super_root" নামের একটি ফাংশন দেয় যা আপনি প্রায়ই ব্যবহার করতে চান, তাহলে ফাইলগুলো দেখতে এমন হবে:

// A file named quick_math.h
#pragma once
namespace quick_math {
    double super_root(double x, int n);
}
// A file named quick_math.cpp
#include "quick_math.h"
#include <cmath>
double quick_math::super_root(double x, int n) {
    while(n) { x = std::sqrt(x), --n;}
    return x;
}

আপনার যদি এমন একটি হেডার ইনক্লুড করার দরকার হয় যা কেবল ইমপ্লিমেন্টেশনের জন্যই দরকার, তাহলে সংশ্লিষ্ট #include লাইনটি কেবল সোর্স ফাইলেই লাগে। হেডারে যা কিছু ইনক্লুড করা হয়, তা .cpp ফাইলেও পাওয়া যায়, যেমন নিচের উদাহরণে string লাইব্রেরি। খেয়াল রাখুন: হেডার ফাইলে ডিক্লারেশনের পরে ; দরকার, কিন্তু সোর্স ফাইলে ডেফিনিশনের পরে নয়।

Note

Exercism-এ অনেক C++ অনুশীলনী শুরু হয় দুটি প্রায় খালি ফাইল দিয়ে: হেডার আর সোর্স। অনুশীলনীটি সমাধান করার জন্য আপনাকে *_test.cpp ফাইলটি দেখে প্রত্যাশিত ফাংশনগুলোর নাম আর নেমস্পেস জেনে নিতে হবে।

ক্লাস আর হেডার

ক্লাস খুব জটিল হয়ে উঠতে পারে, আর হেডার / সোর্স ভাগাভাগির সাথে তাদের সম্পর্ক বিভ্রান্তিকর মনে হতে পারে। একটি সম্ভাব্য বিন্যাস হলো সব ইমপ্লিমেন্টেশনের বিস্তারিত সোর্স ফাইলে রাখা আর সব ডিক্লারেশন ও মেম্বার ভ্যারিয়েবল হেডারে রাখা:

// A file named robot_flower.h
#if !defined(ROBOT_FLOWER_H)
#define ROBOT_FLOWER_H
#include <string>
namespace robots {
    class Flower {
        private:
            bool needs_water{};
            int size{};
            std::string name{};
        public:
            Flower(std::string name, int size = 0);
            void give_water();
            std::string get_name();
            int get_size();
            void start_next_day();
    };
}
#endif
// A file named robot_flower.cpp
#include "robot_flower.h"
robots::Flower::Flower(std::string name, int size) {this->name = "Robotica " + name; this->size = size;}
void robots::Flower::start_next_day() {if (!needs_water) ++size; needs_water = true;}
std::string robots::Flower::get_name() {return name;}
int robots::Flower::get_size() {return size;}

হেডারকে যখন একটি API ওভারভিউ হিসেবে ব্যবহার করা হয়, তখন ডিফল্ট ভ্যালুর মতো তথ্য মানুষ খুঁজতে সেখানেই তাকায়। তাই কনস্ট্রাক্টরের size প্যারামিটারের ডিফল্ট হেডারে ঠিক করা হয়, ইমপ্লিমেন্টেশনে নয়। সোর্স ফাইলের ডেফিনিশনগুলোর সামনে নেমস্পেস robots আর ক্লাস টাইপ Flower বসানো হয়।

আরেকটি বিন্যাসের বিকল্প হলো হেডার-অনলি লাইব্রেরি, যার .cpp ফাইল একেবারেই থাকে না:

// A file named robot_flower.h
#pragma once
#include <string>
namespace robots {
    class Flower {
        private:
            bool needs_water{};
            int size{};
            std::string name{};
        public:
            Flower(std::string name, int size = 0) {this->name = "Robotica " + name; this->size = size;}
            void give_water() {needs_water = false;}
            std::string get_name() {return name;}
            int get_size() {return size;}
            void start_next_day() {if (!needs_water) ++size; needs_water = true;}
    };
}

প্রজেক্টগুলো এই বিন্যাসগুলোর সংমিশ্রণ ব্যবহার করতে পারে, আর কোন ব্যবহারের জন্য কোনটি সবচেয়ে ভালো মানায় তা নিয়ে অনেক আলোচনা আছে।

ইনক্লুড গার্ড

উপরের উদাহরণ হেডার ফাইলে #pragma once লাইনটি আপনি হয়তো খেয়াল করেছেন। এটাকে ইনক্লুড গার্ড বলা হয়, আর এটি নিশ্চিত করে যে এরর এড়াতে কম্পাইলেশনের সময় ফাইলের কনটেন্ট কেবল একবারই ইনক্লুড হয়। ইনক্লুড গার্ডের আরেকটি, আরও জটিল রূপ আছে, যেটি #ifndef দিয়ে শুরু হয় আর #endif দিয়ে শেষ হয়। এটি একই উদ্দেশ্য পূরণ করে, আর এর ব্যবহার উপরের Flower ক্লাসের উদাহরণে দেখানো হয়েছে।

নির্দেশনা

তথ্য উদ্ধারে বিশেষজ্ঞ "Doctor Data" নামের একটি অস্ট্রেলীয় কোম্পানিতে আপনার প্রথম কর্মদিবস শুরু হলো। C++ এবং ভন নিউম্যান প্রোব সম্পর্কে আপনার জ্ঞানের জোরে আপনি চাকরির ইন্টারভিউয়ে সফল হয়েছেন। আপনি ইতিমধ্যে অনেক টেস্ট ফাইল দেখেছেন, তাই আপনার নতুন বস চান আপনি সম্প্রতি কোম্পানির উদ্ধার করা কিছু টেস্ট কোড থেকে সংশ্লিষ্ট সোর্স ও হেডার ফাইলগুলো আবার তৈরি করুন।

এই অনুশীলনীতে আপনি হারিয়ে যাওয়া ফাইল আবার তৈরি করবেন। এই মুহূর্তে ফাইলগুলোর ভেতরের বিষয়বস্তু অচল আবর্জনা ডেটা ছাড়া কিছু নয়।

Note

এই কনসেপ্ট অনুশীলনীর কাজের ধারা Exercism-এর প্র্যাকটিস অনুশীলনীগুলোর গঠনের সাথে খুবই মিল। অনুশীলনীর ভূমিকা লেখাটি স্পেসিফিকেশনের কেবল একটি অংশ। নির্দিষ্ট কোনো সমস্যা সমাধানে টেস্ট ফাইলই আপনার চূড়ান্ত পথনির্দেশ। C++ কম্পাইল যেভাবে কাজ করে, তার কারণে টেস্টে যেসব ক্লাস, ফাংশন ও এনামারেশন দরকার, সেগুলোর ন্যূনতম একটি সংস্করণ আপনি তৈরি না করা পর্যন্ত ওয়েব ইন্টারফেসে টেস্টের ফলাফল নাও দেখা যেতে পারে। আপনি যদি কম্পাইল এরর পান, সব টাস্ক ও টেস্ট শেষ করলে সেগুলো এমনিতেই মিলিয়ে যেতে পারে।

আপনার সামনে চারটি টাস্ক, সবগুলোই হেডার ও সোর্স ফাইলের হারানো কোড উদ্ধার করার সাথে সম্পর্কিত।

1. প্রথম কিছু সূত্র বের করার চেষ্টা করুন

আপনি টেস্ট ফাইল থেকে উদ্ধার হওয়া দুটি লাইন দেখেন:

heaven::Vessel bob{"Robert Johansson", 1};
heaven::Vessel will{"Riker", 2, star_map::System::BetaHydri};

আপনার তীক্ষ্ণ চোখ সঙ্গে সঙ্গে heaven নামের একটি নেমস্পেস চিনে ফেলে। আপনি এটাও দেখেন যে Vessel নামের একটি ক্লাস থাকতে হবে। কনস্ট্রাক্টরটিকে আপাতদৃষ্টিতে দুই বা তিনটি আর্গুমেন্ট দিয়ে কল করা যায়। প্রথম আর্গুমেন্টটি string টাইপের মনে হচ্ছে, দ্বিতীয়টি একটি সংখ্যা। তৃতীয় একটি আর্গুমেন্ট দিয়ে Vessel ক্লাস ইনিশিয়ালাইজ করা সম্ভব। তৃতীয় আর্গুমেন্টটি আসে star_map নেমস্পেস থেকে। এটি System টাইপের একটি এনামারেটর। এমনকি এই এনামারেশনগুলোর একটি আপনি পেয়েও গেছেন: BetaHydri.

আপনার আবিষ্কৃত তথ্য দিয়ে সোর্স ও হেডার ফাইলগুলো তৈরি করুন। আপনার দুটি নেমস্পেস দরকার: heaven এবং star_map। heaven নেমস্পেসে Vessel নামের একটি ক্লাস আছে, যেটিকে দুই বা তিনটি আর্গুমেন্ট দিয়ে কল করা যায়। System এনামটি star_map নেমস্পেসে রাখতে হবে এবং এতে একটি BetaHydri এনামারেশন থাকবে। অন্য এনামারেশনগুলোর দিকে খেয়াল রাখুন, যাতে System এনামটি আপনি নিয়মিত হালনাগাদ করতে পারেন।

2. আরও বিস্তারিত খুঁজে বের করুন

আপনি টেস্ট থেকে আরও কিছু লাইন উদ্ধার করেন:

heaven::Vessel bob{"Robert Johansson", 1};
REQUIRE(bob.current_system == star_map::System::Sol);
REQUIRE(bob.generation == 1);
heaven::Vessel bob5 = bob.replicate("Mario");
REQUIRE(bob5.current_system == star_map::System::Sol);
REQUIRE(bob5.generation == 2);

সদ্য পাওয়া টেস্ট লাইনগুলো Vessel ক্লাসের আরেকটি মেম্বার ফাংশনের হদিস দেয়: replicate। এই ফাংশনটি একটি স্ট্রিং নেয় এবং আরেকটি Vessel ইনস্ট্যান্স রিটার্ন করে। আগের টাস্ক থেকেই কনস্ট্রাক্টরের তৃতীয় আর্গুমেন্টের ডিফল্ট মান সম্পর্কে একটি ধারণা আপনি পেয়ে যান। এছাড়া Vessel ক্লাসের দুটি পাবলিক মেম্বার ভ্যারিয়েবল এবং replicate মেম্বার ফাংশনের স্পেসিফিকেশনও আপনি দেখতে পান।

হেডার ও সোর্স ফাইলে replicate ফাংশনটি এবং current_system ও generation পাবলিক মেম্বার ভ্যারিয়েবল দুটি যোগ করুন।

3. ভেতরের কার্যপ্রণালী দেখে নিন

উদ্ধার হওয়া টেস্ট ফাইলগুলোতে আপনি আরও কিছু মজার লাইন খুঁজে পান:

heaven::Vessel bob6{"Homer", 3, star_map::System::EpsilonEridani};
REQUIRE(bob6.busters == 0);
bob6.make_buster();
REQUIRE(bob6.busters == 1);
bool success = bob6.shoot_buster();
REQUIRE(success);
REQUIRE(bob6.busters == 0);
success = bob6.shoot_buster();
REQUIRE_FALSE(success);

আপাতদৃষ্টিতে Vessel ক্লাসে busters নামের একটি মেম্বার ভ্যারিয়েবল আছে, যেটি make_buster এবং shoot_buster এই দুটি ক্লাস মেম্বার ফাংশন দিয়ে বদলানো যায়। অন্য কোনো তথ্য প্রকাশ না পাওয়া পর্যন্ত আপনি ধরে নেন যে make_buster ফাংশনটি void রিটার্ন করে। যেহেতু shoot_buster-এর রিটার্ন ভ্যালুর জন্য একটি টেস্ট আছে, তাই আপনি ধরে নেন ফাংশনটি bool রিটার্ন করে।

Vessel ক্লাসে দুটি ফাংশন এবং মেম্বার ভ্যারিয়েবলটি যোগ করুন। অন্য System এনামারেটরগুলোর খোঁজ চালিয়ে যান।

4. ছবিটা সম্পূর্ণ করুন

টেস্ট ফাইলগুলো খুঁটিয়ে দেখতে দেখতে আপনি কোডের কেবল দুটি অনাবিষ্কৃত অংশ পান:

heaven::Vessel bob1{"Bob", 1, star_map::System::AlphaCentauri};
heaven::Vessel marv{"Marvin", 2, star_map::System::DeltaEridani};
heaven::Vessel milo{"Milo", 3, star_map::System::DeltaEridani};
heaven::Vessel howie{"Howard", 4, star_map::System::Omicron2Eridani};

REQUIRE("Bob" == heaven::get_older_bob(bob1, marv));
REQUIRE(heaven::in_the_same_system(marv, milo));
REQUIRE_FALSE(heaven::in_the_same_system(marv, howie));

আপনি দুটি ফাংশন দেখেন, যেগুলো Vessel ক্লাসের মেম্বার নয়, কারণ এগুলো কোনো ইনস্ট্যান্স দিয়ে কল করা হয় না। get_older_bob দুটি Vessel ইনস্ট্যান্স তুলনা করে এবং একটি string রিটার্ন করে। in_the_same_system দুটি Vessel ইনস্ট্যান্স তুলনা করে এবং একটি bool রিটার্ন করে।

উপরের উদ্ধার হওয়া লাইনগুলো থেকে বাকি থাকা শেষ ফাংশনগুলো তৈরি করুন।

GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি একটি নতুন উইন্ডো বা ট্যাবে খোলে
C++ Exercism

ডক্টর ডেটা শুরু করতে প্রস্তুত?

Exercism-এ সাইন আপ করুন, C++ ট্র্যাকের 19টি কনসেপ্ট100টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।