템플릿 회문
벡터의 각 요소에 대해 회문 검사를 구현하고 회문 만있는 벡터를 반환하려고합니다. 이것은 다른 유형의 .eg strings int vector of int. 템플릿 솔루션을 수행했지만 템플릿을 최대한 활용하지 못하는 것 같습니다.
//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;
}
답변
첫째 : 이것은 "OO Antipattern" 의 응용 프로그램처럼 보입니다 . 왜 당신이 필요한지 모르겠습니다 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)
손으로.
손 흔들기가 약간 적고 신비한 구문이있는이 코드의 버전은 https://godbolt.org/z/aqPfGx — 일부 신비한 구문이 위협적 일지라도 (솔직히 불필요하게도-C ++로 벡터 벡터를 인쇄하려면 "여기서 시작하지 않을 것입니다") 살펴 보는 것이 흥미로울 수 있습니다.
I. 구성은 여러 단계로 나뉩니다. 즉, 새로 생성 된 객체가 완성되고 사용 준비가 완료된 상태가 아니며 하나 이상의 명시적인 초기화 호출이 필요합니다. 이것은 명백히 오류로 이어질 수있는 매우 강력한 반 패턴입니다.
더 좋은 해결책은 생성자 내부에서 데이터 세트를 필터링하고 한 번에 두 개의 벡터를 유지하지 않아 필요한 공간을 두 배로 늘리는 것입니다 (특히 벡터 중 하나가 다른 하나를 생성하는 인수로만 필요하고 사용할 수없는 경우). 그렇지 않으면 액세스 할 수 없음). (이 설계의 한 가지 단점은 구조가 매우 엄격하다는 것이 분명하지만, 구성된 회문 필터를 실제로 사용할 수 없다는 가능성을 무시하는 한 괜찮습니다.)
II. 에 대한 코드 Print는 정수와 문자열간에 절대적으로 동일하며 벡터의 경우 요소 인쇄 부분 만 다릅니다.
III. 회문 필터 자체는에 정의 된 것과 유사한 범위에서 작동하는 함수로 간단히 구현 된 경우 더 유용 할 수 있습니다 <algorithm>. 또는 적어도 관용적 iterator/ begin/ end인터페이스를 구현 한 경우 . std::remove_if와 IsPalindrome술어가 좋은 시작이 될 것이라고한다.
IV. 그리고 생성자 자체 (작성된대로)를 템플릿 화하여 임의의 인수 팩을 받아 datasetctor 에게 전달할 수 있습니다.