अस्थायी तालु

Sep 07 2020

मैं वेक्टर में प्रत्येक तत्व के लिए एक palindrome की जाँच को लागू करने और केवल palindromes के साथ एक वेक्टर वापस करने की कोशिश कर रहा हूँ। यह विभिन्न प्रकार के लिए काम करना चाहिए। इन स्ट्रिंग्स इंट वेक्टर के ints.I मैं एक अस्थायी समाधान किया है, लेकिन मुझे लगता है कि यह टेम्पलेट्स का पूरा फायदा नहीं उठाता है।

//main.cpp
  // Checking palindrome with integers
  Palindrome <int>pal = {125125, 4947, 74347, 11};
  pal.FindPalindromeDataset();
  pal.Print();

  // Checking palindrome with strings
  Palindrome <std::string>pal1  = {"yay", "world", "level", "hello"};
  pal1.FindPalindromeDataset();
  pal1.Print();

  // Checking palindrome with vector of ints
  Palindrome<std::vector<int> > pal3 = {{6, 2, 2, 6},
                                        {1, 2, 2},
                                        {1, 4, 6, 3, 5, 3, 6, 4, 1},
                                        {5, 2, 2, 6, 9, 1, 2}};
    

//palindrome.hpp
#ifndef PALINDROME_HHP
#define PALINDROME_HHP

#include <vector>
#include <iostream>

template <class T>
class Palindrome {
public:
  //! Construct from a std::initializer list 
  Palindrome(std::initializer_list<T> _dataset) : dataset(_dataset)
  {}

  //! Print the palindromeDataset
  void Print() const;
  
  /* Traverse to check if each element of the vector is a palindrome
   * and push them in the new array
   */
  void FindPalindromeDataset()
  {
    for (auto i : dataset)
    {
      if (IsPalindrome(i))
      {
        palindromeDataset.push_back(i);
      }
    }
  }
  
private:
  //! Is the element of the vector palindrome
  bool IsPalindrome(const T& s) const;

  //! Initial dataset
  std::vector<T> dataset;
  
  //! Dataset after palindrome check
  std::vector<T> palindromeDataset;
};

#endif
#include "palindrome.hpp"
#include "iostream"
#include "string"

template <>
void Palindrome<int>::Print() const
{
  std::cout << "{";
  for (auto iter = palindromeDataset.begin(); iter != palindromeDataset.end();)
  {
    std::cout << *iter;
    if (++iter != palindromeDataset.end())
    {
      std::cout << ", ";
    }
  }
  std::cout << "}"<<std::endl;
}

template <>
void Palindrome<std::string>::Print() const
{
  std::cout << "{";
  for (auto iter = palindromeDataset.begin(); iter != palindromeDataset.end();)
  {
    std::cout << *iter;
    if (++iter != palindromeDataset.end())
    {
      std::cout << ", ";
    }
  }
  std::cout << "}"<<std::endl;
}

template <>
void Palindrome<std::vector<int>>::Print() const
{
  std::cout << "{";
  for (auto iter1 = palindromeDataset.begin(); iter1 != palindromeDataset.end();)
  {
    std::cout << "{";
    for (auto iter2 = iter1->begin(); iter2 != iter1->end();)
    {
      std::cout << *iter2;
      if (++iter2 != iter1->end())
      {
        std::cout << ", ";
      }
    }
    std::cout << "}";
    if (++iter1 != palindromeDataset.end())
    {
      std::cout << ", ";
    }
  }
  std::cout << "}"<<std::endl;
}

template <>
bool Palindrome<int>::IsPalindrome(const int& s) const
{
  int x = s;
  long int rev = 0;
  if (x<0)
  {
    return false;
  }
  
  while (x!=0)
  {
    rev= rev*10+(x%10);
    x=x/10;
  }
  return s == rev;
}

template <>
bool Palindrome<std::string>::IsPalindrome(const std::string& s) const
{
  const size_t len = s.size();
  if (!len)
  {
    return true;
  }
  
  size_t l = 0;
  size_t r = len - 1;
  while (l < r)
  {
    if (s[l] != s[r])
    {
      return false;
    }
    ++l;
    --r;
  }
  return true;
}

template <>
bool Palindrome<std::vector<int>>::IsPalindrome(const std::vector<int>& s) const
{
  const size_t len = s.size();
  if (!len)
  {
    return true;
  }
  
  size_t l = 0;
  size_t r = len - 1;
  while (l < r)
  {
    if(s[l] != s[r])
    {
      return false;
    }
    ++l;
    --r;
  }
  return true;
}

जवाब

7 Quuxplusone Sep 07 2020 at 05:10

पहला: यह "द ओओ एंटीपैटर्न" के एक आवेदन जैसा लगता है । मैं यह नहीं देखता कि आपको इसकी आवश्यकता क्यों है class Palindrome; और अगर आपको इसे रखना चाहिए, तो आपको निश्चित रूप से पूरे डेटासेट को स्टोर नहीं करना चाहिए - बस इसे एक बार कंस्ट्रक्टर में प्रोसेस करें और पालिंड्रोम्स रखें!

इसी तरह, Palindrome<T>::Print()ऐसा लगता है कि इसे "इस चीज़ को प्रिंट करना , जो कुछ भी हो " सामान्यीकृत किया जाना चाहिए ; उस ऑपरेशन का पलिंडोम्स से कोई लेना-देना नहीं है और इसे अपनी उपयोगिता समारोह में विभाजित किया जा सकता है।

तो हम इसके साथ बचे हैं:

template<class T>
std::vector<T> keep_only_palindromes(std::vector<T> dataset) {
    std::erase_if(dataset, [](auto&& elt) {
        return !is_palindromic(elt);
    });
    return dataset;
}

template<class T>
class PrintableVector {
    const std::vector<T> *v_;
public:
    explicit PrintableVector(const std::vector<T>& v) : v_(&v) {}
    friend std::ostream& operator<<(std::ostream& os, const PrintableVector& me) {
        os << "{ ";
        for (auto&& elt : *me.v_) os << elt << ", ";
        os << "}";
        return os;
    }
};

तब हम आपके परीक्षण मामलों को फिर से लिख सकते हैं:

int main() {
    auto pal = keep_only_palindromes(
        std::vector<int>{125125, 4947, 74347, 11}
    );
    std::cout << PrintableVector(pal) << "\n";

    auto pal1 = keep_only_palindromes(
       std::vector<std::string>{"yay", "world", "level", "hello"}
    );
    std::cout << PrintableVector(pal1) << "\n";

    std::vector<std::vector<int>> pal2_data = {
        {6, 2, 2, 6},
        {1, 2, 2},
        {1, 4, 6, 3, 5, 3, 6, 4, 1},
        {5, 2, 2, 6, 9, 1, 2}
    };
    auto pal2 = keep_only_palindromes(pal2_data);
    std::cout << PrintableVector(pal2) << "\n";
}

वैसे, यह बहुत अच्छा है कि आपने परीक्षण मामलों को लिखा है! बहुत कम लोग करते हैं। आपके परीक्षण के मामले उपयोगी हैं क्योंकि वे दिखाते हैं कि आप किस तरह से कक्षा का उपयोग करने का इरादा रखते हैं - और मुझे यह दिखाने की अनुमति दें कि मैं अपने पुन: उपयोग के लिए कैसे लिखना चाहता हूं!

मैं सूचना है कि आप इस तरह के रूप में किसी भी कोने मामलों का परीक्षण नहीं करते हैं, करते हैं 1, 0, -1, "", {42}, या {}। यह इतना महान नहीं है।


आपका IsPalindromeकुछ भी चलने के लिए बिल्कुल वैसा ही होने जा रहा है। इसलिए कुछ लिखना पसंद करते हैं

template<class T>
auto is_palindromic(const T& seq)
    -> decltype(std::begin(seq), std::rbegin(seq), true)
{
    return std::equal(
        std::begin(seq), std::end(seq),
        std::rbegin(seq), std::rend(seq)
    );
}

वहां मैं रिटर्न टाइप SFINAE का उपयोग कर रहा हूं , यह कहने के लिए कि इस टेम्पलेट को केवल तभी इंस्टेंटेशन के लिए माना जाना चाहिए, जब अभिव्यक्ति std::begin(seq), std::rbegin(seq), trueअच्छी तरह से बनाई गई हो। C ++ 20 में आप कुछ इस तरह से इरादे को बेहतर बता सकते हैं:

template<class T>
concept sequence = requires (const T& seq) {
    seq.begin(); seq.rbegin();
};

template<class T> requires sequence<T>  // !!
bool is_palindromic(const T& seq) {
    return std::equal(
        std::begin(seq), std::end(seq),
        std::rbegin(seq), std::rend(seq)
    );
}

या तो मामले में, आपको अभी भी अपने अन्य अधिभार को लिखना होगा

bool is_palindromic(int x)

हाथ से।

इस कोड का एक संस्करण थोड़ा कम handwaving और अधिक रहस्यमय वाक्यविन्यास के साथ है https://godbolt.org/z/aqPfGx - एक नज़र रखना दिलचस्प हो सकता है, भले ही कुछ रहस्यमय वाक्य रचना डराने वाली हो (और, ईमानदारी से, अनावश्यक - अगर मैं C ++ में वेक्टर के वेक्टर को प्रिंट करने जा रहा था, "मैं यहां से शुरू नहीं करूंगा")।

2 bipll Sep 07 2020 at 02:00

I. निर्माण कई चरणों में विभाजित है। या, दूसरे शब्दों में, एक ताजा निर्मित वस्तु अंतिम रूप से तैयार, उपयोग में नहीं है और एक और स्पष्ट आरंभीकरण कॉल की आवश्यकता है। यह एक बहुत मजबूत एंटीपैटर है, जो स्पष्ट रूप से त्रुटियों को जन्म दे सकता है।
एक अधिक सभ्य समाधान यह होगा कि कंस्ट्रक्टर के अंदर डेटासेट को फ़िल्टर किया जाए, और दो वैक्टर को एक साथ न रखा जाए, इस प्रकार अंतरिक्ष की आवश्यकता को दोगुना करना होगा (विशेषकर तब जब वैक्टर में से एक को दूसरे को बनाने के लिए एक तर्क के रूप में आवश्यक हो, और अस्थिर हो और दुर्गम अन्यथा)। (इस डिज़ाइन में एक कमी स्पष्ट रूप से यह है कि निर्माण बहुत सख्त होगा, लेकिन यह ठीक है जब तक हम इस संभावना की उपेक्षा करते हैं कि निर्मित पैलिंड्रोम फ़िल्टर का उपयोग वास्तव में कभी नहीं किया जा सकता है।)

II। के लिए कोड Printहै बिल्कुल ints और तार के बीच समान है, और वैक्टर के लिए, केवल मुद्रण-एक तत्व भाग में अलग है।

III। पैलिंड्रोम फ़िल्टर स्वयं अधिक उपयोगी हो सकता है शायद, अगर इसे केवल एक फ़ंक्शन के रूप में लागू किया जाता है, जो कि परिभाषित सीमाओं के समान है <algorithm>; या कम से कम अगर यह मुहावरेदार iterator/ begin/ endइंटरफ़ेस लागू किया । std::remove_ifके IsPalindromeरूप में विधेय के साथ एक अच्छी शुरुआत होगी।

IV। और कंस्ट्रक्टर खुद (जैसा कि यह लिखा गया है) को datasetविस्थापित किया जा सकता है, एक मनमाना तर्क पैक स्वीकार कर इसे ctor को अग्रेषित किया जा सकता है ।