Learn C++ STL

lesson ৬ / ৯ · C থেকে যতটুকু C++ দরকার

Module ১ · C থেকে যতটুকু C++ দরকার

pair আর tuple: দুই বা তার বেশি জিনিস, নাম একটাই

Freeপড়া

এই lesson-এ যা শিখবে

  • একটা std::pair বানাতে পারবে, তার .first আর .second পড়তে পারবে, আর দুইটা pair কোন ক্রমে তুলনা হয় আর সাজে, সেটা বলতে পারবে।
  • একটা std::tuple বানিয়ে std::get আর structured binding দিয়ে পড়তে পারবে, আর একটা function থেকে দুই বা তিনটা মান ফেরত দিতে পারবে।
  • Pair নাকি নাম দেওয়া field-ওয়ালা struct, বেছে নিতে পারবে, আর pair-এর চারটা চেনা ভুলে GCC 12-এর বার্তা পড়তে পারবে।

Zara-র দরকার সপ্তাহের সবচেয়ে ঠান্ডা reading আর সেটা কোন দিন হয়েছিল। C-তে function একটাই মান ফেরত দেয়। তাই ও reading-টা return করত, আর দিনের জন্য একটা pointer পাঠাত, যেভাবে lesson 03-এর exercise reference দিয়ে করেছিল। C++-এ উত্তরটা আরও ছোট: দুইটা মান একটা প্যাকেটে ভরো, আর প্যাকেটটাই return করো। প্যাকেটটার নাম pair, আর STL সব জায়গায় তোমার হাতে pair ধরিয়ে দেয়, প্রতিটা map থেকে শুরু করে দুইটা সংখ্যার প্রতিটা sort পর্যন্ত।

এক function থেকে Zara-র দুইটা উত্তর

C-র পথটা এরকম, দ্বিতীয় উত্তর ফেরত আসে একটা pointer দিয়ে।

#include <stdio.h>

int coldest(const int temps[], int n, int *day)
{
    int best = temps[0];
    *day = 0;
    for (int i = 1; i < n; i++) {
        if (temps[i] < best) {
            best = temps[i];
            *day = i;
        }
    }
    return best;
}

int main(void)
{
    int temps[7] = {4, -2, 1, -6, 0, -6, 3};
    int day = 0;
    int t = coldest(temps, 7, &day);

    printf("coldest %d on day %d\n", t, day + 1);
    return 0;
}
coldest -6 on day 4

আর এই হলো C++, যেখানে function দুইটা উত্তরই ফেরত দেয় একটা std::pair হিসেবে।

#include <iostream>
#include <utility>

std::pair<int, int> coldest(const int temps[], int n)
{
    int best = temps[0];
    int day = 0;
    for (int i = 1; i < n; i++) {
        if (temps[i] < best) {
            best = temps[i];
            day = i;
        }
    }
    return {best, day};
}

int main()
{
    int temps[7] = {4, -2, 1, -6, 0, -6, 3};
    auto [t, day] = coldest(temps, 7);

    std::cout << "coldest " << t << " on day " << day + 1 << '\n';
    return 0;
}
coldest -6 on day 4

Output একই। return {best, day}; মান দুইটা প্যাক করে, আর auto [t, day] = ... খুলে দুইটা নামে রাখে; দুইটাই নিচে বোঝানো হয়েছে। দিন 6-এর -6 জেতে না, কারণ < best-কে বদলায় শুধু আসলেই বেশি ঠান্ডা দিনে। মানে একটা function এখন দুইটা উত্তর দেয়, কোনো pointer ছাড়া, আর call-এর সময় কিছু পাঠাতে মনে রাখার ঝামেলা ছাড়া।

Pair: একটা নামের নিচে দুইটা মান

std::pair একটা নামের নিচে ঠিক দুইটা মান রাখে, দুইটার type আলাদাও হতে পারে। এটা থাকে <utility>-এ। এর দুই অংশের নাম সবসময় .first আর .second, আর এরা সাধারণ variable, পড়াও যায়, লেখাও যায়। এটা একটা class template (lesson 05), দুইটা type দিয়ে ভরা।

Pair বানানো আর পড়ার গড়ন

std::pair<Type1, Type2> p = {value1, value2};
auto q = std::make_pair(value1, value2);
p.first     p.second
  • std::pair<Type1, Type2> হলো দুইটা type দিয়ে ভরা pair, যেমন std::pair<std::string, int>।
  • {value1, value2} এটা ভরে, প্রথম মান প্রথমে।
  • std::make_pair একটা pair বানায় আর type নিয়ে নেয় মান থেকে, auto-র সাথে কাজের।
  • .first আর .second দুই অংশ, পরে কোনো bracket ছাড়া।
#include <iostream>
#include <string>
#include <utility>

int main()
{
    std::pair<std::string, int> city = {"Oslo", -3};
    auto other = std::make_pair(std::string("Cairo"), 31);

    std::cout << city.first << ": " << city.second << '\n';
    city.second = -1;
    std::cout << city.first << " at noon: " << city.second << '\n';
    std::cout << other.first << ": " << other.second << '\n';
    return 0;
}
Oslo: -3
Oslo at noon: -1
Cairo: 31

মানে pair হলো একসাথে চলা দুইটা নাম দেওয়া বাক্স: .first আর .second, যেকোনো variable-এর মতোই পড়া আর বদলানো যায়।

Pair মানে এক নামের নিচে দুইটা বাক্স, tuple মানে n-টা std::pair<std::string, int> city "Oslo" -3 city.first city.second দুই অংশ, নাম সবসময় first আর second std::tuple<std::string, int, double> book "Dune" 1965 4.5 অংশের নম্বর 0 থেকে: std::get<0>, <1>, <2>

দুইটা pair কীভাবে তুলনা হয়, আর কীভাবে সাজে

একই type-এর দুইটা pair তুলনা হয় অভিধান যেভাবে শব্দ তুলনা করে সেভাবে। আগে সিদ্ধান্ত নেয় .first অংশ দুইটা। ওরা সমান হলে তবেই সিদ্ধান্ত নেয় .second অংশ দুইটা। একে বলে lexicographic ক্রম।

#include <iostream>
#include <utility>

int main()
{
    std::pair<int, int> a = {1, 9};
    std::pair<int, int> b = {2, 0};
    std::pair<int, int> c = {2, 5};

    std::cout << (a < b) << ' ' << (b < c) << ' ' << (c == b) << '\n';
    return 0;
}
1 1 0

a < b সত্যি, কারণ 1 ছোট 2-এর চেয়ে, আর 9-এর দিকে তাকানোই হয় না। b < c সত্যি, কারণ first দুইটা 2-এ সমান, তারপর 0 ছোট 5-এর চেয়ে। ==-এর জন্য দুই অংশই সমান হতে হয়।

এই ক্রমের জন্যই pair সুন্দরভাবে সাজে। এখানে একটা sort, আগাম দেখে নাও: std::vector-এর ওপর (Module 2) std::sort (Module 12), vector হলো এমন array যে নিজের size জানে। Kenji-র scoreboard সাজে নাম ধরে, আর কোনো নাম দুইবার থাকলে score ধরে।

#include <algorithm>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

int main()
{
    std::vector<std::pair<std::string, int>> board = {
        {"Zara", 40}, {"Bob", 75}, {"Amara", 60}, {"Bob", 20}
    };

    std::sort(board.begin(), board.end());
    for (const auto& p : board) {
        std::cout << p.first << ' ' << p.second << '\n';
    }
    return 0;
}
Amara 60
Bob 20
Bob 75
Zara 40

দুইজন Bob নামে সমান, তাই score ঠিক করে ক্রম: 20 আগে, 75 পরে। মানে pair তুলনা হয় আগে .first দিয়ে, তারপর .second দিয়ে, আর pair-এর sort বাড়তি কোনো code ছাড়াই ঠিক ওই ক্রমটাই মানে।

tuple: তিন বা তার বেশি, আর std::get

std::tuple হলো এমন pair, যে যতগুলো খুশি মান রাখতে পারে। এটা থাকে <tuple>-এ। এর অংশগুলোর .first-এর মতো কোনো নাম নেই; ওদের নম্বর 0 থেকে, আর std::get<0>(t) পড়ে 0 নম্বর অংশ। নম্বরটা angle bracket-এ যায়, কারণ অংশটার type জানতে compiler-কে compile-এর সময়েই নম্বরটা জানতে হয়।

#include <iostream>
#include <string>
#include <tuple>

int main()
{
    std::tuple<std::string, int, double> book = {"Dune", 1965, 4.5};

    std::cout << std::get<0>(book) << " (" << std::get<1>(book) << "), rated " << std::get<2>(book) << '\n';
    std::get<2>(book) = 4.8;
    std::cout << "new rating " << std::get<2>(book) << '\n';
    return 0;
}
Dune (1965), rated 4.5
new rating 4.8

Tuple-ও pair-এর মতো একই lexicographic নিয়মে তুলনা হয় আর সাজে, আগে 0 নম্বর অংশ। মানে tuple হলো বেশি অংশওয়ালা pair, অংশগুলোর নামের বদলে নম্বর, আর std::get<N> পৌঁছায় N নম্বর অংশে।

Structured binding: প্রতিটা অংশের একটা নাম

std::get<2>(book) বলে না 2 নম্বর অংশটা আসলে কী। C++17 এনেছে structured binding: একটাই declaration, যেটা pair, tuple বা array-র প্রতিটা অংশকে নিজের একটা নাম দেয়। Lesson-এর শুরুতেই একটা দেখেছ।

Structured binding-এর গড়ন

auto [name1, name2, ...] = something_with_parts;
auto& [name1, name2, ...] = something_with_parts;
  • auto [ ... ] একটা copy বানায় আর তার অংশগুলোর নাম দেয়, ক্রম ধরে।
  • auto& [ ... ] নাম দেয় মূলটার অংশগুলোকে, তাই কোনো নামে লিখলে মূলটা বদলায় (আবার lesson 04-এর নিয়ম)।
  • প্রতিটা অংশের জন্য ঠিক একটা করে নাম দিতে হবে।
#include <iostream>
#include <string>
#include <tuple>
#include <utility>

int main()
{
    std::pair<std::string, int> city = {"Oslo", -3};
    std::tuple<std::string, int, double> book = {"Dune", 1965, 4.5};
    int point[2] = {3, 4};

    auto [place, temp] = city;
    auto [title, year, rating] = book;
    auto& [x, y] = point;

    std::cout << place << ' ' << temp << '\n';
    std::cout << title << ' ' << year << ' ' << rating << '\n';
    x = 10;
    std::cout << point[0] << ' ' << point[1] << '\n';
    return 0;
}
Oslo -3
Dune 1965 4.5
10 4

Array-র ওপর auto& binding আসল বাক্সগুলোরই নাম দিয়েছে, তাই x = 10 বদলে দিয়েছে point[0]। Pair-এর ওপর range-for-এ structured binding সবচেয়ে বেশি কাজে আসে: for (const auto& [name, score] : board) পড়তে p.first আর p.second-এর চেয়ে অনেক ভালো। তাই অংশগুলো নাম পাওয়ার যোগ্য হলেই এই track structured binding লেখে, আর সেটা প্রায় সবসময়।

মুহূর্তের জন্য pair, জিনিসের জন্য struct

Pair চটজলদি কাজের, আর সেটাই এর বিপদ। p.first আর p.second বলে না ভিতরে কী আছে, আর এগুলোয় ভরা program পড়তে লাগে ধাঁধার মতো। Amara-র নিয়ম: মুহূর্তের জন্য pair, জিনিসের জন্য struct।

Pair ঠিক তখন, যখন দুইটা মান অল্প সময়ের জন্য একসাথে হয়: একটা function-এর দুইটা উত্তর, sort-এর একটা key, map-এর একটা element। নাম দেওয়া field-ওয়ালা C struct ঠিক তখন, যখন মানগুলো মিলে এমন একটা জিনিস, যেটা তোমার program রেখে দেয় আর যার কথা বলে। একটা parcel-এর ওজন আর দাম আছে, আর parcel.weight সেটা মুখে বলে দেয়।

#include <iostream>
#include <string>
#include <utility>

struct Parcel {
    std::string to;
    int weight;
    int price;
};

std::pair<int, int> splitMinutes(int total)
{
    return {total / 60, total % 60};
}

int main()
{
    Parcel p = {"Kenji", 3, 120};
    std::cout << p.to << ": " << p.weight << " kg, " << p.price << '\n';

    auto [hours, minutes] = splitMinutes(135);
    std::cout << "delivery in " << hours << " h " << minutes << " min\n";
    return 0;
}
Kenji: 3 kg, 120
delivery in 2 h 15 min

Parcel একটা জিনিস, রাখা হয়, print হয়, তাই তার field-গুলোর নাম আছে। ঘণ্টা আর মিনিট বাঁচে এক লাইনের জন্য, তাই pair-ই যথেষ্ট, আর structured binding সাথে সাথে তাদের নাম দিয়ে দেয়। মানে মান দুইটা যখন এক মুহূর্তের উত্তর, তখন pair নাও; আর যখন নাম থাকা একটা জিনিস, তখন struct।

std::tie আর std::ignore

Structured binding সবসময় নতুন নাম বানায়। কখনো কখনো variable-গুলো আগে থেকেই থাকে। <tuple>-এর std::tie আগে থেকে থাকা variable-গুলোর reference দিয়ে একটা tuple বানায়, তাই এতে একটা tuple assign করলে variable-গুলো ভরে যায়। std::ignore বসে সেই অংশের জায়গায়, যেটা তুমি চাও না।

#include <iostream>
#include <string>
#include <tuple>

int main()
{
    std::tuple<std::string, int, double> book = {"Dune", 1965, 4.5};
    std::string title;
    double rating = 0;

    std::tie(title, std::ignore, rating) = book;
    std::cout << title << ' ' << rating << '\n';
    return 0;
}
Dune 4.5

std::tie বেশিরভাগ সময় পড়বে অন্যের code-এ, প্রায়ই std::tie(a.x, a.y) < std::tie(b.x, b.y)-এর মতো তুলনায়, যেটা দুইটা struct-কে field ধরে ধরে তুলনা করে, pair যেভাবে করে। মানে std::tie আগে থেকে থাকা variable ভরে, আর std::ignore একটা অংশ বাদ দেয়।

Example 1: দুই শহরের মধ্যে বেশি গরম কোনটা

David দুইটা শহরের দুপুরের তাপমাত্রা তুলনা করে। প্রতিটা শহর একটা pair, আর উত্তরটাও পুরো একটা pair।

#include <iostream>
#include <string>
#include <utility>

int main()
{
    std::pair<std::string, int> a = {"Lima", 19};
    std::pair<std::string, int> b = {"Perth", 24};

    std::pair<std::string, int> hotter = a;
    if (b.second > a.second) {
        hotter = b;
    }
    std::cout << hotter.first << " is hotter at " << hotter.second << '\n';
    return 0;
}
Perth is hotter at 24

তুলনা দেখে শুধু .second, কারণ এখানে সিদ্ধান্ত নেয় তাপমাত্রা, নাম না। Pair copy করলে দুই অংশ একসাথে copy হয়।

Run in Compiler
Example 2: এক function থেকে তিনটা উত্তর

Alice বিক্রির একটা list-এর সারাংশ চায়: কয়টা, মোট কত, আর গড় কত। একটা tuple তিনটাই ফেরত আনে, আর structured binding ওদের নাম দেয়।

#include <iostream>
#include <tuple>

std::tuple<int, long long, double> summary(const int sales[], int n)
{
    long long total = 0;
    for (int i = 0; i < n; i++) {
        total += sales[i];
    }
    return {n, total, (double)total / n};
}

int main()
{
    int sales[5] = {120, 80, 45, 300, 55};
    auto [count, total, average] = summary(sales, 5);

    std::cout << count << " sales, total " << total << ", average " << average << '\n';
    return 0;
}
5 sales, total 600, average 120

Return type তিনটা type-এর নাম বলে ক্রম ধরে, আর return {n, total, ...}; সেই ক্রমেই ভরে। (double) cast গড়ের ভগ্নাংশটা রেখে দেয়, C-র মতোই।

Run in Compiler
Example 3: গেটের সবচেয়ে কাছের দোকান

Maria একটা বাজারের নকশা করছে। প্রতিটা দোকানের একটা নাম আর একটা grid-এ একটা জায়গা আছে, আর ও চায় (0, 0)-এর গেটের সবচেয়ে কাছের দোকান। দুইটা দোকান সমান কাছে হলে, ও চায় সেটা, যার নাম অভিধানে আগে আসে। (দূরত্ব, নাম)-এর একটা pair পুরো নিয়মটাই ধরে রাখে, কারণ pair আগে দূরত্ব তুলনা করে, তারপর নাম।

#include <algorithm>
#include <iostream>
#include <string>
#include <utility>

int main()
{
    int n = 0;
    std::cin >> n;

    std::pair<int, std::string> best = {0, ""};
    for (int i = 0; i < n; i++) {
        std::string name;
        int x = 0;
        int y = 0;
        std::cin >> name >> x >> y;

        std::pair<int, std::string> here = {x * x + y * y, name};
        if (i == 0) {
            best = here;
        } else {
            best = std::min(best, here);
        }
    }
    auto [dist2, name] = best;
    std::cout << name << " (squared distance " << dist2 << ")\n";
    return 0;
}
fruit (squared distance 25)

এই output input 4, তারপর tea 6 1, spice 5 0, fruit 3 4 আর bread -2 6-এর জন্য, প্রতি লাইনে একটা দোকান। Spice আর fruit দুইটাই বর্গ করা দূরত্বে 25, তাই নাম সিদ্ধান্ত নেয়, আর "fruit" আসে "spice"-এর আগে। বর্গ করা দূরত্ব তুলনা করলে বর্গমূল আর তার rounding এড়ানো যায়। দুইটা pair-এ std::min হলো lesson 05-এর template, একটা pair type দিয়ে ভরা।

Run in Compiler

এটা কোথায় কাজে লাগে

  • প্রতিটা std::map। Map-এর element হলো const Key আর value-র pair, তাই map-এর ওপর প্রতিটা loop পড়ে .first আর .second, বা একটা structured binding (Module 9)।
  • হ্যাঁ-না সহ উত্তর। std::map::insert একটা pair ফেরত দেয়: element-টা কোথায়, আর একটা bool, যেটা বলে সেটা নতুন কি না (Module 9)।
  • Contest-এর graph। Edge list হলো যুক্ত node-গুলোর একটা std::vector<std::pair<int, int>>, আর Dijkstra-র queue রাখে (দূরত্ব, node) pair, আগে দূরত্ব ধরে সাজানো (Module 16)।
  • এক algorithm-এর দুইটা ফল। std::minmax_element এক পাকেই সবচেয়ে ছোট আর বড়টা খুঁজে একটা pair হিসেবে ফেরত দেয় (Module 14)।

যে ভুলগুলো সবাই করে

১. .first-এর পরে bracket।

std::pair<int, int> p = {3, 4};
std::cout << p.first() << '\n';

প্রতিটা command line-এ error: error: expression cannot be used as a function। .first একটা variable, function না, তাই এর পরে bracket বসে না। তুমি bracket দেবে, কারণ STL-এ dot-এর পরের বেশিরভাগ জিনিসই function, যেমন .size()।

২. পুরো একটা pair print করা।

std::cout << p << '\n';

প্রতিটা command line-এ error, Playground-এর flag-এ Compiler Explorer-এ 200-এর বেশি লাইন জুড়ে। পড়ার মতো প্রথম লাইনটা: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'std::pair<int, int>')। std::cout জানে না pair কীভাবে print করতে হয়, তাই অংশগুলো print করো: p.first << ' ' << p.second।

৩. Tuple-এর এমন অংশ, যেটা নেই।

std::tuple<std::string, int, double> t = {"Zara", 3, 2.5};
std::cout << std::get<3>(t) << '\n';

প্রতিটা command line-এ error, ওঠে library-র ভিতরে: error: static assertion failed: tuple index must be in range, সাথে note the comparison reduces to '(3 < 3)'। তিনটা অংশের নম্বর 0, 1 আর 2, ঠিক array index-এর মতো। Bob-এর off-by-one, program চলার আগেই ধরা পড়ল।

৪. Structured binding-এ ভুল সংখ্যক নাম।

auto [name, count] = t;

প্রতিটা command line-এ error: error: only 2 names provided for structured binding, সাথে note যে tuple-টা decomposes into 3 elements। প্রতিটা অংশের জন্য একটা নাম দাও, যে অংশ ব্যবহার করবে না তার জন্যও।

মাথা খাটাও

Kenji তিনটা তুলনা print করে।

#include <iostream>
#include <string>
#include <utility>

int main()
{
    std::pair<int, int> a = {1, 2};
    std::pair<int, int> b = {1, 3};
    std::pair<std::string, int> c = {"Zara", 1};
    std::pair<std::string, int> d = {"apple", 9};

    std::cout << (a < b) << ' ' << (b < a) << ' ' << (c < d) << '\n';
    return 0;
}

চালানোর আগে তিনটা অঙ্কই আন্দাজ করো। প্রতিটার জন্য বলো, pair-এর কোন অংশ সিদ্ধান্ত নিয়েছে, আর অন্য অংশটার দিকে কেন তাকানোই হয়নি।

আগে first-গুলো তুলনা করো। শুধু সমান হলে second-গুলো। String-এর বেলায় মনে করো, ASCII table-এ বড় হাতের অক্ষর কোথায় বসে।

অনুশীলন ১সহজ

Amara একটা class test-এর খাতা দেখে, আর দেখতে দেখতে প্রতিটা ছাত্রের নাম আর নম্বর লিখে রাখে। সবচেয়ে বেশি নম্বর পেলে একটা বই পুরস্কার। দুইজনের নম্বর সমান সবচেয়ে বেশি হলে বই পায় সে, যার খাতা ও আগে দেখেছে। এখন পর্যন্ত জেতাকে রাখো std::pair<std::string, int> best-এ, starter যেমন রেখেছে।

Input. প্রথম লাইনে n। পরের n-টা লাইনের প্রতিটায় একটা নাম আর একটা নম্বর।

Output. এক লাইন: বিজয়ীর নাম আর নম্বর, মাঝে একটা space।

Constraints. 1 <= n <= 10000। নাম 1 থেকে 20টা ছোট হাতের অক্ষর। নম্বর 0 থেকে 100-এর একটা integer।

Sample. Input 4, তারপর alice 82, bob 91, zara 91 আর kenji 75 দিলে আসে bob 91।

#include <iostream>
#include <string>
#include <utility>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n = 0;
    std::cin >> n;

    std::pair<std::string, int> best;

    // Read n lines, each a name and a mark. Keep the best one in best:
    // the highest mark, and on a tie the one that came first.
    // Then print best's name and mark, separated by one space.

    return 0;
}

best-mark নামে গ্রেড হয়। Hidden test-এ আছে n = 1, আর এমন একটা class যেখানে সবার নম্বর 0। আরেকটায় সবচেয়ে বেশি নম্বরে সমতা আছে, যেটা প্রথম লাইনে না। Zara সবার আগে চালাত সব-শূন্যের class-টা: প্রথম read-এর আগে best-এ কী থাকে, ভেবে দেখো।

Run in Compiler
অনুশীলন ২সহজ

Bob বন্ধুদের মধ্যে চকলেট ভাগ করে। std::pair<int, int> share(int sweets, int friends) লেখো, যেটা ফেরত দেয় প্রতিটা বন্ধু কয়টা পায় আর কয়টা বেঁচে যায়, আর input-এর প্রতিটা লাইনে এটা call করো।

Input. প্রথম লাইনে t। পরের t-টা লাইনের প্রতিটায় দুইটা integer, চকলেট আর বন্ধুর সংখ্যা।

Output. t-টা লাইন, প্রতিটা each E, left L।

Constraints. 1 <= t <= 100। 0 <= sweets <= 1000000000 আর 1 <= friends <= 1000।

Sample. Input 2, তারপর 17 5 আর 9 3 দিলে আসে each 3, left 2 আর each 3, left 0।

#include <iostream>
#include <utility>

// Return {sweets each friend gets, sweets left over}.
std::pair<int, int> share(int sweets, int friends)
{
    return {0, 0}; // replace this line
}

int main()
{
    int t = 0;
    std::cin >> t;
    for (int i = 0; i < t; i++) {
        int sweets = 0;
        int friends = 0;
        std::cin >> sweets >> friends;

        // Call share, name the two answers with a structured binding,
        // and print them as: each E, left L
    }
    return 0;
}

আলাদা করে গ্রেড হয় না। Zara দেখে নিত 0টা চকলেট, আর চকলেটের চেয়ে বেশি বন্ধু।

Run in Compiler
অনুশীলন ৩সহজ

Alice একটা বই বদলের আসর চালায়। প্রতিটা বইয়ের একটা তাকের নম্বর আর সেই তাকে একটা ঘরের নম্বর আছে। ও তালিকাটা চায় সেই ক্রমে, যে ক্রমে একজন হেঁটে হেঁটে বইগুলো পেরিয়ে যায়: তাক ধরে ধরে, আর একই তাকের ভিতরে ঘর ধরে ধরে। Starter pair-গুলো পড়ে রাখে একটা std::vector<std::pair<int, int>>-এ, এমন array যে নিজের size জানে।

Input. প্রথম লাইনে n। পরের n-টা লাইনের প্রতিটায় একটা তাক আর একটা ঘর।

Output. n-টা লাইন, প্রতিটায় একটা তাক আর একটা ঘর, মাঝে একটা space, তাক ধরে সাজানো, আর একই তাকে ঘর ধরে। একই pair যতবার আছে, ততবার print হবে।

Constraints. 1 <= n <= 40000। প্রতিটা integer -1000000000 থেকে 1000000000-এর মধ্যে।

Sample. Input 4, তারপর 3 1, 1 5, 3 0 আর 1 2 দিলে চার লাইনে আসে 1 2, 1 5, 3 0 আর 3 1।

#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n = 0;
    std::cin >> n;

    // A vector is an array that knows its own size (Module 2 teaches it).
    std::vector<std::pair<int, int>> v(n);
    for (auto& p : v) {
        std::cin >> p.first >> p.second;
    }

    // Sort v by first, and by second when the firsts are equal.
    // Then print one pair per line: first, one space, second.

    return 0;
}

sort-pairs নামে গ্রেড হয়। Hidden test-এ আছে n = 1, ছয়টা হুবহু এক pair, এমন একটা তাক যার ঘরগুলো উল্টো ক্রমে আসে, আর সীমার কিনারায় 40000টা pair।

Run in Compiler
অনুশীলন ৪মাঝারি

David একটা cold store-এর তাপমাত্রার log দিনে কয়েকবার দেখে। প্রতিবারের দেখা হলো reading-এর একটা ছোট list, আর report-এ লাগে প্রতিটা list-এর সবচেয়ে ঠান্ডা আর সবচেয়ে গরমটা। Starter declare করেছে std::pair<int, int> minMax(const int a[], int k)। এটা দিয়ে সবচেয়ে ছোট আর বড়টা একটা pair হিসেবে ফেরত দাও, আর main থেকে দুইটাই print করো।

Input. প্রথম লাইনে t। পরের t-টা লাইনের প্রতিটা শুরু হয় k দিয়ে, তারপর k-টা integer।

Output. t-টা লাইন, প্রতিটায় তার list-এর সবচেয়ে ছোট আর বড়টা, মাঝে একটা space।

Constraints. 1 <= t <= 1000 আর 1 <= k <= 1000। সব list মিলিয়ে বড়জোর 80000টা integer। প্রতিটা integer -1000000000 থেকে 1000000000-এর মধ্যে।

Sample. Input 3, তারপর 3 4 -2 7, 1 5 আর 4 2 2 2 2 দিলে তিন লাইনে আসে -2 7, 5 5 আর 2 2।

#include <iostream>
#include <utility>

const int MAX_K = 1000;

// Return the smallest and the largest of a[0] to a[k - 1], in that order.
std::pair<int, int> minMax(const int a[], int k)
{
    return {0, 0}; // replace this line
}

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int t = 0;
    std::cin >> t;

    int a[MAX_K];
    for (int i = 0; i < t; i++) {
        int k = 0;
        std::cin >> k;
        for (int j = 0; j < k; j++) {
            std::cin >> a[j];
        }

        // Call minMax and print the smallest and the largest,
        // separated by one space.
    }
    return 0;
}

min-max-pair নামে গ্রেড হয়। Hidden test-এ আছে এক reading-এর list, শুধু ঋণাত্মক reading-এর list, আর শুধু বড় ধনাত্মক reading-এর list। একটায় আছে সীমার কিনারায় 1000টা করে 80টা list। সবচেয়ে ছোট বা বড়টা 0 থেকে শুরু করলে এর দুইটায় ফেল।

Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • Pair কি দুইটা আলাদা variable-এর চেয়ে ধীর?

    না। Pair হলো দুই field-এর একটা সাধারণ struct, memory-তে সাজানো যেকোনো C struct-এর মতো। -O2-এ compiler এটার অংশ দুইটাকে দুইটা variable-এর মতোই দেখে।

  • Struct-এর বদলে tuple কখন নেব?

    কমই, আর অল্প সময়ের জন্য। Tuple-এর অংশগুলোর নম্বর থাকে, তাই std::get<2> পাঠককে কিছুই বলে না। ছোট একটা function থেকে tuple ফেরত দাও, আর সাথে সাথে structured binding দিয়ে অংশগুলোর নাম দাও; যা রেখে দেবে, তার জন্য struct লেখো।

  • Structured binding-এর নামগুলো কি copy?

    auto [a, b]-এ পুরো pair একবার copy হয়, আর নামগুলো সেই copy-র অংশ বোঝায়। auto& [a, b] বা const auto& [a, b]-এ নামগুলো বোঝায় মূলটাকে, lesson 04-এর সেই একই নিয়ম।

  • Pair-এর ভিতরে আরেকটা pair রাখা যায়?

    যায়: std::pair<std::string, std::pair<int, int>> হলো একটা নাম আর একটা জায়গা, পড়তে হয় p.second.first দিয়ে। চলে ঠিকই, আর ঠিক এরকম ধাঁধার code নিয়েই pair-নাকি-struct নিয়মটা সাবধান করে।

মূল কথা

  • std::pair এক নামের নিচে দুইটা মান রাখে; অংশ দুইটা .first আর .second, কোনো bracket ছাড়া।
  • Pair আর tuple তুলনা হয় lexicographic ক্রমে: প্রথম অংশ সিদ্ধান্ত নেয়, পরের অংশ শুধু সমতা ভাঙে।
  • std::tuple যতগুলো খুশি মান রাখে, নম্বর 0 থেকে, পড়া হয় std::get<N> দিয়ে।
  • auto [a, b] = p; একবারেই প্রতিটা অংশের নাম দেয়, আর auto& নাম দেয় মূলটার অংশগুলোকে।
  • মুহূর্তের জন্য pair, যেমন একটা function-এর দুইটা উত্তর; একটা জিনিসের জন্য নাম দেওয়া field-ওয়ালা struct।

এরপর lesson 07 বোঝাবে এই module-এর প্রতিটা নামের সামনের std::, আর STL-এর কোন অংশ কোন header-এ থাকে।

lesson ৬ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: std::, namespace আর যে header-গুলো সত্যিই include করবে

pair আর tuple: দুই বা তার বেশি জিনিস, নাম একটাই | Learn C++ STL | Progsity