Palindrom templated

Sep 07 2020

Saya mencoba menerapkan pemeriksaan palindrome untuk setiap elemen dalam vektor dan mengembalikan vektor hanya dengan palindrom. Ini harus bekerja untuk jenis yang berbeda .eg string int vektor int. Saya telah melakukan solusi template tetapi saya merasa ini tidak memanfaatkan sepenuhnya template.

//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;
}

Jawaban

7 Quuxplusone Sep 07 2020 at 05:10

Pertama: Sepertinya ini adalah aplikasi "The OO Antipattern" . Saya tidak mengerti mengapa Anda membutuhkannya class Palindromesama sekali; dan jika Anda harus menyimpannya, Anda tentunya tidak boleh menyimpan seluruh kumpulan data - cukup proses sekali di konstruktor dan simpan palindrom!

Demikian pula, Palindrome<T>::Print()sepertinya itu harus digeneralisasikan untuk "mencetak hal ini , apapun itu"; operasi itu tidak ada hubungannya dengan palindrome dan dapat dibagi menjadi fungsi utilitasnya sendiri.

Jadi kita pergi dengan ini:

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;
    }
};

Kemudian kami dapat menulis ulang kasus pengujian Anda sebagai:

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";
}

Ngomong-ngomong, sangat bagus Anda menulis kasus uji! Sangat sedikit orang yang melakukannya. Test case Anda berguna karena menunjukkan bagaimana Anda bermaksud menggunakan kelas - dan izinkan saya untuk menunjukkan bagaimana saya bermaksud menggunakan rewrite saya!

Saya melakukan pemberitahuan bahwa Anda tidak menguji setiap sudut kasus, seperti 1, 0, -1, "", {42}, atau {}. Ini tidak terlalu bagus.


Anda IsPalindromeuntuk apa pun yang dapat diulang akan menjadi persis sama. Jadi lebih suka menulis sesuatu seperti

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)
    );
}

Di sana saya menggunakan SFINAE tipe kembali untuk mengatakan bahwa template ini harus dipertimbangkan untuk instantiation hanya ketika ekspresi std::begin(seq), std::rbegin(seq), trueterbentuk dengan baik. Di C ++ 20 Anda bisa menyampaikan maksud lebih baik dengan sesuatu seperti ini:

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)
    );
}

Dalam kedua kasus tersebut, Anda masih harus menulis overload Anda yang lain

bool is_palindromic(int x)

dengan tangan.

Versi kode ini dengan sedikit lebih sedikit handwaving dan sintaks yang lebih misterius ada di https://godbolt.org/z/aqPfGx - mungkin menarik untuk dilihat, bahkan jika beberapa sintaks misterius mengintimidasi (dan, sejujurnya, tidak perlu - jika saya akan mencetak vektor vektor dalam C ++, "Saya tidak akan mulai dari sini").

2 bipll Sep 07 2020 at 02:00

I. Konstruksi dibagi dalam beberapa tahap. Atau, dengan kata lain, objek yang baru dibangun tidak dalam status final, siap digunakan dan membutuhkan satu lagi panggilan inisialisasi eksplisit. Ini adalah antipattern yang sangat kuat, yang jelas dapat menyebabkan kesalahan.
Solusi yang lebih baik adalah dengan memfilter dataset tepat di dalam konstruktor, dan tidak menyimpan dua vektor sekaligus, sehingga menggandakan ruang yang dibutuhkan (terutama ketika salah satu vektor hanya diperlukan sebagai argumen untuk membuat yang lain, dan tidak dapat digunakan dan tidak dapat diakses jika tidak). (Satu kelemahan dari desain ini jelas bahwa konstruksinya akan sangat ketat, tetapi hal ini tidak masalah selama kita mengabaikan kemungkinan bahwa filter palindrome yang dibuat tidak akan pernah benar-benar digunakan.)

II. Kode untuk benarPrint - benar identik antara int dan string, dan untuk vektor, hanya berbeda di bagian pencetakan-elemen.

AKU AKU AKU. Filter palindrome itu sendiri mungkin bisa lebih berguna, jika diterapkan hanya sebagai fungsi yang beroperasi pada rentang, mirip dengan yang ditentukan di <algorithm>; atau setidaknya jika diimplementasikan idiomatic iterator/ begin/ endinterface. std::remove_ifdengan IsPalindromepredikat akan menjadi awal yang baik.

IV. Dan konstruktor itu sendiri (seperti yang tertulis) dapat diberi template, menerima paket argumen sewenang-wenang dan meneruskannya ke datasetctor.