सामग्री पर जाएँ
Topos Uranos

सारांश

बहुत-से डेटा एक पंक्ति नहीं, बल्कि एक चौखाना ढाँचा बनाते हैं: किसी कक्षा के अंक, हर विद्यार्थी और हर परीक्षा के अनुसार, एक खेल का बोर्ड, एक आव्यूह। यह पाठ तालिका, यानी दो विमाओं वाली संरचना, को C++ में उसके दो रूपों में प्रस्तुत करता है: वेक्टरों का वेक्टर, जिसका आकार चलाते समय तय होता है, और ऐरों का ऐरे, जिसका आकार कंपाइल करते समय ज्ञात होता है। इसके बाद पंक्तियों और स्तंभों के अनुसार ट्रैवर्सल आते हैं; आव्यूहों का परिवर्त और गुणनफल, जिसकी लागत त्रिघाती है; किसी खाने के पड़ोसी, जो किनारों का ध्यान रखने को बाध्य करते हैं; और इंडेक्स i * columns + j के साथ एक ही वेक्टर में रखी गई तालिका, जो वही रूप है जिसमें मेमोरी उसे देखती है।

अधिगम उद्देश्य

  1. तालिका को std::vector<std::vector<T>> के रूप में या std::array के std::array के रूप में निरूपित करना, और आकार कंपाइल करते समय ज्ञात है या नहीं, इसके अनुसार दोनों में से चुनना।
  2. तालिका को पंक्तियों और स्तंभों के अनुसार ट्रैवर्स करना, और हर पंक्ति और हर स्तंभ के योग और माध्य निकालना।
  3. तालिकाओं की संक्रियाएँ (परिवर्त, आव्यूहों का गुणनफल, किसी खाने के पड़ोसियों की गिनती) इस सावधानी के साथ लागू करना कि कोई इंडेक्स किनारों से बाहर न जाए।
  4. तालिका को इंडेक्स i * columns + j के साथ एक फ़्लैट वेक्टर में निरूपित करना, और खाने और उसकी स्थिति के बीच की संगति को एक उदाहरण से समझाना।

तालिकाएँ

पंक्तियाँ और स्तंभ

mm पंक्तियों और nn स्तंभों वाली तालिका एक ही प्रकार के m⋅nm \cdot n डेटा का संग्रह है, जिनमें से हर एक दो इंडेक्सों से निर्दिष्ट होता है: अपनी पंक्ति का इंडेक्स, ii, जहाँ 0≤i<m0 \leq i < m, और अपने स्तंभ का इंडेक्स, jj, जहाँ 0≤j<n0 \leq j < n। गणित इसे आव्यूह कहता है और aija_{ij} लिखता है; बीजगणित और ज्यामिति II पाठ्यक्रम आव्यूहों और उनकी संक्रियाओं का अध्ययन करता है, और यह पाठ उनमें से कुछ को प्रोग्रामों के रूप में लिखता है। इंडेक्सों का क्रम कोई अपवाद नहीं मानता: पहले पंक्ति, फिर स्तंभ। इन्हें आपस में उलझा देना तालिकाओं के साथ सबसे आम त्रुटि है, और कंपाइलर इसकी चेतावनी नहीं दे सकता, क्योंकि दोनों इंडेक्स एक ही प्रकार के हैं।

पाठ वेक्टर ने परिवर्ती आकार वाला अनुक्रम प्रस्तुत किया। यदि ऐसा है, तो तालिका पंक्तियों का एक अनुक्रम है, और हर पंक्ति स्वयं एक अनुक्रम है: एक वेक्टर जिसके अवयव वेक्टर हैं, std::vector<std::vector<int>>।

C++ प्रोग्राम
#include <print>#include <vector>​int main(){    std::vector<std::vector<int>> table{{1, 2, 3}, {4, 5, 6}};    std::println("{}", table);    std::println("पंक्तियाँ {}, स्तंभ {}", table.size(), table[0].size());    std::println("पंक्ति 1: {}; पंक्ति 1, स्तंभ 2: {}", table[1], table[1][2]);    table[0][1] = 20;    for (const std::vector<int>& row : table) {        for (const int value : row) {            std::print("{:>4}", value);        }        std::println("");    }    const std::vector<std::vector<int>> zeros(3, std::vector<int>(4));    std::println("{}", zeros);}
आउटपुट
[[1, 2, 3], [4, 5, 6]]पंक्तियाँ 2, स्तंभ 3पंक्ति 1: [4, 5, 6]; पंक्ति 1, स्तंभ 2: 6   1  20   3   4   5   6[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

कोड की पंक्ति 6 तालिका को पंक्तियों की सूची के रूप में लिखती है, हर पंक्ति अपने धनुकोष्ठकों में, और रेंज का फ़ॉर्मैट उसे उसी तरह छापता है, वर्ग कोष्ठकों के साथ। table.size() पंक्तियों की संख्या है, और table[0].size() पहली पंक्ति की लंबाई, जो सही ढंग से बनी तालिका में स्तंभों की संख्या है। table[1] पूरी पंक्ति 1 है, एक std::vector<int>, और table[1][2] उस पंक्ति का अवयव 2: दोहरा इंडेक्स कोई नई संक्रिया नहीं, बल्कि लगातार दो पहुँच हैं, पहली पंक्ति चुनती है और दूसरी, उसके भीतर, स्तंभ। कोड की पंक्तियाँ 11 से 16 तालिका को दो नेस्टेड रेंज-आधारित for से चौखाने के रूप में लिखती हैं; पंक्ति को कॉन्स्ट रेफ़रेंस द्वारा लिया जाता है ताकि हर चक्कर में उसकी प्रतिलिपि न बने। कोड की पंक्ति 17 शून्यों से भरी 3 गुणा 4 की एक तालिका बनाती है: गोल कोष्ठकों से, जैसे पाठ वेक्टर में, चार शून्यों वाली एक पंक्ति की 3 प्रतियाँ माँगी जाती हैं।

उदाहरणपंक्तियों और स्तंभों के अनुसार ट्रैवर्सल

3 गुणा 4 की एक तालिका, जिसे पहले पंक्ति-दर-पंक्ति (i बाहरी लूप में) और फिर स्तंभ-दर-स्तंभ (लूपों की अदला-बदली करके) ट्रैवर्स किया जाता है। हर खाना अपनी विज़िट का क्रमांक दिखाता है।

प्रमाण

  1. तालिका, पंक्तियों के इंडेक्स बाईं ओर और स्तंभों के इंडेक्स ऊपर।

  2. पंक्तियों के अनुसार: i=0i = 0 होने पर भीतरी लूप j=0,1,2,3j = 0, 1, 2, 3 से गुज़रता है।

  3. i=1i = 1 होने पर भीतरी लूप फिर से j=0j = 0 से शुरू होता है।

  4. i=2i = 2 होने पर: तालिका वैसे ही ट्रैवर्स हुई जैसे कोई लेख पढ़ा जाता है, एक के बाद एक लाइन।

  5. स्तंभों के अनुसार: j=0j = 0 होने पर भीतरी लूप i=0,1,2i = 0, 1, 2 से गुज़रता है।

  6. j=1j = 1 होने पर, दूसरा स्तंभ।

  7. j=2j = 2 होने पर।

  8. j=3j = 3 होने पर। हर खाने पर फिर से ठीक एक बार जाया गया: लूपों की अदला-बदली यह नहीं बदलती कि किस पर जाया जाता है, बल्कि यह कि कब।

अलग-अलग लंबाई की पंक्तियाँ

वेक्टरों के वेक्टर की पंक्तियों को एक ही लंबाई का होने के लिए कुछ भी बाध्य नहीं करता: हर पंक्ति एक स्वतंत्र वेक्टर है। कभी-कभी यही चाहिए होता है, जैसे पास्कल के त्रिभुज में; पर तालिका में, जो परिभाषा से आयताकार है, छोटी पंक्ति डेटा की त्रुटि है, और जो प्रोग्राम table[0].size() को स्तंभों की संख्या मानता है, वह उस पंक्ति के बाहर पढ़ता है। इसलिए बाहर से आई तालिका की जाँच ज़रूरी है।

C++ प्रोग्राम
#include <print>#include <vector>​// पूर्व-शर्त: कोई नहीं।// पश्च-शर्त: true लौटाता है यदि table में कम से कम एक पंक्ति है और उसकी सभी// पंक्तियों की लंबाई पहली पंक्ति जितनी है।bool isRectangular(const std::vector<std::vector<int>>& table){    if (table.empty()) {        return false;    }    for (const std::vector<int>& row : table) {        if (row.size() != table[0].size()) {            return false;        }    }    return true;}​int main(){    const std::vector<std::vector<int>> pascal{{1}, {1, 1}, {1, 2, 1}, {1, 3, 3, 1}};    const std::vector<std::vector<int>> grid{{1, 2, 3}, {4, 5, 6}};    std::println("{} {}", pascal, isRectangular(pascal));    std::println("{} {}", grid, isRectangular(grid));}
आउटपुट
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]] false[[1, 2, 3], [4, 5, 6]] true

फ़ंक्शन उस तालिका को बाहर रखता है जिसमें कोई पंक्ति नहीं, और जिसमें table[0] मौजूद नहीं। इस पाठ के जो फ़ंक्शन table[0].size() पढ़ते हैं, वे अपनी पूर्व-शर्त में एक आयताकार और ख़ाली न होने वाली तालिका माँगते हैं, और isRectangular इसे सुनिश्चित करने देता है।

ऐरों का ऐरे

जब विमाएँ कंपाइल करते समय ज्ञात हों (शतरंज की बिसात, जादुई वर्ग), तो तालिका std::array का std::array हो सकती है: std::array<std::array<int, 3>, 3> को भीतर से बाहर की ओर पढ़ा जाता है, “3 पूर्णांकों की 3 पंक्तियाँ”। उदाहरण के लिए कोटि 3 का जादुई वर्ग लेते हैं, जिसकी हर पंक्ति, हर स्तंभ और दोनों विकर्णों का योग 15 है।

C++ प्रोग्राम
#include <array>#include <print>​int main(){    constexpr std::array<std::array<int, 3>, 3> magic{{        {2, 7, 6},        {9, 5, 1},        {4, 3, 8},    }};    std::println("{}", magic);    std::println("केंद्र {}, {} बाइट", magic[1][1], sizeof(magic));}
आउटपुट
[[2, 7, 6], [9, 5, 1], [4, 3, 8]]केंद्र 5, 36 बाइट

धनुकोष्ठक हर सिरे पर तीन हैं। std::array का एक ही सदस्य है, एक C ऐरे, इसलिए सबसे बाहरी धनुकोष्ठक std::array का है, दूसरा उस भीतरी ऐरे का, और केवल तीसरा हर पंक्ति का। दो धनुकोष्ठकों के साथ, पहली पंक्ति को भीतरी ऐरे का इनिशियलाइज़र मान लिया जाता है, और दूसरी पंक्ति के लिए इनिशियलाइज़ करने को कुछ नहीं बचता।

C++ प्रोग्राम
#include <array>#include <print>​int main(){    const std::array<std::array<int, 3>, 2> table{{1, 2, 3}, {4, 5, 6}};    std::println("{}", table);}
कंपाइलर के त्रुटि संदेश
main.cpp: In function ‘int main()’:main.cpp:6:71: error: too many initializers for ‘const std::array<std::array<int, 3>, 2>’    6 |     const std::array<std::array<int, 3>, 2> table{{1, 2, 3}, {4, 5, 6}};      |                                                                       ^

Too many initializers: सूची {4, 5, 6} फ़ालतू है। फ़्लैट सूची, {1, 2, 3, 4, 5, 6}, भी कंपाइल होती है और पंक्तियों को क्रम से भरती है, पर तालिका की आकृति छिपा देती है; यह पाठ्यक्रम तीनों धनुकोष्ठक लिखता है।

sizeof के 36 बाइट 4 बाइट वाले नौ int के हैं (सामान्य आकार, जिसकी मानक गारंटी नहीं देता), और कुछ नहीं: ऐरों का ऐरे पंक्तियों को एक के बाद एक एक ही ब्लॉक में रखता है। इसके विपरीत, वेक्टरों का वेक्टर हर पंक्ति के लिए एक ब्लॉक माँगता है, पंक्तियों वाले ब्लॉक के अलावा, और इन ब्लॉकों का सटा हुआ होना ज़रूरी नहीं। इसलिए चुनाव पाठ ऐरे और पाठ वेक्टर के नियम का पालन करता है।

  • यदि दोनों विमाएँ कंपाइल करते समय ज्ञात हैं, तो ऐरों का ऐरे: एक ही ब्लॉक, प्रकार द्वारा गारंटीकृत आयताकार आकृति, और constexpr की संभावना।
  • यदि कोई विमा केवल चलाते समय ज्ञात है, तो वेक्टरों का वेक्टर, इस दायित्व के साथ कि जाँचा जाए कि पंक्तियों की लंबाई समान है, या अंतिम भाग का फ़्लैट वेक्टर।