Şablonlu palindrom

Sep 07 2020

Bir vektördeki her öğe için bir palindrom kontrolü uygulamaya ve yalnızca palindromlarla bir vektör döndürmeye çalışıyorum. Bu, farklı türde .eg dizeleri int vektörü için çalışmalıdır. Şablonlu bir çözüm yaptım ama bunun şablonlardan tam olarak yararlanmadığını hissediyorum.

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

Yanıtlar

7 Quuxplusone Sep 07 2020 at 05:10

Birincisi: Bu, "The OO Antipattern" uygulamasına benziyor . Neden ihtiyacın olduğunu anlamıyorum class Palindrome; ve eğer saklamanız gerekiyorsa, veri setinin tamamını kesinlikle saklamamalısınız - sadece yapıcıda bir kez işleyin ve palindromları saklayın!

Benzer şekilde, Palindrome<T>::Print()" ne olursa olsun bu şeyi basmak" için genelleştirilmesi gerekiyor gibi görünüyor ; bu işlemin palindromlarla ilgisi yoktur ve kendi fayda fonksiyonuna bölünebilir.

Yani bununla kaldık:

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

Sonra test senaryolarınızı şu şekilde yeniden yazabiliriz:

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

Bu arada, test senaryoları yazmış olman çok güzel! Çok az insan bunu yapar. Ve izin - onlar sınıfını kullanılacak niyetinde göstermek çünkü test durumları yararlıdır beni kullanılacak benim yeniden yazma niyetinde göstermek için!

Bu kadar gibi herhangi köşe durumlarını test olmadığını bildirimi yapmak 1, 0, -1, "", {42}, veya {}. Bu o kadar da harika değil.


Sizin IsPalindromeherhangi bir şey için iterable tam olarak aynı olacak. Öyleyse şöyle bir şey yazmayı tercih et

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

Bu şablonun yalnızca ifade iyi biçimlendirildiğinde örnekleme için değerlendirilmesi gerektiğini söylemek için SFINAE dönüş türünü kullanıyorum std::begin(seq), std::rbegin(seq), true. C ++ 20'de, amacı şu şekilde daha iyi aktarabilirsiniz:

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

Her iki durumda da, yine de diğer aşırı yüklemenizi yazmanız gerekir.

bool is_palindromic(int x)

elle.

Bu kodun biraz daha az el dalgası ve daha fazla gizemli sözdizimi içeren bir sürümü şu adrestedir: https://godbolt.org/z/aqPfGx - Gizemli sözdizimi korkutucu olsa bile (ve dürüst olmak gerekirse, gereksiz - eğer C ++ 'da vektörlerin bir vektörünü yazdıracak olsaydım, "buradan başlamazdım") bir göz atmak ilginç olabilir.

2 bipll Sep 07 2020 at 02:00

I. İnşaat birkaç aşamaya ayrılmıştır. Ya da başka bir deyişle, yeni oluşturulmuş bir nesne sonlandırılmış, kullanıma hazır durumda değildir ve bir açık başlatma çağrısına daha ihtiyaç duyar. Bu, açıkça hatalara yol açabilecek çok güçlü bir anti-modeldir.
Daha iyi bir çözüm, veri kümesini kurucunun içinde filtrelemek ve aynı anda iki vektörü tutmamak, böylece gereken alanı ikiye katlamak olacaktır (özellikle vektörlerden biri yalnızca diğerini oluşturmak için bir argüman olarak gerekliyse ve mümkün değilse ve aksi takdirde erişilemez). (Bu tasarımın bir dezavantajı, inşaatın çok katı olacağı açıktır, ancak inşa edilen palindrom filtresinin asla gerçekten kullanılamayacağı olasılığını ihmal ettiğimiz sürece bu sorun değildir.)

II. İçin kod Print, girişler ve dizeler arasında kesinlikle aynıdır ve vektörler için yalnızca bir öğe yazdırma bölümünde farklılık gösterir.

III. Palindrom filtresinin kendisi, içinde tanımlananlara benzer şekilde, basitçe aralıklar üzerinde çalışan bir işlev olarak uygulanmış olsaydı, belki daha yararlı olabilirdi <algorithm>; ya da en azından deyimsel iterator/ begin/ endarayüzü uyguladıysa . std::remove_ifile IsPalindromeyüklem iyi bir başlangıç olurdu.

IV. Ve kurucunun kendisi (yazıldığı gibi), keyfi bir argüman paketini kabul edip datasetctor'a ileterek şablon haline getirilebilir .